From 9ce05555af2dcac1e76f1ec762a0939dbd1d3d69 Mon Sep 17 00:00:00 2001 From: Apple Date: Fri, 24 Oct 2003 23:30:25 +0000 Subject: [PATCH] CF-299.tar.gz --- APPLE_LICENSE | 367 ++ AppServices.subproj/CFUserNotification.c | 498 ++ AppServices.subproj/CFUserNotification.h | 202 + Base.subproj/CFBase.c | 876 +++ Base.subproj/CFBase.h | 341 ++ Base.subproj/CFByteOrder.h | 298 + Base.subproj/CFFileUtilities.c | 767 +++ Base.subproj/CFInternal.h | 461 ++ Base.subproj/CFPlatform.c | 461 ++ Base.subproj/CFPriv.h | 282 + Base.subproj/CFRuntime.c | 953 ++++ Base.subproj/CFRuntime.h | 320 ++ Base.subproj/CFSortFunctions.c | 833 +++ Base.subproj/CFUUID.c | 383 ++ Base.subproj/CFUUID.h | 99 + Base.subproj/CFUtilities.c | 533 ++ Base.subproj/CFUtilities.h | 76 + Base.subproj/CoreFoundation.h | 95 + Base.subproj/ForFoundationOnly.h | 339 ++ Base.subproj/uuid.c | 1297 +++++ CharacterSets/CFCharacterSetBitmaps.bitmap | Bin 0 -> 352454 bytes CharacterSets/CFUniCharPropertyDatabase.data | Bin 0 -> 17688 bytes CharacterSets/CFUnicodeData-B.mapping | Bin 0 -> 81316 bytes CharacterSets/CFUnicodeData-L.mapping | Bin 0 -> 81316 bytes Collections.subproj/CFArray.c | 1061 ++++ Collections.subproj/CFArray.h | 712 +++ Collections.subproj/CFBag.c | 764 +++ Collections.subproj/CFBag.h | 118 + Collections.subproj/CFBinaryHeap.c | 444 ++ Collections.subproj/CFBinaryHeap.h | 316 ++ Collections.subproj/CFBitVector.c | 548 ++ Collections.subproj/CFBitVector.h | 70 + Collections.subproj/CFData.c | 396 ++ Collections.subproj/CFData.h | 92 + Collections.subproj/CFDictionary.c | 1153 ++++ Collections.subproj/CFDictionary.h | 704 +++ Collections.subproj/CFSet.c | 817 +++ Collections.subproj/CFSet.h | 513 ++ Collections.subproj/CFStorage.c | 597 ++ Collections.subproj/CFStorage.h | 236 + Collections.subproj/CFTree.c | 440 ++ Collections.subproj/CFTree.h | 352 ++ Locale.subproj/CFLocale.h | 49 + Makefile | 436 ++ NumberDate.subproj/CFDate.c | 518 ++ NumberDate.subproj/CFDate.h | 131 + NumberDate.subproj/CFNumber.c | 575 ++ NumberDate.subproj/CFNumber.h | 148 + NumberDate.subproj/CFTimeZone.c | 1472 +++++ NumberDate.subproj/CFTimeZone.h | 96 + Parsing.subproj/CFBinaryPList.c | 899 ++++ Parsing.subproj/CFPropertyList.c | 2520 +++++++++ Parsing.subproj/CFPropertyList.h | 103 + Parsing.subproj/CFXMLInputStream.c | 726 +++ Parsing.subproj/CFXMLInputStream.h | 110 + Parsing.subproj/CFXMLNode.c | 370 ++ Parsing.subproj/CFXMLNode.h | 211 + Parsing.subproj/CFXMLParser.c | 2025 +++++++ Parsing.subproj/CFXMLParser.h | 288 + Parsing.subproj/CFXMLTree.c | 258 + PlugIn.subproj/CFBundle.c | 2969 ++++++++++ PlugIn.subproj/CFBundle.h | 321 ++ PlugIn.subproj/CFBundlePriv.h | 254 + PlugIn.subproj/CFBundle_BinaryTypes.h | 64 + PlugIn.subproj/CFBundle_Internal.h | 232 + PlugIn.subproj/CFBundle_Resources.c | 1977 +++++++ PlugIn.subproj/CFPlugIn.c | 177 + PlugIn.subproj/CFPlugIn.h | 164 + PlugIn.subproj/CFPlugInCOM.h | 124 + PlugIn.subproj/CFPlugIn_Factory.c | 289 + PlugIn.subproj/CFPlugIn_Factory.h | 83 + PlugIn.subproj/CFPlugIn_Instance.c | 125 + PlugIn.subproj/CFPlugIn_PlugIn.c | 240 + PropertyList.dtd | 20 + RunLoop.subproj/CFMachPort.c | 666 +++ RunLoop.subproj/CFMachPort.h | 75 + RunLoop.subproj/CFMessagePort.c | 836 +++ RunLoop.subproj/CFMessagePort.h | 86 + RunLoop.subproj/CFRunLoop.c | 2593 +++++++++ RunLoop.subproj/CFRunLoop.h | 433 ++ RunLoop.subproj/CFSocket.c | 1615 ++++++ RunLoop.subproj/CFSocket.h | 245 + RunLoop.subproj/CFWindowsMessageQueue.c | 230 + RunLoop.subproj/CFWindowsMessageQueue.h | 61 + String.subproj/CFCharacterSet.c | 2681 +++++++++ String.subproj/CFCharacterSet.h | 416 ++ String.subproj/CFCharacterSetPriv.h | 97 + String.subproj/CFString.c | 4783 +++++++++++++++++ String.subproj/CFString.h | 716 +++ String.subproj/CFStringEncodings.c | 685 +++ String.subproj/CFStringScanner.c | 274 + String.subproj/CFStringUtilities.c | 341 ++ StringEncodings.subproj/CFBuiltinConverters.c | 1197 +++++ .../CFStringEncodingConverter.c | 980 ++++ .../CFStringEncodingConverter.h | 113 + .../CFStringEncodingConverterExt.h | 136 + .../CFStringEncodingConverterPriv.h | 77 + StringEncodings.subproj/CFUniChar.c | 1161 ++++ StringEncodings.subproj/CFUniChar.h | 195 + StringEncodings.subproj/CFUniCharPriv.h | 48 + .../CFUnicodeDecomposition.c | 581 ++ .../CFUnicodeDecomposition.h | 73 + .../CFUnicodePrecomposition.c | 366 ++ .../CFUnicodePrecomposition.h | 57 + URL.subproj/CFURL.c | 4442 +++++++++++++++ URL.subproj/CFURL.h | 416 ++ URL.subproj/CFURLAccess.c | 483 ++ URL.subproj/CFURLAccess.h | 131 + version.c | 34 + 109 files changed, 64111 insertions(+) create mode 100644 APPLE_LICENSE create mode 100644 AppServices.subproj/CFUserNotification.c create mode 100644 AppServices.subproj/CFUserNotification.h create mode 100644 Base.subproj/CFBase.c create mode 100644 Base.subproj/CFBase.h create mode 100644 Base.subproj/CFByteOrder.h create mode 100644 Base.subproj/CFFileUtilities.c create mode 100644 Base.subproj/CFInternal.h create mode 100644 Base.subproj/CFPlatform.c create mode 100644 Base.subproj/CFPriv.h create mode 100644 Base.subproj/CFRuntime.c create mode 100644 Base.subproj/CFRuntime.h create mode 100644 Base.subproj/CFSortFunctions.c create mode 100644 Base.subproj/CFUUID.c create mode 100644 Base.subproj/CFUUID.h create mode 100644 Base.subproj/CFUtilities.c create mode 100644 Base.subproj/CFUtilities.h create mode 100644 Base.subproj/CoreFoundation.h create mode 100644 Base.subproj/ForFoundationOnly.h create mode 100644 Base.subproj/uuid.c create mode 100644 CharacterSets/CFCharacterSetBitmaps.bitmap create mode 100644 CharacterSets/CFUniCharPropertyDatabase.data create mode 100644 CharacterSets/CFUnicodeData-B.mapping create mode 100644 CharacterSets/CFUnicodeData-L.mapping create mode 100644 Collections.subproj/CFArray.c create mode 100644 Collections.subproj/CFArray.h create mode 100644 Collections.subproj/CFBag.c create mode 100644 Collections.subproj/CFBag.h create mode 100644 Collections.subproj/CFBinaryHeap.c create mode 100644 Collections.subproj/CFBinaryHeap.h create mode 100644 Collections.subproj/CFBitVector.c create mode 100644 Collections.subproj/CFBitVector.h create mode 100644 Collections.subproj/CFData.c create mode 100644 Collections.subproj/CFData.h create mode 100644 Collections.subproj/CFDictionary.c create mode 100644 Collections.subproj/CFDictionary.h create mode 100644 Collections.subproj/CFSet.c create mode 100644 Collections.subproj/CFSet.h create mode 100644 Collections.subproj/CFStorage.c create mode 100644 Collections.subproj/CFStorage.h create mode 100644 Collections.subproj/CFTree.c create mode 100644 Collections.subproj/CFTree.h create mode 100644 Locale.subproj/CFLocale.h create mode 100644 Makefile create mode 100644 NumberDate.subproj/CFDate.c create mode 100644 NumberDate.subproj/CFDate.h create mode 100644 NumberDate.subproj/CFNumber.c create mode 100644 NumberDate.subproj/CFNumber.h create mode 100644 NumberDate.subproj/CFTimeZone.c create mode 100644 NumberDate.subproj/CFTimeZone.h create mode 100644 Parsing.subproj/CFBinaryPList.c create mode 100644 Parsing.subproj/CFPropertyList.c create mode 100644 Parsing.subproj/CFPropertyList.h create mode 100644 Parsing.subproj/CFXMLInputStream.c create mode 100644 Parsing.subproj/CFXMLInputStream.h create mode 100644 Parsing.subproj/CFXMLNode.c create mode 100644 Parsing.subproj/CFXMLNode.h create mode 100644 Parsing.subproj/CFXMLParser.c create mode 100644 Parsing.subproj/CFXMLParser.h create mode 100644 Parsing.subproj/CFXMLTree.c create mode 100644 PlugIn.subproj/CFBundle.c create mode 100644 PlugIn.subproj/CFBundle.h create mode 100644 PlugIn.subproj/CFBundlePriv.h create mode 100644 PlugIn.subproj/CFBundle_BinaryTypes.h create mode 100644 PlugIn.subproj/CFBundle_Internal.h create mode 100644 PlugIn.subproj/CFBundle_Resources.c create mode 100644 PlugIn.subproj/CFPlugIn.c create mode 100644 PlugIn.subproj/CFPlugIn.h create mode 100644 PlugIn.subproj/CFPlugInCOM.h create mode 100644 PlugIn.subproj/CFPlugIn_Factory.c create mode 100644 PlugIn.subproj/CFPlugIn_Factory.h create mode 100644 PlugIn.subproj/CFPlugIn_Instance.c create mode 100644 PlugIn.subproj/CFPlugIn_PlugIn.c create mode 100644 PropertyList.dtd create mode 100644 RunLoop.subproj/CFMachPort.c create mode 100644 RunLoop.subproj/CFMachPort.h create mode 100644 RunLoop.subproj/CFMessagePort.c create mode 100644 RunLoop.subproj/CFMessagePort.h create mode 100644 RunLoop.subproj/CFRunLoop.c create mode 100644 RunLoop.subproj/CFRunLoop.h create mode 100644 RunLoop.subproj/CFSocket.c create mode 100644 RunLoop.subproj/CFSocket.h create mode 100644 RunLoop.subproj/CFWindowsMessageQueue.c create mode 100644 RunLoop.subproj/CFWindowsMessageQueue.h create mode 100644 String.subproj/CFCharacterSet.c create mode 100644 String.subproj/CFCharacterSet.h create mode 100644 String.subproj/CFCharacterSetPriv.h create mode 100644 String.subproj/CFString.c create mode 100644 String.subproj/CFString.h create mode 100644 String.subproj/CFStringEncodings.c create mode 100644 String.subproj/CFStringScanner.c create mode 100644 String.subproj/CFStringUtilities.c create mode 100644 StringEncodings.subproj/CFBuiltinConverters.c create mode 100644 StringEncodings.subproj/CFStringEncodingConverter.c create mode 100644 StringEncodings.subproj/CFStringEncodingConverter.h create mode 100644 StringEncodings.subproj/CFStringEncodingConverterExt.h create mode 100644 StringEncodings.subproj/CFStringEncodingConverterPriv.h create mode 100644 StringEncodings.subproj/CFUniChar.c create mode 100644 StringEncodings.subproj/CFUniChar.h create mode 100644 StringEncodings.subproj/CFUniCharPriv.h create mode 100644 StringEncodings.subproj/CFUnicodeDecomposition.c create mode 100644 StringEncodings.subproj/CFUnicodeDecomposition.h create mode 100644 StringEncodings.subproj/CFUnicodePrecomposition.c create mode 100644 StringEncodings.subproj/CFUnicodePrecomposition.h create mode 100644 URL.subproj/CFURL.c create mode 100644 URL.subproj/CFURL.h create mode 100644 URL.subproj/CFURLAccess.c create mode 100644 URL.subproj/CFURLAccess.h create mode 100644 version.c diff --git a/APPLE_LICENSE b/APPLE_LICENSE new file mode 100644 index 0000000..fe81a60 --- /dev/null +++ b/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/AppServices.subproj/CFUserNotification.c b/AppServices.subproj/CFUserNotification.c new file mode 100644 index 0000000..fca188f --- /dev/null +++ b/AppServices.subproj/CFUserNotification.c @@ -0,0 +1,498 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFUserNotification.c + Copyright 2000-2002, Apple, Inc. All rights reserved. + Responsibility: Doug Davidson +*/ + +#include +#include +#include +#include +#include +#include "CFInternal.h" + +#define __kCFLogUserNotification 20 +#define CFUserNotificationLog(alertHeader, alertMessage) CFLog(__kCFLogUserNotification, CFSTR("%@: %@"), alertHeader, alertMessage); + +enum { + kCFUserNotificationCancelFlag = (1 << 3), + kCFUserNotificationUpdateFlag = (1 << 4) +}; + +CONST_STRING_DECL(kCFUserNotificationTokenKey, "Token") +CONST_STRING_DECL(kCFUserNotificationTimeoutKey, "Timeout") +CONST_STRING_DECL(kCFUserNotificationFlagsKey, "Flags") +CONST_STRING_DECL(kCFUserNotificationIconPathKey, "IconPath") +CONST_STRING_DECL(kCFUserNotificationSoundPathKey, "SoundPath") +CONST_STRING_DECL(kCFUserNotificationLocalizationPathKey, "LocalizationPath") +CONST_STRING_DECL(kCFUserNotificationAlertSourceKey, "AlertSource") +CONST_STRING_DECL(kCFUserNotificationTextFieldLabelsKey, "TextFieldTitles") +CONST_STRING_DECL(kCFUserNotificationCheckBoxLabelsKey, "CheckBoxTitles") +CONST_STRING_DECL(kCFUserNotificationIconURLKey, "IconURL") +CONST_STRING_DECL(kCFUserNotificationSoundURLKey, "SoundURL") +CONST_STRING_DECL(kCFUserNotificationLocalizationURLKey, "LocalizationURL") +CONST_STRING_DECL(kCFUserNotificationAlertHeaderKey, "AlertHeader") +CONST_STRING_DECL(kCFUserNotificationAlertMessageKey, "AlertMessage") +CONST_STRING_DECL(kCFUserNotificationDefaultButtonTitleKey, "DefaultButtonTitle") +CONST_STRING_DECL(kCFUserNotificationAlternateButtonTitleKey, "AlternateButtonTitle") +CONST_STRING_DECL(kCFUserNotificationOtherButtonTitleKey, "OtherButtonTitle") +CONST_STRING_DECL(kCFUserNotificationProgressIndicatorValueKey, "ProgressIndicatorValue") +CONST_STRING_DECL(kCFUserNotificationSessionIDKey, "SessionID") +CONST_STRING_DECL(kCFUserNotificationPopUpTitlesKey, "PopUpTitles") +CONST_STRING_DECL(kCFUserNotificationTextFieldTitlesKey, "TextFieldTitles") +CONST_STRING_DECL(kCFUserNotificationCheckBoxTitlesKey, "CheckBoxTitles") +CONST_STRING_DECL(kCFUserNotificationTextFieldValuesKey, "TextFieldValues") +CONST_STRING_DECL(kCFUserNotificationPopUpSelectionKey, "PopUpSelection") + +static CFTypeID __kCFUserNotificationTypeID = _kCFRuntimeNotATypeID; + +struct __CFUserNotification { + CFRuntimeBase _base; + SInt32 _replyPort; + SInt32 _token; + CFTimeInterval _timeout; + CFOptionFlags _requestFlags; + CFOptionFlags _responseFlags; + CFStringRef _sessionID; + CFDictionaryRef _responseDictionary; + CFMachPortRef _machPort; + CFUserNotificationCallBack _callout; +}; + +static CFStringRef __CFUserNotificationCopyDescription(CFTypeRef cf) { + CFMutableStringRef result; + result = CFStringCreateMutable(CFGetAllocator(cf), 0); + CFStringAppendFormat(result, NULL, CFSTR(""), (UInt32)cf); + return result; +} + +#if defined(__MACH__) + +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_STRING_LENGTH PATH_MAX +#define MAX_STRING_COUNT 16 +#define MAX_PORT_NAME_LENGTH 63 +#define NOTIFICATION_PORT_NAME "com.apple.UNCUserNotification" +#define NOTIFICATION_PORT_NAME_OLD "UNCUserNotification" +#define NOTIFICATION_PORT_NAME_SUFFIX ".session." +#define MESSAGE_TIMEOUT 100 + +static void __CFUserNotificationDeallocate(CFTypeRef cf) { + CFUserNotificationRef userNotification = (CFUserNotificationRef)cf; + if (userNotification->_machPort) { + CFMachPortInvalidate(userNotification->_machPort); + CFRelease(userNotification->_machPort); + } else if (MACH_PORT_NULL != userNotification->_replyPort) { + mach_port_destroy(mach_task_self(), userNotification->_replyPort); + } + if (userNotification->_sessionID) CFRelease(userNotification->_sessionID); + if (userNotification->_responseDictionary) CFRelease(userNotification->_responseDictionary); +} + +#endif /* __MACH__ */ + +static const CFRuntimeClass __CFUserNotificationClass = { + 0, + "CFUserNotification", + NULL, // init + NULL, // copy + __CFUserNotificationDeallocate, + NULL, // equal + NULL, // hash + NULL, // + __CFUserNotificationCopyDescription +}; + +__private_extern__ void __CFUserNotificationInitialize(void) { + __kCFUserNotificationTypeID = _CFRuntimeRegisterClass(&__CFUserNotificationClass); +} + +CFTypeID CFUserNotificationGetTypeID(void) { + return __kCFUserNotificationTypeID; +} + +#if defined(__MACH__) + +static void _CFUserNotificationAddToDictionary(const void *key, const void *value, void *context) { + if (CFGetTypeID(key) == CFStringGetTypeID()) CFDictionarySetValue((CFMutableDictionaryRef)context, key, value); +} + +static CFDictionaryRef _CFUserNotificationModifiedDictionary(CFAllocatorRef allocator, CFDictionaryRef dictionary, SInt32 token, SInt32 timeout, CFStringRef source) { + CFMutableDictionaryRef md = CFDictionaryCreateMutable(allocator, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFNumberRef tokenNumber = CFNumberCreate(allocator, kCFNumberSInt32Type, &token); + CFNumberRef timeoutNumber = CFNumberCreate(allocator, kCFNumberSInt32Type, &timeout); + CFURLRef url = NULL; + CFStringRef path = NULL; + + if (dictionary) CFDictionaryApplyFunction(dictionary, _CFUserNotificationAddToDictionary, md); + if (source) CFDictionaryAddValue(md, kCFUserNotificationAlertSourceKey, source); + if (tokenNumber) { + CFDictionaryAddValue(md, kCFUserNotificationTokenKey, tokenNumber); + CFRelease(tokenNumber); + } + if (timeoutNumber) { + CFDictionaryAddValue(md, kCFUserNotificationTimeoutKey, timeoutNumber); + CFRelease(timeoutNumber); + } + + url = CFDictionaryGetValue(md, kCFUserNotificationIconURLKey); + if (url && CFGetTypeID((CFTypeRef)url) == CFURLGetTypeID()) { + url = CFURLCopyAbsoluteURL(url); + CFDictionaryRemoveValue(md, kCFUserNotificationIconURLKey); + path = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle); + CFDictionaryAddValue(md, kCFUserNotificationIconPathKey, path); + CFRelease(url); + CFRelease(path); + } + url = CFDictionaryGetValue(md, kCFUserNotificationSoundURLKey); + if (url && CFGetTypeID((CFTypeRef)url) == CFURLGetTypeID()) { + url = CFURLCopyAbsoluteURL(url); + CFDictionaryRemoveValue(md, kCFUserNotificationSoundURLKey); + path = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle); + CFDictionaryAddValue(md, kCFUserNotificationSoundPathKey, path); + CFRelease(url); + CFRelease(path); + } + url = CFDictionaryGetValue(md, kCFUserNotificationLocalizationURLKey); + if (url && CFGetTypeID((CFTypeRef)url) == CFURLGetTypeID()) { + url = CFURLCopyAbsoluteURL(url); + CFDictionaryRemoveValue(md, kCFUserNotificationLocalizationURLKey); + path = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle); + CFDictionaryAddValue(md, kCFUserNotificationLocalizationPathKey, path); + CFRelease(url); + CFRelease(path); + } + return md; +} + +static SInt32 _CFUserNotificationSendRequest(CFAllocatorRef allocator, CFStringRef sessionID, mach_port_t replyPort, SInt32 token, CFTimeInterval timeout, CFOptionFlags flags, CFDictionaryRef dictionary) { + CFDictionaryRef modifiedDictionary = NULL; + SInt32 retval = ERR_SUCCESS, itimeout = (timeout > 0.0 && timeout < INT_MAX) ? (SInt32)timeout : 0; + CFDataRef data; + mach_msg_base_t *msg = NULL; + mach_port_t bootstrapPort = MACH_PORT_NULL, serverPort = MACH_PORT_NULL; + CFIndex size; + char namebuffer[MAX_PORT_NAME_LENGTH + 1], oldnamebuffer[MAX_PORT_NAME_LENGTH + 1]; + size_t namelen; + + strcpy(namebuffer, NOTIFICATION_PORT_NAME); + strcpy(oldnamebuffer, NOTIFICATION_PORT_NAME_OLD); + if (sessionID) { + strcat(namebuffer, NOTIFICATION_PORT_NAME_SUFFIX); + namelen = strlen(namebuffer); + CFStringGetBytes(sessionID, CFRangeMake(0, CFStringGetLength(sessionID)), kCFStringEncodingUTF8, 0, false, namebuffer + namelen, MAX_PORT_NAME_LENGTH - namelen, &size); + namebuffer[namelen + size] = '\0'; + + strcat(oldnamebuffer, NOTIFICATION_PORT_NAME_SUFFIX); + namelen = strlen(oldnamebuffer); + CFStringGetBytes(sessionID, CFRangeMake(0, CFStringGetLength(sessionID)), kCFStringEncodingUTF8, 0, false, oldnamebuffer + namelen, MAX_PORT_NAME_LENGTH - namelen, &size); + oldnamebuffer[namelen + size] = '\0'; + } + + retval = task_get_bootstrap_port(mach_task_self(), &bootstrapPort); + if (ERR_SUCCESS == retval && MACH_PORT_NULL != bootstrapPort) retval = bootstrap_look_up(bootstrapPort, namebuffer, &serverPort); + if (ERR_SUCCESS != retval || MACH_PORT_NULL == serverPort) retval = bootstrap_look_up(bootstrapPort, oldnamebuffer, &serverPort); + if (ERR_SUCCESS == retval && MACH_PORT_NULL != serverPort) { + modifiedDictionary = _CFUserNotificationModifiedDictionary(allocator, dictionary, token, itimeout, _CFProcessNameString()); + if (modifiedDictionary) { + data = CFPropertyListCreateXMLData(allocator, modifiedDictionary); + if (data) { + size = sizeof(mach_msg_base_t) + ((CFDataGetLength(data) + 3) & (~0x3)); + msg = (mach_msg_base_t *)CFAllocatorAllocate(allocator, size, 0); + if (__CFOASafe) __CFSetLastAllocationEventName(msg, "CFUserNotification (temp)"); + if (msg) { + memset(msg, 0, size); + msg->header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE); + msg->header.msgh_size = size; + msg->header.msgh_remote_port = serverPort; + msg->header.msgh_local_port = replyPort; + msg->header.msgh_id = flags; + msg->body.msgh_descriptor_count = 0; + CFDataGetBytes(data, CFRangeMake(0, CFDataGetLength(data)), (uint8_t *)msg + sizeof(mach_msg_base_t)); + //CFShow(CFStringCreateWithBytes(NULL, (UInt8 *)msg + sizeof(mach_msg_base_t), CFDataGetLength(data), kCFStringEncodingUTF8, false)); + retval = mach_msg((mach_msg_header_t *)msg, MACH_SEND_MSG|MACH_SEND_TIMEOUT, size, 0, MACH_PORT_NULL, MESSAGE_TIMEOUT, MACH_PORT_NULL); + CFAllocatorDeallocate(allocator, msg); + } else { + retval = unix_err(ENOMEM); + } + CFRelease(data); + } else { + retval = unix_err(ENOMEM); + } + CFRelease(modifiedDictionary); + } else { + retval = unix_err(ENOMEM); + } + } + return retval; +} + +CFUserNotificationRef CFUserNotificationCreate(CFAllocatorRef allocator, CFTimeInterval timeout, CFOptionFlags flags, SInt32 *error, CFDictionaryRef dictionary) { + CFUserNotificationRef userNotification = NULL; + SInt32 retval = ERR_SUCCESS; + static uint16_t tokenCounter = 0; + SInt32 token = ((getpid()<<16) | (tokenCounter++)); + CFStringRef sessionID = (dictionary ? CFDictionaryGetValue(dictionary, kCFUserNotificationSessionIDKey) : NULL); + mach_port_t replyPort = MACH_PORT_NULL; + + if (!allocator) allocator = __CFGetDefaultAllocator(); + + retval = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &replyPort); + if (ERR_SUCCESS == retval && MACH_PORT_NULL != replyPort) retval = _CFUserNotificationSendRequest(allocator, sessionID, replyPort, token, timeout, flags, dictionary); + if (ERR_SUCCESS == retval) { + userNotification = (CFUserNotificationRef)_CFRuntimeCreateInstance(allocator, __kCFUserNotificationTypeID, sizeof(struct __CFUserNotification) - sizeof(CFRuntimeBase), NULL); + if (userNotification) { + userNotification->_replyPort = replyPort; + userNotification->_token = token; + userNotification->_timeout = timeout; + userNotification->_requestFlags = flags; + userNotification->_responseFlags = 0; + userNotification->_sessionID = NULL; + userNotification->_responseDictionary = NULL; + userNotification->_machPort = NULL; + userNotification->_callout = NULL; + if (sessionID) userNotification->_sessionID = CFStringCreateCopy(allocator, sessionID); + } else { + retval = unix_err(ENOMEM); + } + } else { + if (dictionary) CFUserNotificationLog(CFDictionaryGetValue(dictionary, kCFUserNotificationAlertHeaderKey), CFDictionaryGetValue(dictionary, kCFUserNotificationAlertMessageKey)); + } + if (ERR_SUCCESS != retval && MACH_PORT_NULL != replyPort) mach_port_destroy(mach_task_self(), replyPort); + if (error) *error = retval; + return userNotification; +} + +static void _CFUserNotificationMachPortCallBack(CFMachPortRef port, void *m, CFIndex size, void *info) { + CFUserNotificationRef userNotification = (CFUserNotificationRef)info; + mach_msg_base_t *msg = (mach_msg_base_t *)m; + CFOptionFlags responseFlags = msg->header.msgh_id; + CFDataRef responseData = CFDataCreate(NULL, (uint8_t *)msg + sizeof(mach_msg_base_t), msg->header.msgh_size - sizeof(mach_msg_base_t)); + if (responseData) { + userNotification->_responseDictionary = CFPropertyListCreateFromXMLData(NULL, responseData, kCFPropertyListImmutable, NULL); + CFRelease(responseData); + } + CFMachPortInvalidate(userNotification->_machPort); + CFRelease(userNotification->_machPort); + userNotification->_machPort = NULL; + mach_port_destroy(mach_task_self(), userNotification->_replyPort); + userNotification->_replyPort = MACH_PORT_NULL; + userNotification->_callout(userNotification, responseFlags); +} + +SInt32 CFUserNotificationReceiveResponse(CFUserNotificationRef userNotification, CFTimeInterval timeout, CFOptionFlags *responseFlags) { + SInt32 retval = ERR_SUCCESS; + mach_msg_timeout_t msgtime = (timeout > 0.0 && 1000.0 * timeout < INT_MAX) ? (mach_msg_timeout_t)(1000.0 * timeout) : 0; + mach_msg_base_t *msg = NULL; + CFIndex size = MAX_STRING_COUNT * MAX_STRING_LENGTH; + CFDataRef responseData; + + if (userNotification && MACH_PORT_NULL != userNotification->_replyPort) { + msg = (mach_msg_base_t *)CFAllocatorAllocate(CFGetAllocator(userNotification), size, 0); + if (__CFOASafe) __CFSetLastAllocationEventName(msg, "CFUserNotification (temp)"); + if (msg) { + memset(msg, 0, size); + msg->header.msgh_size = size; + if (msgtime > 0) { + retval = mach_msg((mach_msg_header_t *)msg, MACH_RCV_MSG|MACH_RCV_TIMEOUT, 0, size, userNotification->_replyPort, msgtime, MACH_PORT_NULL); + } else { + retval = mach_msg((mach_msg_header_t *)msg, MACH_RCV_MSG, 0, size, userNotification->_replyPort, 0, MACH_PORT_NULL); + } + if (ERR_SUCCESS == retval) { + if (responseFlags) *responseFlags = msg->header.msgh_id; + responseData = CFDataCreate(NULL, (uint8_t *)msg + sizeof(mach_msg_base_t), msg->header.msgh_size - sizeof(mach_msg_base_t)); + if (responseData) { + userNotification->_responseDictionary = CFPropertyListCreateFromXMLData(NULL, responseData, kCFPropertyListImmutable, NULL); + CFRelease(responseData); + } + if (userNotification->_machPort) { + CFMachPortInvalidate(userNotification->_machPort); + CFRelease(userNotification->_machPort); + userNotification->_machPort = NULL; + } + mach_port_destroy(mach_task_self(), userNotification->_replyPort); + userNotification->_replyPort = MACH_PORT_NULL; + } + CFAllocatorDeallocate(CFGetAllocator(userNotification), msg); + } else { + retval = unix_err(ENOMEM); + } + } + return retval; +} + +CFStringRef CFUserNotificationGetResponseValue(CFUserNotificationRef userNotification, CFStringRef key, CFIndex idx) { + CFStringRef retval = NULL; + CFTypeRef value = NULL; + if (userNotification && userNotification->_responseDictionary) { + value = CFDictionaryGetValue(userNotification->_responseDictionary, key); + if (CFGetTypeID(value) == CFStringGetTypeID()) { + if (0 == idx) { + retval = (CFStringRef)value; + } + } else if (CFGetTypeID(value) == CFArrayGetTypeID()) { + if (0 <= idx && idx < CFArrayGetCount((CFArrayRef)value)) { + retval = (CFStringRef)CFArrayGetValueAtIndex((CFArrayRef)value, idx); + } + } + } + return retval; +} + +CFDictionaryRef CFUserNotificationGetResponseDictionary(CFUserNotificationRef userNotification) { + return userNotification ? userNotification->_responseDictionary : NULL; +} + +SInt32 CFUserNotificationUpdate(CFUserNotificationRef userNotification, CFTimeInterval timeout, CFOptionFlags flags, CFDictionaryRef dictionary) { + SInt32 retval = ERR_SUCCESS; + if (userNotification && MACH_PORT_NULL != userNotification->_replyPort) { + retval = _CFUserNotificationSendRequest(CFGetAllocator(userNotification), userNotification->_sessionID, userNotification->_replyPort, userNotification->_token, timeout, flags|kCFUserNotificationUpdateFlag, dictionary); + } + return retval; +} + +SInt32 CFUserNotificationCancel(CFUserNotificationRef userNotification) { + SInt32 retval = ERR_SUCCESS; + if (userNotification && MACH_PORT_NULL != userNotification->_replyPort) { + retval = _CFUserNotificationSendRequest(CFGetAllocator(userNotification), userNotification->_sessionID, userNotification->_replyPort, userNotification->_token, 0, kCFUserNotificationCancelFlag, NULL); + } + return retval; +} + +CFRunLoopSourceRef CFUserNotificationCreateRunLoopSource(CFAllocatorRef allocator, CFUserNotificationRef userNotification, CFUserNotificationCallBack callout, CFIndex order) { + CFRunLoopSourceRef source = NULL; + if (userNotification && callout && !userNotification->_machPort && MACH_PORT_NULL != userNotification->_replyPort) { + CFMachPortContext context = {0, userNotification, NULL, NULL, NULL}; + userNotification->_machPort = CFMachPortCreateWithPort(CFGetAllocator(userNotification), (mach_port_t)userNotification->_replyPort, _CFUserNotificationMachPortCallBack, &context, false); + } + if (userNotification && userNotification->_machPort) { + source = CFMachPortCreateRunLoopSource(allocator, userNotification->_machPort, order); + userNotification->_callout = callout; + } + return source; +} + +SInt32 CFUserNotificationDisplayNotice(CFTimeInterval timeout, CFOptionFlags flags, CFURLRef iconURL, CFURLRef soundURL, CFURLRef localizationURL, CFStringRef alertHeader, CFStringRef alertMessage, CFStringRef defaultButtonTitle) { + CFUserNotificationRef userNotification; + SInt32 retval = ERR_SUCCESS; + CFMutableDictionaryRef dict = CFDictionaryCreateMutable(NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + if (iconURL) CFDictionaryAddValue(dict, kCFUserNotificationIconURLKey, iconURL); + if (soundURL) CFDictionaryAddValue(dict, kCFUserNotificationSoundURLKey, soundURL); + if (localizationURL) CFDictionaryAddValue(dict, kCFUserNotificationLocalizationURLKey, localizationURL); + if (alertHeader) CFDictionaryAddValue(dict, kCFUserNotificationAlertHeaderKey, alertHeader); + if (alertMessage) CFDictionaryAddValue(dict, kCFUserNotificationAlertMessageKey, alertMessage); + if (defaultButtonTitle) CFDictionaryAddValue(dict, kCFUserNotificationDefaultButtonTitleKey, defaultButtonTitle); + userNotification = CFUserNotificationCreate(NULL, timeout, flags, &retval, dict); + if (userNotification) CFRelease(userNotification); + CFRelease(dict); + return retval; +} + +CF_EXPORT SInt32 CFUserNotificationDisplayAlert(CFTimeInterval timeout, CFOptionFlags flags, CFURLRef iconURL, CFURLRef soundURL, CFURLRef localizationURL, CFStringRef alertHeader, CFStringRef alertMessage, CFStringRef defaultButtonTitle, CFStringRef alternateButtonTitle, CFStringRef otherButtonTitle, CFOptionFlags *responseFlags) { + CFUserNotificationRef userNotification; + SInt32 retval = ERR_SUCCESS; + CFMutableDictionaryRef dict = CFDictionaryCreateMutable(NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + if (iconURL) CFDictionaryAddValue(dict, kCFUserNotificationIconURLKey, iconURL); + if (soundURL) CFDictionaryAddValue(dict, kCFUserNotificationSoundURLKey, soundURL); + if (localizationURL) CFDictionaryAddValue(dict, kCFUserNotificationLocalizationURLKey, localizationURL); + if (alertHeader) CFDictionaryAddValue(dict, kCFUserNotificationAlertHeaderKey, alertHeader); + if (alertMessage) CFDictionaryAddValue(dict, kCFUserNotificationAlertMessageKey, alertMessage); + if (defaultButtonTitle) CFDictionaryAddValue(dict, kCFUserNotificationDefaultButtonTitleKey, defaultButtonTitle); + if (alternateButtonTitle) CFDictionaryAddValue(dict, kCFUserNotificationAlternateButtonTitleKey, alternateButtonTitle); + if (otherButtonTitle) CFDictionaryAddValue(dict, kCFUserNotificationOtherButtonTitleKey, otherButtonTitle); + userNotification = CFUserNotificationCreate(NULL, timeout, flags, &retval, dict); + if (userNotification) { + retval = CFUserNotificationReceiveResponse(userNotification, timeout, responseFlags); + if (MACH_RCV_TIMED_OUT == retval) { + retval = CFUserNotificationCancel(userNotification); + if (responseFlags) *responseFlags = kCFUserNotificationCancelResponse; + } + CFRelease(userNotification); + } + CFRelease(dict); + return retval; +} + +#else /* __MACH__ */ + +#warning CFUserNotification functions not fully implemented + +void __CFUserNotificationDeallocate(CFTypeRef cf) { +} + +CFUserNotificationRef CFUserNotificationCreate(CFAllocatorRef allocator, CFTimeInterval timeout, CFOptionFlags flags, SInt32 *error, CFDictionaryRef dictionary) { + CFUserNotificationLog(CFDictionaryGetValue(dictionary, kCFUserNotificationAlertHeaderKey), CFDictionaryGetValue(dictionary, kCFUserNotificationAlertMessageKey)); + return NULL; +} + +SInt32 CFUserNotificationReceiveResponse(CFUserNotificationRef userNotification, CFTimeInterval timeout, CFOptionFlags *responseFlags) { + return -1; +} + +CFDictionaryRef CFUserNotificationCopyResponseDictionary(CFUserNotificationRef userNotification) { + return NULL; +} + +SInt32 CFUserNotificationUpdate(CFUserNotificationRef userNotification, CFTimeInterval timeout, CFOptionFlags flags, CFDictionaryRef dictionary) { + return -1; +} + +SInt32 CFUserNotificationCancel(CFUserNotificationRef userNotification) { + return -1; +} + +CFRunLoopSourceRef CFUserNotificationCreateRunLoopSource(CFAllocatorRef allocator, CFUserNotificationRef userNotification, CFUserNotificationCallBack callout, CFIndex order) { + return NULL; +} + +SInt32 CFUserNotificationDisplayNotice(CFTimeInterval timeout, CFOptionFlags flags, CFURLRef iconURL, CFURLRef soundURL, CFURLRef localizationURL, CFStringRef alertHeader, CFStringRef alertMessage, CFStringRef defaultButtonTitle) { + CFUserNotificationLog(alertHeader, alertMessage); + return -1; +} + +SInt32 CFUserNotificationDisplayAlert(CFTimeInterval timeout, CFOptionFlags flags, CFURLRef iconURL, CFURLRef soundURL, CFURLRef localizationURL, CFStringRef alertHeader, CFStringRef alertMessage, CFStringRef defaultButtonTitle, CFStringRef alternateButtonTitle, CFStringRef otherButtonTitle, CFOptionFlags *responseFlags) { + CFUserNotificationLog(alertHeader, alertMessage); + return -1; +} + +#endif /* __MACH__ */ + +#undef __kCFLogUserNotification +#undef CFUserNotificationLog +#undef MAX_STRING_LENGTH +#undef MAX_STRING_COUNT +#undef NOTIFICATION_PORT_NAME +#undef NOTIFICATION_PORT_NAME_OLD +#undef MESSAGE_TIMEOUT + diff --git a/AppServices.subproj/CFUserNotification.h b/AppServices.subproj/CFUserNotification.h new file mode 100644 index 0000000..6fd7f62 --- /dev/null +++ b/AppServices.subproj/CFUserNotification.h @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFUserNotification.h + Copyright (c) 2000-2003, Apple, Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFUSERNOTIFICATION__) +#define __COREFOUNDATION_CFUSERNOTIFICATION__ 1 + +#include +#include +#include +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +typedef struct __CFUserNotification * CFUserNotificationRef; + +/* A CFUserNotification is a notification intended to be presented to a +user at the console (if one is present). This is for the use of processes +that do not otherwise have user interfaces, but may need occasional +interaction with a user. There is a parallel API for this functionality +at the System framework level, described in UNCUserNotification.h. + +The contents of the notification can include a header, a message, textfields, +a popup button, radio buttons or checkboxes, a progress indicator, and up to +three ordinary buttons. All of these items are optional, but a default +button will be supplied even if not specified unless the +kCFUserNotificationNoDefaultButtonFlag is set. + +The contents of the notification are specified in the dictionary used to +create the notification, whose keys should be taken from the list of constants +below, and whose values should be either strings or arrays of strings +(except for kCFUserNotificationProgressIndicatorValueKey, in which case the +value should be a number between 0 and 1, for a "definite" progress indicator, +or a boolean, for an "indefinite" progress indicator). Additionally, URLs can +optionally be supplied for an icon, a sound, and a bundle whose Localizable.strings +files will be used to localize strings. + +Certain request flags are specified when a notification is created. +These specify an alert level for the notification, determine whether +radio buttons or check boxes are to be used, specify which if any of these +are checked by default, specify whether any of the textfields are to +be secure textfields, and determine which popup item should be selected +by default. A timeout is also specified, which determines how long the +notification should be supplied to the user (if zero, it will not timeout). + +A CFUserNotification is dispatched for presentation when it is created. +If any reply is required, it may be awaited in one of two ways: either +synchronously, using CFUserNotificationReceiveResponse, or asynchronously, +using a run loop source. CFUserNotificationReceiveResponse has a timeout +parameter that determines how long it will block (zero meaning indefinitely) +and it may be called as many times as necessary until a response arrives. +If a notification has not yet received a response, it may be updated with +new information, or it may be cancelled. Notifications may not be reused. + +When a response arrives, it carries with it response flags that describe +which button was used to dismiss the notification, which checkboxes or +radio buttons were checked, and what the selection of the popup was. +It also carries a response dictionary, which describes the contents +of the textfields. */ + +typedef void (*CFUserNotificationCallBack)(CFUserNotificationRef userNotification, CFOptionFlags responseFlags); + +CF_EXPORT +CFTypeID CFUserNotificationGetTypeID(void); + +CF_EXPORT +CFUserNotificationRef CFUserNotificationCreate(CFAllocatorRef allocator, CFTimeInterval timeout, CFOptionFlags flags, SInt32 *error, CFDictionaryRef dictionary); + +CF_EXPORT +SInt32 CFUserNotificationReceiveResponse(CFUserNotificationRef userNotification, CFTimeInterval timeout, CFOptionFlags *responseFlags); + +CF_EXPORT +CFStringRef CFUserNotificationGetResponseValue(CFUserNotificationRef userNotification, CFStringRef key, CFIndex idx); + +CF_EXPORT +CFDictionaryRef CFUserNotificationGetResponseDictionary(CFUserNotificationRef userNotification); + +CF_EXPORT +SInt32 CFUserNotificationUpdate(CFUserNotificationRef userNotification, CFTimeInterval timeout, CFOptionFlags flags, CFDictionaryRef dictionary); + +CF_EXPORT +SInt32 CFUserNotificationCancel(CFUserNotificationRef userNotification); + +CF_EXPORT +CFRunLoopSourceRef CFUserNotificationCreateRunLoopSource(CFAllocatorRef allocator, CFUserNotificationRef userNotification, CFUserNotificationCallBack callout, CFIndex order); + +/* Convenience functions for handling the simplest and most common cases: +a one-way notification, and a notification with up to three buttons. */ + +CF_EXPORT +SInt32 CFUserNotificationDisplayNotice(CFTimeInterval timeout, CFOptionFlags flags, CFURLRef iconURL, CFURLRef soundURL, CFURLRef localizationURL, CFStringRef alertHeader, CFStringRef alertMessage, CFStringRef defaultButtonTitle); + +CF_EXPORT +SInt32 CFUserNotificationDisplayAlert(CFTimeInterval timeout, CFOptionFlags flags, CFURLRef iconURL, CFURLRef soundURL, CFURLRef localizationURL, CFStringRef alertHeader, CFStringRef alertMessage, CFStringRef defaultButtonTitle, CFStringRef alternateButtonTitle, CFStringRef otherButtonTitle, CFOptionFlags *responseFlags); + + +/* Flags */ + +enum { + kCFUserNotificationStopAlertLevel = 0, + kCFUserNotificationNoteAlertLevel = 1, + kCFUserNotificationCautionAlertLevel = 2, + kCFUserNotificationPlainAlertLevel = 3 +}; + +enum { + kCFUserNotificationDefaultResponse = 0, + kCFUserNotificationAlternateResponse = 1, + kCFUserNotificationOtherResponse = 2, + kCFUserNotificationCancelResponse = 3 +}; + +enum { + kCFUserNotificationNoDefaultButtonFlag = (1 << 5), + kCFUserNotificationUseRadioButtonsFlag = (1 << 6) +}; + +CF_INLINE CFOptionFlags CFUserNotificationCheckBoxChecked(CFIndex i) {return ((CFOptionFlags)(1 << (8 + i)));} +CF_INLINE CFOptionFlags CFUserNotificationSecureTextField(CFIndex i) {return ((CFOptionFlags)(1 << (16 + i)));} +CF_INLINE CFOptionFlags CFUserNotificationPopUpSelection(CFIndex n) {return ((CFOptionFlags)(n << 24));} + + +/* Keys */ + +CF_EXPORT +const CFStringRef kCFUserNotificationIconURLKey; + +CF_EXPORT +const CFStringRef kCFUserNotificationSoundURLKey; + +CF_EXPORT +const CFStringRef kCFUserNotificationLocalizationURLKey; + +CF_EXPORT +const CFStringRef kCFUserNotificationAlertHeaderKey; + +CF_EXPORT +const CFStringRef kCFUserNotificationAlertMessageKey; + +CF_EXPORT +const CFStringRef kCFUserNotificationDefaultButtonTitleKey; + +CF_EXPORT +const CFStringRef kCFUserNotificationAlternateButtonTitleKey; + +CF_EXPORT +const CFStringRef kCFUserNotificationOtherButtonTitleKey; + +CF_EXPORT +const CFStringRef kCFUserNotificationProgressIndicatorValueKey; + +CF_EXPORT +const CFStringRef kCFUserNotificationPopUpTitlesKey; + +CF_EXPORT +const CFStringRef kCFUserNotificationTextFieldTitlesKey; + +CF_EXPORT +const CFStringRef kCFUserNotificationCheckBoxTitlesKey; + +CF_EXPORT +const CFStringRef kCFUserNotificationTextFieldValuesKey; + +#if MAC_OS_X_VERSION_10_3 <= MAC_OS_X_VERSION_MAX_ALLOWED +CF_EXPORT +const CFStringRef kCFUserNotificationPopUpSelectionKey AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; +#endif + +#if defined(__cplusplus) +} +#endif + +#endif /* ! __COREFOUNDATION_CFUSERNOTIFICATION__ */ + diff --git a/Base.subproj/CFBase.c b/Base.subproj/CFBase.c new file mode 100644 index 0000000..076924c --- /dev/null +++ b/Base.subproj/CFBase.c @@ -0,0 +1,876 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFBase.c + Copyright 1998-2002, Apple, Inc. All rights reserved. + Responsibility: Christopher Kane +*/ + +#include +#include "CFInternal.h" +#if defined(__MACH__) || defined(__LINUX__) || defined(__FREEBSD__) + #include +#endif +#if defined(__MACH__) +#endif +#if defined(__WIN32__) + #include +#endif +#if defined(__MACH__) + #include + #include + #include +#endif +#include +#include + +extern size_t malloc_good_size(size_t size); + +// -------- -------- -------- -------- -------- -------- -------- -------- + +#if defined(__MACH__) +// CFAllocator structure must match struct _malloc_zone_t! +// The first two reserved fields in struct _malloc_zone_t are for us with CFRuntimeBase +#endif +struct __CFAllocator { + CFRuntimeBase _base; +#if defined(__MACH__) + 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 defined(__MACH__) + +__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((unsigned)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); +} + +static struct malloc_introspection_t __CFAllocatorZoneIntrospect = { + (void *)__CFAllocatorZoneIntrospectNoOp, + (void *)__CFAllocatorCustomGoodSize, + (void *)__CFAllocatorZoneIntrospectTrue, + (void *)__CFAllocatorZoneIntrospectNoOp, + (void *)__CFAllocatorZoneIntrospectNoOp, + (void *)__CFAllocatorZoneIntrospectNoOp, + (void *)__CFAllocatorZoneIntrospectNoOp, + (void *)__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 = { + (void *)__CFAllocatorZoneIntrospectNoOp, + (void *)__CFAllocatorNullGoodSize, + (void *)__CFAllocatorZoneIntrospectTrue, + (void *)__CFAllocatorZoneIntrospectNoOp, + (void *)__CFAllocatorZoneIntrospectNoOp, + (void *)__CFAllocatorZoneIntrospectNoOp, + (void *)__CFAllocatorZoneIntrospectNoOp, + (void *)__CFAllocatorZoneIntrospectNoOp +}; + +static void *__CFAllocatorSystemAllocate(CFIndex size, CFOptionFlags hint, void *info) { + return malloc_zone_malloc(info, size); +} + +static void *__CFAllocatorSystemReallocate(void *ptr, CFIndex newsize, CFOptionFlags hint, void *info) { + return malloc_zone_realloc(info, ptr, newsize); +} + +static void __CFAllocatorSystemDeallocate(void *ptr, void *info) { + malloc_zone_free(info, ptr); +} + +#endif + +#if defined(__WIN32__) || defined(__LINUX__) || defined(__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) { + 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; +} + +static struct __CFAllocator __kCFAllocatorMalloc = { + {NULL, 0, 0x0080}, +#if defined(__MACH__) + __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. + {0, NULL, NULL, NULL, NULL, (void *)malloc, (void *)realloc, (void *)free, NULL} +}; + +static struct __CFAllocator __kCFAllocatorSystemDefault = { + {NULL, 0, 0x0080}, +#if defined(__MACH__) + __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 = { + {NULL, 0, 0x0080}, +#if defined(__MACH__) + __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 kCFAllocatorNull = &__kCFAllocatorNull; +const CFAllocatorRef kCFAllocatorUseContext = (CFAllocatorRef)0x0227; + +static CFStringRef __CFAllocatorCopyDescription(CFTypeRef cf) { + CFAllocatorRef self = cf; + CFAllocatorRef allocator = (kCFAllocatorUseContext == self->_allocator) ? self : self->_allocator; + return CFStringCreateWithFormat(allocator, NULL, CFSTR("{info = 0x%x}"), (UInt32)cf, (UInt32)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 = cf; + return (kCFAllocatorUseContext == allocator->_allocator) ? allocator : allocator->_allocator; +} + +__private_extern__ void __CFAllocatorDeallocate(CFTypeRef cf) { + CFAllocatorRef self = 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); + } + CFAllocatorDeallocate(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._isa = __CFISAForTypeID(__kCFAllocatorTypeID); +#if defined(__MACH__) + __kCFAllocatorSystemDefault._context.info = malloc_default_zone(); +#endif + __kCFAllocatorSystemDefault._allocator = kCFAllocatorSystemDefault; + memset(malloc_default_zone(), 0, 8); + + _CFRuntimeSetInstanceTypeID(&__kCFAllocatorMalloc, __kCFAllocatorTypeID); + __kCFAllocatorMalloc._base._isa = __CFISAForTypeID(__kCFAllocatorTypeID); + __kCFAllocatorMalloc._allocator = kCFAllocatorSystemDefault; + + _CFRuntimeSetInstanceTypeID(&__kCFAllocatorNull, __kCFAllocatorTypeID); + __kCFAllocatorNull._base._isa = __CFISAForTypeID(__kCFAllocatorTypeID); + __kCFAllocatorNull._allocator = kCFAllocatorSystemDefault; + +} + +CFTypeID CFAllocatorGetTypeID(void) { + return __kCFAllocatorTypeID; +} + +CFAllocatorRef CFAllocatorGetDefault(void) { + CFAllocatorRef allocator = __CFGetThreadSpecificData_inline()->_allocator; + if (NULL == allocator) { + allocator = kCFAllocatorSystemDefault; + } + return allocator; +} + +void CFAllocatorSetDefault(CFAllocatorRef allocator) { + CFAllocatorRef current = __CFGetThreadSpecificData_inline()->_allocator; +#if defined(DEBUG) + if (NULL != allocator) { + __CFGenericValidateType(allocator, __kCFAllocatorTypeID); + } +#endif +#if defined(__MACH__) + if (allocator && allocator->_base._isa != __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; + } +} + +CFAllocatorRef CFAllocatorCreate(CFAllocatorRef allocator, CFAllocatorContext *context) { + struct __CFAllocator *memory; + CFAllocatorRetainCallBack retainFunc; + CFAllocatorAllocateCallBack allocateFunc; + void *retainedInfo; +#if defined(DEBUG) + if (NULL == context->allocate) { + HALT; + } +#endif +#if defined(__MACH__) + if (allocator && kCFAllocatorUseContext != allocator && allocator->_base._isa != __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 = (void *)INVOKE_CALLBACK3(allocateFunc, sizeof(struct __CFAllocator), 0, retainedInfo); + if (NULL == memory) { + return NULL; + } + } else { + allocator = (NULL == allocator) ? __CFGetDefaultAllocator() : allocator; + memory = CFAllocatorAllocate(allocator, sizeof(struct __CFAllocator), 0); + if (__CFOASafe) __CFSetLastAllocationEventName(memory, "CFAllocator"); + if (NULL == memory) { + return NULL; + } + } + memory->_base._isa = 0; + memory->_base._rc = 1; + memory->_base._info = 0; + _CFRuntimeSetInstanceTypeID(memory, __kCFAllocatorTypeID); + memory->_base._isa = __CFISAForTypeID(__kCFAllocatorTypeID); +#if defined(__MACH__) + 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; +} + +void *CFAllocatorAllocate(CFAllocatorRef allocator, CFIndex size, CFOptionFlags hint) { + CFAllocatorAllocateCallBack allocateFunc; + void *newptr; + allocator = (NULL == allocator) ? __CFGetDefaultAllocator() : allocator; +#if defined(__MACH__) && defined(DEBUG) + if (allocator->_base._isa == __CFISAForTypeID(__kCFAllocatorTypeID)) { + __CFGenericValidateType(allocator, __kCFAllocatorTypeID); + } +#else + __CFGenericValidateType(allocator, __kCFAllocatorTypeID); +#endif + if (0 == size) return NULL; +#if defined(__MACH__) + if (allocator->_base._isa != __CFISAForTypeID(__kCFAllocatorTypeID)) { // malloc_zone_t * + return malloc_zone_malloc((malloc_zone_t *)allocator, size); + } +#endif + allocateFunc = __CFAllocatorGetAllocateFunction(&allocator->_context); + 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 defined(__MACH__) && defined(DEBUG) + if (allocator->_base._isa == __CFISAForTypeID(__kCFAllocatorTypeID)) { + __CFGenericValidateType(allocator, __kCFAllocatorTypeID); + } +#else + __CFGenericValidateType(allocator, __kCFAllocatorTypeID); +#endif + if (NULL == ptr && 0 < newsize) { +#if defined(__MACH__) + if (allocator->_base._isa != __CFISAForTypeID(__kCFAllocatorTypeID)) { // malloc_zone_t * + return malloc_zone_malloc((malloc_zone_t *)allocator, newsize); + } +#endif + allocateFunc = __CFAllocatorGetAllocateFunction(&allocator->_context); + newptr = (void *)INVOKE_CALLBACK3(allocateFunc, newsize, hint, allocator->_context.info); + return newptr; + } + if (NULL != ptr && 0 == newsize) { +#if defined(__MACH__) + if (allocator->_base._isa != __CFISAForTypeID(__kCFAllocatorTypeID)) { // malloc_zone_t * + malloc_zone_free((malloc_zone_t *)allocator, ptr); + return NULL; + } +#endif + deallocateFunc = __CFAllocatorGetDeallocateFunction(&allocator->_context); + if (NULL != deallocateFunc) { + INVOKE_CALLBACK2(deallocateFunc, ptr, allocator->_context.info); + } + return NULL; + } + if (NULL == ptr && 0 == newsize) return NULL; +#if defined(__MACH__) + if (allocator->_base._isa != __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 defined(__MACH__) && defined(DEBUG) + if (allocator->_base._isa == __CFISAForTypeID(__kCFAllocatorTypeID)) { + __CFGenericValidateType(allocator, __kCFAllocatorTypeID); + } +#else + __CFGenericValidateType(allocator, __kCFAllocatorTypeID); +#endif +#if defined(__MACH__) + if (allocator->_base._isa != __CFISAForTypeID(__kCFAllocatorTypeID)) { // malloc_zone_t * + return malloc_zone_free((malloc_zone_t *)allocator, ptr); + } +#endif + 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 defined(__MACH__) && defined(DEBUG) + if (allocator->_base._isa == __CFISAForTypeID(__kCFAllocatorTypeID)) { + __CFGenericValidateType(allocator, __kCFAllocatorTypeID); + } +#else + __CFGenericValidateType(allocator, __kCFAllocatorTypeID); +#endif +#if defined(__MACH__) + if (allocator->_base._isa != __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 defined(__MACH__) && defined(DEBUG) + if (allocator->_base._isa == __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 defined(__MACH__) + if (allocator->_base._isa != __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); + 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); +} + +// -------- -------- -------- -------- -------- -------- -------- -------- + +#if defined(__MACH__) || defined(__LINUX__) || defined(__FREEBSD__) +__private_extern__ pthread_key_t __CFTSDKey = (pthread_key_t)NULL; +#endif +#if defined(__WIN32__) +__private_extern__ DWORD __CFTSDKey = 0xFFFFFFFF; +#endif + +// Called for each thread as it exits +static void __CFFinalizeThreadData(void *arg) { + __CFThreadSpecificData *tsd = (__CFThreadSpecificData *)arg; + if (NULL == tsd) return; + if (tsd->_allocator) CFRelease(tsd->_allocator); + if (tsd->_runLoop) CFRelease(tsd->_runLoop); + CFAllocatorDeallocate(kCFAllocatorSystemDefault, tsd); +} + +__private_extern__ __CFThreadSpecificData *__CFGetThreadSpecificData(void) { +#if defined(__MACH__) || defined(__LINUX__) || defined(__FREEBSD__) + __CFThreadSpecificData *data; + data = pthread_getspecific(__CFTSDKey); + if (data) { + return data; + } + data = CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(__CFThreadSpecificData), 0); + if (__CFOASafe) __CFSetLastAllocationEventName(data, "CFUtilities (thread-data)"); + memset(data, 0, sizeof(__CFThreadSpecificData)); + pthread_setspecific(__CFTSDKey, data); + return data; +#elif defined(__WIN32__) + __CFThreadSpecificData *data; + data = TlsGetValue(__CFTSDKey); + if (data) { + return data; + } + data = 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 defined(__MACH__) || defined(__LINUX__) || defined(__FREEBSD__) + pthread_key_create(&__CFTSDKey, __CFFinalizeThreadData); +#endif +#if defined(__WIN32__) + __CFTSDKey = TlsAlloc(); +#endif +} + + +CFRange __CFRangeMake(CFIndex loc, CFIndex len) { + CFRange range; + range.location = loc; + range.length = len; + return range; +} + +__private_extern__ const void *__CFTypeCollectionRetain(CFAllocatorRef allocator, const void *ptr) { + return (const void *)CFRetain(ptr); +} + +__private_extern__ void __CFTypeCollectionRelease(CFAllocatorRef allocator, const void *ptr) { + CFRelease(ptr); +} + + +struct __CFNull { + CFRuntimeBase _base; +}; + +static struct __CFNull __kCFNull = { + {NULL, 0, 0x0080} +}; +const CFNullRef kCFNull = &__kCFNull; + +static CFStringRef __CFNullCopyDescription(CFTypeRef cf) { + return CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR(""), cf, CFGetAllocator(cf)); +} + +static CFStringRef __CFNullCopyFormattingDescription(CFTypeRef cf, CFDictionaryRef formatOptions) { + return 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._isa = __CFISAForTypeID(__kCFNullTypeID); +} + +CFTypeID CFNullGetTypeID(void) { + return __kCFNullTypeID; +} + + +static int hasCFM = 0; + +void _CFRuntimeSetCFMPresent(int a) { + hasCFM = 1; +} + +#if defined(__MACH__) && 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) { + *ptr = (void *)(p | 0x1); + } else { + int __known = _dyld_image_containing_address(p); + *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(__ppc__) +__asm__ ( +".text\n" +" .align 2\n" +#if defined(__MACH__) +".private_extern ___HALT\n" +#else +".globl ___HALT\n" +#endif +"___HALT:\n" +" trap\n" +); +#endif + +#if defined(__i386__) +__asm__ ( +".text\n" +" .align 2, 0x90\n" +#if defined(__MACH__) +".private_extern ___HALT\n" +#else +".globl ___HALT\n" +#endif +"___HALT:\n" +" int3\n" +); +#endif + diff --git a/Base.subproj/CFBase.h b/Base.subproj/CFBase.h new file mode 100644 index 0000000..09f1990 --- /dev/null +++ b/Base.subproj/CFBase.h @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFBase.h + Copyright (c) 1998-2003, Apple, Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFBASE__) +#define __COREFOUNDATION_CFBASE__ 1 + +#if defined(__WIN32__) +#include +#endif + +#include +#include +#include + +#if defined(__MACH__) + #include +#else + typedef unsigned char Boolean; + typedef unsigned char UInt8; + typedef signed char SInt8; + typedef unsigned short UInt16; + typedef signed short SInt16; + typedef unsigned long UInt32; + typedef signed long SInt32; + typedef uint64_t UInt64; + typedef int64_t SInt64; + 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 SInt32 OSStatus; + typedef UInt32 UTF32Char; + typedef UInt16 UTF16Char; + typedef UInt8 UTF8Char; +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +#if !defined(NULL) + #define NULL 0 +#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 +#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__) + #define CF_INLINE static __inline__ + #elif defined(__MWERKS__) || defined(__cplusplus) + #define CF_INLINE static inline + #elif defined(__WIN32__) + #define CF_INLINE static __inline__ + #endif +#endif + + +CF_EXPORT double kCFCoreFoundationVersionNumber; + +#define kCFCoreFoundationVersionNumber10_0 196.4 +#define kCFCoreFoundationVersionNumber10_0_3 196.5 +#if MAC_OS_X_VERSION_10_2 <= MAC_OS_X_VERSION_MAX_ALLOWED +#define kCFCoreFoundationVersionNumber10_1 226.0 +/* Note these do not follow the usual numbering policy from the base release */ +#define kCFCoreFoundationVersionNumber10_1_2 227.2 +#define kCFCoreFoundationVersionNumber10_1_4 227.3 +#endif +#if MAC_OS_X_VERSION_10_3 <= MAC_OS_X_VERSION_MAX_ALLOWED +#define kCFCoreFoundationVersionNumber10_2 263.0 +#endif + +typedef UInt32 CFTypeID; +typedef UInt32 CFOptionFlags; +typedef UInt32 CFHashCode; +typedef SInt32 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 */ +typedef enum { + kCFCompareLessThan = -1, + kCFCompareEqualTo = 0, + kCFCompareGreaterThan = 1 +} 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; + +/* 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 +Boolean CFEqual(CFTypeRef cf1, CFTypeRef cf2); + +CF_EXPORT +CFHashCode CFHash(CFTypeRef cf); + +CF_EXPORT +CFStringRef CFCopyDescription(CFTypeRef cf); + +CF_EXPORT +CFAllocatorRef CFGetAllocator(CFTypeRef cf); + +#if defined(__cplusplus) +} +#endif + +#endif /* ! __COREFOUNDATION_CFBASE__ */ + diff --git a/Base.subproj/CFByteOrder.h b/Base.subproj/CFByteOrder.h new file mode 100644 index 0000000..af3b0d7 --- /dev/null +++ b/Base.subproj/CFByteOrder.h @@ -0,0 +1,298 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFByteOrder.h + Copyright (c) 1995-2003, Apple, Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFBYTEORDER__) +#define __COREFOUNDATION_CFBYTEORDER__ 1 + +#if defined(__i386) && !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 + +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +typedef enum __CFByteOrder { + CFByteOrderUnknown, + CFByteOrderLittleEndian, + CFByteOrderBigEndian +} CFByteOrder; + +CF_INLINE CFByteOrder CFByteOrderGetCurrent(void) { + uint32_t x = (CFByteOrderBigEndian << 24) | CFByteOrderLittleEndian; + return (CFByteOrder)*((uint8_t *)&x); +} + +CF_INLINE uint16_t CFSwapInt16(uint16_t arg) { +#if defined(__i386__) && defined(__GNUC__) + __asm__("xchgb %b0, %h0" : "+q" (arg)); + return arg; +#elif defined(__ppc__) && defined(__GNUC__) + uint16_t result; + __asm__("lhbrx %0,0,%1" : "=r" (result) : "r" (&arg), "m" (arg)); + return result; +#else + uint16_t result; + result = ((arg << 8) & 0xFF00) | ((arg >> 8) & 0xFF); + return result; +#endif +} + +CF_INLINE uint32_t CFSwapInt32(uint32_t arg) { +#if defined(__i386__) && defined(__GNUC__) + __asm__("bswap %0" : "+r" (arg)); + return arg; +#elif defined(__ppc__) && defined(__GNUC__) + uint32_t result; + __asm__("lwbrx %0,0,%1" : "=r" (result) : "r" (&arg), "m" (arg)); + return result; +#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) { + 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; +} + +CF_INLINE uint16_t CFSwapInt16BigToHost(uint16_t arg) { +#if defined(__BIG_ENDIAN__) + return arg; +#else + return CFSwapInt16(arg); +#endif +} + +CF_INLINE uint32_t CFSwapInt32BigToHost(uint32_t arg) { +#if defined(__BIG_ENDIAN__) + return arg; +#else + return CFSwapInt32(arg); +#endif +} + +CF_INLINE uint64_t CFSwapInt64BigToHost(uint64_t arg) { +#if defined(__BIG_ENDIAN__) + return arg; +#else + return CFSwapInt64(arg); +#endif +} + +CF_INLINE uint16_t CFSwapInt16HostToBig(uint16_t arg) { +#if defined(__BIG_ENDIAN__) + return arg; +#else + return CFSwapInt16(arg); +#endif +} + +CF_INLINE uint32_t CFSwapInt32HostToBig(uint32_t arg) { +#if defined(__BIG_ENDIAN__) + return arg; +#else + return CFSwapInt32(arg); +#endif +} + +CF_INLINE uint64_t CFSwapInt64HostToBig(uint64_t arg) { +#if defined(__BIG_ENDIAN__) + return arg; +#else + return CFSwapInt64(arg); +#endif +} + +CF_INLINE uint16_t CFSwapInt16LittleToHost(uint16_t arg) { +#if defined(__LITTLE_ENDIAN__) + return arg; +#else + return CFSwapInt16(arg); +#endif +} + +CF_INLINE uint32_t CFSwapInt32LittleToHost(uint32_t arg) { +#if defined(__LITTLE_ENDIAN__) + return arg; +#else + return CFSwapInt32(arg); +#endif +} + +CF_INLINE uint64_t CFSwapInt64LittleToHost(uint64_t arg) { +#if defined(__LITTLE_ENDIAN__) + return arg; +#else + return CFSwapInt64(arg); +#endif +} + +CF_INLINE uint16_t CFSwapInt16HostToLittle(uint16_t arg) { +#if defined(__LITTLE_ENDIAN__) + return arg; +#else + return CFSwapInt16(arg); +#endif +} + +CF_INLINE uint32_t CFSwapInt32HostToLittle(uint32_t arg) { +#if defined(__LITTLE_ENDIAN__) + return arg; +#else + return CFSwapInt32(arg); +#endif +} + +CF_INLINE uint64_t CFSwapInt64HostToLittle(uint64_t arg) { +#if defined(__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 defined(__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 defined(__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 defined(__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 defined(__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 defined(__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 defined(__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 defined(__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 defined(__LITTLE_ENDIAN__) + result.sv.v = CFSwapInt64(result.sv.v); +#endif + return result.v; +} + +#if defined(__cplusplus) +} +#endif + +#endif /* ! __COREFOUNDATION_CFBYTEORDER__ */ + diff --git a/Base.subproj/CFFileUtilities.c b/Base.subproj/CFFileUtilities.c new file mode 100644 index 0000000..64c672a --- /dev/null +++ b/Base.subproj/CFFileUtilities.c @@ -0,0 +1,767 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFFileUtilities.c + Copyright 1999-2002, Apple, Inc. All rights reserved. + Responsibility: Christopher Kane +*/ + +#include "CFInternal.h" +#include "CFPriv.h" +#if defined(__WIN32__) + #include + #include + #include + #include + #define timeval xxx_timeval + #define BOOLEAN xxx_BOOLEAN + #include + #undef BOOLEAN + #undef timeval + #define fstat _fstat + #define open _open + #define close _close + #define write _write + #define read _read + #define stat _stat +#else + #include + #include + #include + #include + #include + #include + #include + #include + #include +#endif + +#if defined(__WIN32__) + #define CF_OPENFLGS (_O_BINARY|_O_NOINHERIT) +#else + #define CF_OPENFLGS (0) +#endif + + +__private_extern__ CFStringRef _CFCopyExtensionForAbstractType(CFStringRef abstractType) { + return (abstractType ? CFRetain(abstractType) : NULL); +} + + +__private_extern__ Boolean _CFCreateDirectory(const char *path) { +#if defined(__WIN32__) + return CreateDirectoryA(path, (LPSECURITY_ATTRIBUTES)NULL); +#else + return ((mkdir(path, 0777) == 0) ? true : false); +#endif +} + +__private_extern__ Boolean _CFRemoveDirectory(const char *path) { +#if defined(__WIN32__) + return RemoveDirectoryA(path); +#else + return ((rmdir(path) == 0) ? true : false); +#endif +} + +__private_extern__ Boolean _CFDeleteFile(const char *path) { +#if defined(__WIN32__) + return DeleteFileA(path); +#else + return unlink(path) == 0; +#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, path, CFMaxPathSize)) { + return false; + } + + *bytes = NULL; + +__CFSetNastyFile(url); + +#if defined(__WIN32__) + fd = open(path, O_RDONLY|CF_OPENFLGS, 0666|_S_IREAD); +#else + fd = open(path, O_RDONLY|CF_OPENFLGS, 0666); +#endif + if (fd < 0) { + return false; + } + if (fstat(fd, &statBuf) < 0) { + int saveerr = thread_errno(); + close(fd); + thread_set_errno(saveerr); + return false; + } + if ((statBuf.st_mode & S_IFMT) != S_IFREG) { + close(fd); + 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)"); + if (read(fd, *bytes, desiredLength) < 0) { + CFAllocatorDeallocate(alloc, *bytes); + close(fd); + return false; + } + *length = desiredLength; + } + close(fd); + return true; +} + +__private_extern__ Boolean _CFWriteBytesToFile(CFURLRef url, const void *bytes, CFIndex length) { + struct stat statBuf; + int fd = -1; + int mode, mask; + char path[CFMaxPathSize]; + if (!CFURLGetFileSystemRepresentation(url, true, path, CFMaxPathSize)) { + return false; + } + +#if defined(__WIN32__) + mask = 0; +#else + mask = umask(0); + umask(mask); +#endif + mode = 0666 & ~mask; + if (0 == stat(path, &statBuf)) { + mode = statBuf.st_mode; + } else if (thread_errno() != ENOENT) { + return false; + } +#if defined(__WIN32__) + fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|CF_OPENFLGS, 0666|_S_IWRITE); +#else + fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|CF_OPENFLGS, 0666); +#endif + if (fd < 0) { + return false; + } + if (length && write(fd, bytes, length) != length) { + int saveerr = thread_errno(); + close(fd); + thread_set_errno(saveerr); + return false; + } +#if defined(__WIN32__) + FlushFileBuffers((HANDLE)_get_osfhandle(fd)); +#else + fsync(fd); +#endif + close(fd); + 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. */ +__private_extern__ CFMutableArrayRef _CFContentsOfDirectory(CFAllocatorRef alloc, char *dirPath, void *dirSpec, CFURLRef dirURL, CFStringRef matchingAbstractType) { + CFMutableArrayRef files = NULL; + Boolean releaseBase = false; + CFIndex pathLength = dirPath ? 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 defined(__WIN32__) + /* Windows Variables */ + /* The Win32 code has not been updated for: + path has been renamed dirPath + base has been renamed dirURL + dirPath may be NULL (in which case dirURL is not) + if dirPath is NULL, pathLength is 0 + */ + WIN32_FIND_DATA file; + HANDLE handle; +#elif defined(__svr4__) || defined(__hpux__) || defined(__LINUX__) || defined(__FREEBSD__) + /* Solaris and HPUX Variables */ + /* The Solaris and HPUX code has not been updated for: + base has been renamed dirURL + dirPath may be NULL (in which case dirURL is not) + if dirPath is NULL, pathLength is 0 + */ + DIR *dirp; + struct dirent *dp; + int err; +#elif defined(__MACH__) + /* Mac OS X Variables */ + int fd, numread; + long basep; + char dirge[8192]; + uint8_t pathBuf[CFMaxPathSize]; +#endif + + + if (extLen > 0) { + CFStringGetBytes(extension, CFRangeMake(0, extLen), CFStringFileSystemEncoding(), 0, false, extBuff, CFMaxPathSize, &extLen); + extBuff[extLen] = '\0'; + } + +#if defined(__WIN32__) + /* Windows Implementation */ + + 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 = 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(&(file.cFileName[namelen - extLen]), extBuff) != 0) { + continue; + } + } + if (dirURL == NULL) { + dirURL = CFURLCreateFromFileSystemRepresentation(alloc, dirPath, pathLength, true); + releaseBase = true; + } + // MF:!!! What about the trailing slash? + fileURL = CFURLCreateFromFileSystemRepresentationRelativeToBase(alloc, file.cFileName, namelen, false, dirURL); + CFArrayAppendValue(files, fileURL); + CFRelease(fileURL); + } while (FindNextFileA(handle, &file)); + FindClose(handle); + dirPath[pathLength] = '\0'; + +#elif defined(__svr4__) || defined(__hpux__) || defined(__LINUX__) || defined(__FREEBSD__) + /* Solaris and HPUX Implementation */ + + dirp = opendir(dirPath); + if (!dirp) { + if (extension) { + CFRelease(extension); + } + return NULL; + // raiseErrno("opendir", path); + } + files = CFArrayCreateMutable(alloc, 0, & kCFTypeArrayCallBacks); + + while((dp = readdir(dirp)) != NULL) { + 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 > 0) { + // Check to see if it matches the extension we're looking for. + if (strncmp(&(dp->d_name[namelen - extLen]), extBuff, extLen) != 0) { + continue; + } + } + if (dirURL == NULL) { + dirURL = CFURLCreateFromFileSystemRepresentation(alloc, dirPath, pathLength, true); + releaseBase = true; + } + // MF:!!! What about the trailing slash? + fileURL = CFURLCreateFromFileSystemRepresentationRelativeToBase(alloc, dp->d_name, namelen, false, dirURL); + CFArrayAppendValue(files, fileURL); + CFRelease(fileURL); + } + err = closedir(dirp); + if (err != 0) { + CFRelease(files); + if (releaseBase) { + CFRelease(dirURL); + } + if (extension) { + CFRelease(extension); + } + return NULL; + // raiseErrno("closedir", path); + } + +#elif defined(__MACH__) + /* Mac OS X Variables - repeated for convenience */ + // int fd, numread; + // long basep; + // char dirge[8192]; + // UInt8 pathBuf[CFMaxPathSize]; + /* Mac OS X Implementation */ + + if (!dirPath) { + if (!CFURLGetFileSystemRepresentation(dirURL, true, pathBuf, CFMaxPathLength)) { + if (extension) CFRelease(extension); + return NULL; + } else { + dirPath = pathBuf; + pathLength = strlen(dirPath); + } + } + fd = open(dirPath, O_RDONLY, 0777); + if (fd < 0) { + if (extension) { + CFRelease(extension); + } + return NULL; + } + files = CFArrayCreateMutable(alloc, 0, &kCFTypeArrayCallBacks); + + while ((numread = getdirentries(fd, dirge, sizeof(dirge), &basep)) > 0) { + struct dirent *dent; + for (dent = (struct dirent *)dirge; dent < (struct dirent *)(dirge + numread); dent = (struct dirent *)((char *)dent + dent->d_reclen)) { + CFURLRef fileURL; + CFIndex nameLen; + + nameLen = dent->d_namlen; + // skip . & ..; they cause descenders to go berserk + if (0 == dent->d_fileno || (dent->d_name[0] == '.' && (nameLen == 1 || (nameLen == 2 && dent->d_name[1] == '.')))) { + continue; + } + if (extLen > 0) { + // Check to see if it matches the extension we're looking for. + if (strncmp(&(dent->d_name[nameLen - extLen]), extBuff, extLen) != 0) { + continue; + } + } + if (dirURL == NULL) { + dirURL = CFURLCreateFromFileSystemRepresentation(alloc, dirPath, pathLength, true); + releaseBase = true; + } + + if (dent->d_type == DT_DIR || dent->d_type == DT_UNKNOWN) { + Boolean isDir = (dent->d_type == DT_DIR); + if (!isDir) { + // Ugh; must stat. + char subdirPath[CFMaxPathLength]; + struct stat statBuf; + strncpy(subdirPath, dirPath, pathLength); + subdirPath[pathLength] = '/'; + strncpy(subdirPath + pathLength + 1, dent->d_name, nameLen); + subdirPath[pathLength + nameLen + 1] = '\0'; + if (stat(subdirPath, &statBuf) == 0) { + isDir = ((statBuf.st_mode & S_IFMT) == S_IFDIR); + } + } + fileURL = CFURLCreateFromFileSystemRepresentationRelativeToBase(alloc, dent->d_name, nameLen, isDir, dirURL); + } else { + fileURL = CFURLCreateFromFileSystemRepresentationRelativeToBase (alloc, dent->d_name, nameLen, false, dirURL); + } + CFArrayAppendValue(files, fileURL); + CFRelease(fileURL); + } + } + close(fd); + if (-1 == numread) { + CFRelease(files); + if (releaseBase) { + CFRelease(dirURL); + } + if (extension) { + CFRelease(extension); + } + return NULL; + } +#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; + + struct stat statBuf; + char path[CFMaxPathLength]; + + if ((exists == NULL) && (posixMode == NULL) && (size == NULL) && (modTime == NULL) && (ownerID == NULL) && (dirContents == NULL)) { + // Nothing to do. + return 0; + } + + if (!CFURLGetFileSystemRepresentation(pathURL, true, path, CFMaxPathLength)) { + return -1; + } + + if (stat(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); + } + + + 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) { + CFTimeInterval theTime; + + theTime = kCFAbsoluteTimeIntervalSince1970 + statBuf.st_mtime; + + *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 defined(__MACH__) || defined(__svr4__) || defined(__hpux__) || defined(__LINUX__) || defined(__FREEBSD__) + #define UNIX_PATH_SEMANTICS +#elif defined(__WIN32__) + #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/Base.subproj/CFInternal.h b/Base.subproj/CFInternal.h new file mode 100644 index 0000000..5dd95b9 --- /dev/null +++ b/Base.subproj/CFInternal.h @@ -0,0 +1,461 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFInternal.h + Copyright (c) 1998-2003, 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 +#include +#include +#include +#include +#include "ForFoundationOnly.h" +#include "CFRuntime.h" +#if defined(__MACH__) +#include +#endif +#if defined(__MACH__) || defined(__LINUX__) || defined(__FREEBSD__) +#include +#endif +#include +#include + +#if !defined(__MACH__) +#define __private_extern__ +#endif + +CF_EXPORT char **_CFArgv(void); +CF_EXPORT int _CFArgc(void); + +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 CFStringRef _CFStringCreateHostName(void); + +CF_EXPORT void __CFSetNastyFile(CFTypeRef cf); + +CF_EXPORT void _CFMachPortInstallNotifyPort(CFRunLoopRef rl, CFStringRef mode); + + +#if defined(__ppc__) +#define HALT asm __volatile__("trap") +#elif defined(__i386__) +#define HALT asm __volatile__("int3") +#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 15 + +#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) +#endif + + +/* 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. */ +#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))) + +typedef struct ___CFThreadSpecificData { + void *_unused1; + void *_allocator; + void *_runLoop; + int _runLoop_pid; +// If you add things to this struct, add cleanup to __CFFinalizeThreadData() +} __CFThreadSpecificData; + +extern __CFThreadSpecificData *__CFGetThreadSpecificData(void); + +#if defined(__MACH__) || defined(__LINUX__) || defined(__FREEBSD__) +extern pthread_key_t __CFTSDKey; +#endif +#if defined(__WIN32__) +extern DWORD __CFTSDKey; +#endif + +//extern void *pthread_getspecific(pthread_key_t key); + +CF_INLINE __CFThreadSpecificData *__CFGetThreadSpecificData_inline(void) { +#if defined(__MACH__) || defined(__LINUX__) || defined(__FREEBSD__) + __CFThreadSpecificData *data = pthread_getspecific(__CFTSDKey); + return data ? data : __CFGetThreadSpecificData(); +#elif defined(__WIN32__) + __CFThreadSpecificData *data = TlsGetValue(__CFTSDKey); + return data ? data : __CFGetThreadSpecificData(); +#endif +} + +CF_EXPORT void CFLog(int p, CFStringRef str, ...); + +#define __kCFAllocatorTypeID_CONST 2 + +CF_INLINE CFAllocatorRef __CFGetDefaultAllocator(void) { + CFAllocatorRef allocator = __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 (__CFBitfieldGetValue(((const CFRuntimeBase *)cf)->_info, 7, 7)) { + 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 + +CF_EXPORT CFAllocatorRef _CFTemporaryMemoryAllocator(void); + +extern SInt64 __CFTimeIntervalToTSR(CFTimeInterval ti); +extern CFTimeInterval __CFTSRToTimeInterval(SInt64 tsr); +extern SInt64 __CFAbsoluteTimeToTSR(CFAbsoluteTime at); +extern CFAbsoluteTime __CFTSRToAbsoluteTime(SInt64 tsr); + +extern CFStringRef __CFCopyFormattingDescription(CFTypeRef cf, CFDictionaryRef formatOptions); + +/* result is long long or int, depending on doLonglong +*/ +extern Boolean __CFStringScanInteger(CFStringInlineBuffer *buf, CFDictionaryRef locale, SInt32 *indexPtr, Boolean doLonglong, void *result); +extern Boolean __CFStringScanDouble(CFStringInlineBuffer *buf, CFDictionaryRef locale, SInt32 *indexPtr, double *resultPtr); +extern Boolean __CFStringScanHex(CFStringInlineBuffer *buf, SInt32 *indexPtr, unsigned *result); + + +#define CONST_STRING_DECL(S, V) const CFStringRef S = __builtin___CFStringMakeConstantString(V); + + +#if defined(__MACH__) +#define __kCFCharacterSetDir "/System/Library/CoreServices" +#elif defined(__LINUX__) || defined(__FREEBSD__) +#define __kCFCharacterSetDir "/usr/local/share/CoreFoundation" +#elif defined(__WIN32__) +#define __kCFCharacterSetDir "\\Windows\\CoreFoundation" +#endif + + +/* Buffer size for file pathname */ +#if defined(__WIN32__) + #define CFMaxPathSize ((CFIndex)262) + #define CFMaxPathLength ((CFIndex)260) +#else + #define CFMaxPathSize ((CFIndex)1026) + #define CFMaxPathLength ((CFIndex)1024) +#endif + +#if defined(__MACH__) +extern bool __CFOASafe; +extern void __CFSetLastAllocationEventName(void *ptr, const char *classname); +#else +#define __CFOASafe 0 +#define __CFSetLastAllocationEventName(a, b) +#endif + + +CF_EXPORT CFStringRef _CFCreateLimitedUniqueString(void); + +extern CFStringRef __CFCopyEthernetAddrString(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); + +CF_EXPORT 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 *__CFTypeCollectionRetain(CFAllocatorRef allocator, const void *ptr); +extern void __CFTypeCollectionRelease(CFAllocatorRef allocator, const void *ptr); + +typedef uint32_t CFSpinLock_t; + +#if defined(__MACH__) + +// In libSystem: +extern int __is_threaded; +extern void _spin_lock(CFSpinLock_t *lockp); +extern void _spin_unlock(CFSpinLock_t *lockp); +// It would be better to use _pthread_is_threaded() instead of +// __is_threaded, but the latter is SO much faster it's hard to +// resist, and CF _is_ an internal project that can rev if needed. + +CF_INLINE void __CFSpinLock(CFSpinLock_t *lockp) { + if (__is_threaded) _spin_lock(lockp); +} + +CF_INLINE void __CFSpinUnlock(CFSpinLock_t *lockp) { + if (__is_threaded) _spin_unlock(lockp); +} + +#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(__svr4__) || defined(__hpux__) +#include +#elif defined(__WIN32__) +#elif defined(__MACH__) || defined(__LINUX__) || defined(__FREEBSD__) +#include +#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 defined(__MACH__) +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); + +#if !defined(__MACH__) + +#define CF_IS_OBJC(typeID, obj) (false) + +#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) + +#endif + +#if defined(__LINUX__) || defined(__FREEBSD__) || defined(__WIN32__) +#define __CFISAForTypeID(x) (NULL) +#endif + +#if defined(__MACH__) + +struct objc_class { // nasty, nasty + long __fields0__[2]; + const char *name; + long __fields1__[7]; +}; + +#define __CFMaxRuntimeTypes 256 + +extern struct objc_class *__CFRuntimeObjCClassTable[]; +CF_INLINE void *__CFISAForTypeID(CFTypeID typeID) { + return (void *)(__CFRuntimeObjCClassTable[typeID]); +} + +typedef void *SEL; + +extern SEL (*__CFGetObjCSelector)(const char *); +extern void * (*__CFSendObjCMsg)(const void *, SEL, ...); + +// Although it might seem to make better performance to check for NULL +// first, doing the other check first is better. +CF_INLINE int CF_IS_OBJC(CFTypeID typeID, const void *obj) { + return (((CFRuntimeBase *)obj)->_isa != __CFISAForTypeID(typeID) && ((CFRuntimeBase *)obj)->_isa > (void *)0xFFF); +} + +#define CF_OBJC_FUNCDISPATCH0(typeID, rettype, obj, sel) \ + if (CF_IS_OBJC(typeID, obj)) \ + {rettype (*func)(const void *, SEL) = (void *)__CFSendObjCMsg; \ + static SEL s = NULL; if (!s) s = __CFGetObjCSelector(sel); \ + return func((const void *)obj, s);} +#define CF_OBJC_FUNCDISPATCH1(typeID, rettype, obj, sel, a1) \ + if (CF_IS_OBJC(typeID, obj)) \ + {rettype (*func)(const void *, SEL, ...) = (void *)__CFSendObjCMsg; \ + static SEL s = NULL; if (!s) s = __CFGetObjCSelector(sel); \ + return func((const void *)obj, s, (a1));} +#define CF_OBJC_FUNCDISPATCH2(typeID, rettype, obj, sel, a1, a2) \ + if (CF_IS_OBJC(typeID, obj)) \ + {rettype (*func)(const void *, SEL, ...) = (void *)__CFSendObjCMsg; \ + static SEL s = NULL; if (!s) s = __CFGetObjCSelector(sel); \ + return func((const void *)obj, s, (a1), (a2));} +#define CF_OBJC_FUNCDISPATCH3(typeID, rettype, obj, sel, a1, a2, a3) \ + if (CF_IS_OBJC(typeID, obj)) \ + {rettype (*func)(const void *, SEL, ...) = (void *)__CFSendObjCMsg; \ + static SEL s = NULL; if (!s) s = __CFGetObjCSelector(sel); \ + return func((const void *)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)(const void *, SEL, ...) = (void *)__CFSendObjCMsg; \ + static SEL s = NULL; if (!s) s = __CFGetObjCSelector(sel); \ + return func((const void *)obj, s, (a1), (a2), (a3), (a4));} + +#endif + +/* See comments in CFBase.c +*/ +#if defined(__ppc__) && defined(__MACH__) +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) +#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) +#endif + + +__private_extern__ CFArrayRef _CFBundleCopyUserLanguages(Boolean useBackstops); + +#endif /* ! __COREFOUNDATION_CFINTERNAL__ */ + diff --git a/Base.subproj/CFPlatform.c b/Base.subproj/CFPlatform.c new file mode 100644 index 0000000..8ab9956 --- /dev/null +++ b/Base.subproj/CFPlatform.c @@ -0,0 +1,461 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFPlatform.c + Copyright 1999-2002, Apple, Inc. All rights reserved. + Responsibility: Christopher Kane +*/ + +#include "CFInternal.h" +#include "CFPriv.h" +#if defined(__WIN32__) + #include + #include + #include + #include +#else + #include + #include + #include + #include +#endif +#if defined(__MACH__) + #include + #include +#endif + +extern char *getenv(const char *name); + +#if defined(__MACH__) +#define kCFPlatformInterfaceStringEncoding kCFStringEncodingUTF8 +#else +#define kCFPlatformInterfaceStringEncoding CFStringGetSystemEncoding() +#endif + +char **_CFArgv(void) { +#if defined(__MACH__) + return *_NSGetArgv(); +#else + return NULL; +#endif +} + +int _CFArgc(void) { +#if defined(__MACH__) + return *_NSGetArgc(); +#else + return 0; +#endif +} + + +__private_extern__ Boolean _CFGetCurrentDirectory(char *path, int maxlen) { +#if defined(__WIN32__) + DWORD len = GetCurrentDirectoryA(maxlen, path); + return (0 != len && len + 1 <= 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 defined(__WIN32__) +#define PATH_LIST_SEP ';' +#else +#define PATH_LIST_SEP ':' +#endif + +static char *_CFSearchForNameInPath(CFAllocatorRef alloc, const char *name, char *path) { + struct stat statbuf; + char *nname = CFAllocatorAllocate(alloc, strlen(name) + strlen(path) + 2, 0); + if (__CFOASafe) __CFSetLastAllocationEventName(nname, "CFUtilities (temp)"); + for (;;) { + char *p = (char *)strchr(path, PATH_LIST_SEP); + if (NULL != p) { + *p = '\0'; + } + nname[0] = '\0'; + strcat(nname, path); + strcat(nname, "/"); + strcat(nname, name); + // 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; + } + return nname; + } + if (NULL == p) { + break; + } + *p = PATH_LIST_SEP; + path = p + 1; + } + CFAllocatorDeallocate(alloc, nname); + return NULL; +} + + + +static const char *__CFProcessPath = NULL; +static const char *__CFprogname = ""; + +const char **_CFGetProgname(void) { // This is a hack around the broken _NSGetPrognam(), for now; will be removed + return &__CFprogname; +} + +const char *_CFProcessPath(void) { + CFAllocatorRef alloc = NULL; + char *thePath = NULL; + int execIndex = 0; + + if (__CFProcessPath) return __CFProcessPath; + if (!__CFProcessPath) { + thePath = getenv("CFProcessPath"); + + alloc = CFRetain(__CFGetDefaultAllocator()); + + if (thePath) { + int len = strlen(thePath); + __CFProcessPath = CFAllocatorAllocate(alloc, len+1, 0); + if (__CFOASafe) __CFSetLastAllocationEventName((void *)__CFProcessPath, "CFUtilities (process-path)"); + memmove((char *)__CFProcessPath, thePath, len + 1); + } + } + +#if defined(__MACH__) + { + struct stat exec, lcfm; + unsigned long size = CFMaxPathSize; + char buffer[CFMaxPathSize]; + if (0 == _NSGetExecutablePath(buffer, &size) && + strcasestr(buffer, "LaunchCFMApp") != NULL && + 0 == stat("/System/Library/Frameworks/Carbon.framework/Versions/Current/Support/LaunchCFMApp", &lcfm) && + 0 == stat(buffer, &exec) && + (lcfm.st_dev == exec.st_dev) && + (lcfm.st_ino == exec.st_ino)) { + // Executable is LaunchCFMApp, take special action + execIndex = 1; + __CFIsCFM = true; + } + } +#endif + + if (!__CFProcessPath && NULL != (*_NSGetArgv())[execIndex]) { + char buf[CFMaxPathSize] = {0}; +#if defined(__WIN32__) + HINSTANCE hinst = GetModuleHandle(NULL); + DWORD rlen = hinst ? GetModuleFileName(hinst, buf, 1028) : 0; + thePath = rlen ? buf : NULL; +#else + 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(alloc, arg0, theList); + if (thePath) { + // User could have "." or "../bin" or other relative path in $PATH + if (('/' != thePath[0]) && _CFGetCurrentDirectory(buf, CFMaxPathSize)) { + strcat(buf, "/"); + strcat(buf, thePath); + if (0 == stat(buf, &statbuf)) { + CFAllocatorDeallocate(alloc, (void *)thePath); + thePath = buf; + } + } + if (thePath != buf) { + strcpy(buf, thePath); + CFAllocatorDeallocate(alloc, (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] != '/') { + strcat(buf, "/"); + } + strcat(buf, arg0); + if (0 == stat(buf, &statbuf)) { + thePath = buf; + } + } + + if (thePath) { + // We are going to process the buffer replacing all "/./" with "/" + CFIndex srcIndex = 0, dstIndex = 0; + CFIndex len = strlen(thePath); + for (srcIndex=0; srcIndexpw_dir) { + home = CFURLCreateFromFileSystemRepresentation(NULL, 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(NULL, 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(NULL, upwd->pw_name, kCFPlatformInterfaceStringEncoding); + } else { + const char *cuser = getenv("USER"); + if (cuser) + __CFUserName = CFStringCreateWithCString(NULL, cuser, kCFPlatformInterfaceStringEncoding); + } +} +#endif + +static CFURLRef _CFCreateHomeDirectoryURLForUser(CFStringRef uName) { +#if defined(__MACH__) || defined(__svr4__) || defined(__hpux__) || defined(__LINUX__) || defined(__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 = CFAllocatorAllocate(kCFAllocatorDefault, size+1, 0); + if (__CFOASafe) __CFSetLastAllocationEventName(user, "CFUtilities (temp)"); + } + if (CFStringGetBytes(uName, CFRangeMake(0, len), kCFPlatformInterfaceStringEncoding, 0, true, user, size, &usedSize) == len) { + user[usedSize] = '\0'; + upwd = getpwnam(user); + } + if (buf != user) { + CFAllocatorDeallocate(kCFAllocatorDefault, user); + } + return _CFCopyHomeDirURLForUser(upwd); + } +#elif defined(__WIN32__) +#warning CF: Windows home directory goop disabled + return NULL; +#if 0 + CFString *user = !uName ? CFUserName() : uName; + + if (!uName || CFEqual(user, CFUserName())) { + const char *cpath = getenv("HOMEPATH"); + const char *cdrive = getenv("HOMEDRIVE"); + if (cdrive && cpath) { + char fullPath[CFMaxPathSize]; + CFStringRef str; + strcpy(fullPath, cdrive); + strncat(fullPath, cpath, CFMaxPathSize-strlen(cdrive)-1); + str = CFStringCreateWithCString(NULL, fullPath, kCFPlatformInterfaceStringEncoding); + home = CFURLCreateWithFileSystemPath(NULL, str, kCFURLWindowsPathStyle, true); + CFRelease(str); + } + } + if (!home) { + struct _USER_INFO_2 *userInfo; + HINSTANCE hinstDll = GetModuleHandleA("NETAPI32"); + if (!hinstDll) + hinstDll = LoadLibraryEx("NETAPI32", NULL, 0); + if (hinstDll) { + FARPROC lpfn = GetProcAddress(hinstDll, "NetUserGetInfo"); + if (lpfn) { + unsigned namelen = CFStringGetLength(user); + UniChar *username; + username = CFAllocatorAllocate(kCFAllocatorDefault, sizeof(UniChar) * (namelen + 1), 0); + if (__CFOASafe) __CFSetLastAllocationEventName(username, "CFUtilities (temp)"); + CFStringGetCharacters(user, CFRangeMake(0, namelen), username); + if (!(*lpfn)(NULL, (LPWSTR)username, 2, (LPBYTE *)&userInfo)) { + UInt32 len = 0; + CFMutableStringRef str; + while (userInfo->usri2_home_dir[len] != 0) len ++; + str = CFStringCreateMutable(NULL, len+1); + CFStringAppendCharacters(str, userInfo->usri2_home_dir, len); + home = CFURLCreateWithFileSystemPath(NULL, str, kCFURLWindowsPathStyle, true); + CFRelease(str); + } + CFAllocatorDeallocate(kCFAllocatorDefault, username); + } + } else { + } + } + // 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 (CFStringGetLength(CFURLGetPath(home)) == 0) { + CFRelease(home); + home=NULL; + } +#endif + +#else +#error Dont know how to compute users home directories on this platform +#endif +} + +static CFStringRef _CFUserName(void) { +#if defined(__MACH__) || defined(__svr4__) || defined(__hpux__) || defined(__LINUX__) || defined(__FREEBSD__) + if (geteuid() != __CFEUID || getuid() != __CFUID) + _CFUpdateUserInfo(); +#elif defined(__WIN32__) + if (!__CFUserName) { + char username[1040]; + DWORD size = 1040; + username[0] = 0; + if (GetUserNameA(username, &size)) { + __CFUserName = CFStringCreateWithCString(NULL, username, kCFPlatformInterfaceStringEncoding); + } else { + const char *cname = getenv("USERNAME"); + if (cname) + __CFUserName = CFStringCreateWithCString(NULL, cname, kCFPlatformInterfaceStringEncoding); + } + } +#else +#error Dont know how to compute user name on this platform +#endif + if (!__CFUserName) + __CFUserName = CFRetain(CFSTR("")); + return __CFUserName; +} + +__private_extern__ CFStringRef _CFGetUserName(void) { + return CFStringCreateCopy(NULL, _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(NULL, 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 PATH_LIST_SEP +#undef CFMaxHostNameLength +#undef CFMaxHostNameSize + diff --git a/Base.subproj/CFPriv.h b/Base.subproj/CFPriv.h new file mode 100644 index 0000000..f51ff10 --- /dev/null +++ b/Base.subproj/CFPriv.h @@ -0,0 +1,282 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFPriv.h + Copyright (c) 1998-2003, Apple, Inc. All rights reserved. +*/ + +/* + APPLE SPI: NOT TO BE USED OUTSIDE APPLE! +*/ + +#if !defined(__COREFOUNDATION_CFPRIV__) +#define __COREFOUNDATION_CFPRIV__ 1 + +#include +#include +#include +#include +#include +#if defined(__MACH__) +#include +#include +#include +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +CF_EXPORT intptr_t _CFDoOperation(intptr_t code, intptr_t subcode1, intptr_t subcode2); + +CF_EXPORT void _CFRuntimeSetCFMPresent(int a); + +CF_EXPORT const char *_CFProcessPath(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); + +CF_EXPORT CFIndex CFMachPortGetQueuedMessageCount(CFMachPortRef mp); + +CF_EXPORT CFPropertyListRef _CFURLCopyPropertyListRepresentation(CFURLRef url); +CF_EXPORT CFURLRef _CFURLCreateFromPropertyListRepresentation(CFAllocatorRef alloc, CFPropertyListRef pListRepresentation); +#endif /* __MACH__ */ + + +#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 + +#if MAC_OS_X_VERSION_10_2 <= MAC_OS_X_VERSION_MAX_ALLOWED +typedef enum { + kCFURLComponentDecompositionNonHierarchical, + kCFURLComponentDecompositionRFC1808, /* use this for RFC 1738 decompositions as well */ + kCFURLComponentDecompositionRFC2396 +} 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 */ + +/* + 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. +*/ +typedef 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) */ +} CFSearchPathDirectory; + +typedef 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 */ +} 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 - the results of these calls are cached, and should be fast after the first call */ +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" + +typedef enum { + kCFStringGramphemeCluster = 1, /* Unicode Grapheme Cluster (not different from kCFStringComposedCharacterCluster right now) */ + kCFStringComposedCharacterCluster = 2, /* Compose all non-base (including spacing marks) */ + kCFStringCursorMovementCluster = 3, /* Cluster suitable for cursor movements */ + kCFStringBackwardDeletionCluster = 4 /* Cluster suitable for backward deletion */ +} CFStringCharacterClusterType; + +CF_EXPORT CFRange CFStringGetRangeOfCharacterClusterAtIndex(CFStringRef string, CFIndex charIndex, CFStringCharacterClusterType type); + + +/* 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)) { + int 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 { + int 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 */ + +#if defined(__cplusplus) +} +#endif + +#endif /* ! __COREFOUNDATION_CFPRIV__ */ + diff --git a/Base.subproj/CFRuntime.c b/Base.subproj/CFRuntime.c new file mode 100644 index 0000000..55a9676 --- /dev/null +++ b/Base.subproj/CFRuntime.c @@ -0,0 +1,953 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFRuntime.c + Copyright 1999-2002, Apple, Inc. All rights reserved. + Responsibility: Christopher Kane +*/ + +#include "CFRuntime.h" +#include "CFInternal.h" +#include +#include +#if defined(__MACH__) +#include +#include +#include +#else +#define __is_threaded (1) +#endif + +extern size_t malloc_size(const void *ptr); +extern int __setonlyClocaleconv(int val); + +#if defined(__MACH__) +extern void __CFRecordAllocationEvent(int eventnum, void *ptr, int size, int data, const char *classname); +#else +#define __CFRecordAllocationEvent(a, b, c, d, e) +#endif + +enum { +// retain/release recording constants -- must match values +// used by OA for now; probably will change in the future +__kCFRetainEvent = 28, +__kCFReleaseEvent = 29 +}; + +/* On Win32 we should use _msize instead of malloc_size + * (Aleksey Dukhnyakov) + */ +#if defined(__WIN32__) +#include +CF_INLINE size_t malloc_size(void *memblock) { + return _msize(memblock); +} +#endif + +#if defined(__MACH__) + +bool __CFOASafe = false; + +void __CFOAInitialize(void) { + static void (*dyfunc)(void) = (void *)0xFFFFFFFF; + if (NULL == getenv("OAKeepAllocationStatistics")) return; + if ((void *)0xFFFFFFFF == dyfunc) { + dyfunc = NULL; + if (NSIsSymbolNameDefined("__OAInitialize")) + dyfunc = (void *)NSAddressOfSymbol(NSLookupAndBindSymbol("__OAInitialize")); + } + if (NULL != dyfunc) { + dyfunc(); + __CFOASafe = true; + } +} + +void __CFRecordAllocationEvent(int eventnum, void *ptr, int size, int data, const char *classname) { + static void (*dyfunc)(int, void *, int, int, const char *) = (void *)0xFFFFFFFF; + if (!__CFOASafe) return; + if ((void *)0xFFFFFFFF == dyfunc) { + dyfunc = NULL; + if (NSIsSymbolNameDefined("__OARecordAllocationEvent")) + dyfunc = (void *)NSAddressOfSymbol(NSLookupAndBindSymbol("__OARecordAllocationEvent")); + } + if (NULL != dyfunc) { + dyfunc(eventnum, ptr, size, data, classname); + } +} + +void __CFSetLastAllocationEventName(void *ptr, const char *classname) { + static void (*dyfunc)(void *, const char *) = (void *)0xFFFFFFFF; + if (!__CFOASafe) return; + if ((void *)0xFFFFFFFF == dyfunc) { + dyfunc = NULL; + if (NSIsSymbolNameDefined("__OASetLastAllocationEventName")) + dyfunc = (void *)NSAddressOfSymbol(NSLookupAndBindSymbol("__OASetLastAllocationEventName")); + } + if (NULL != dyfunc) { + dyfunc(ptr, classname); + } +} +#endif + +extern void __HALT(void); + +static CFTypeID __kCFNotATypeTypeID = _kCFRuntimeNotATypeID; + +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 +}; + +/* bits 15-8 in the CFRuntimeBase _info are type */ +/* bits 7-0 in the CFRuntimeBase are reserved for CF's use */ + +static CFRuntimeClass * __CFRuntimeClassTable[__CFMaxRuntimeTypes] = {NULL}; +static int32_t __CFRuntimeClassTableCount = 0; + +#if defined(__MACH__) + +__private_extern__ SEL (*__CFGetObjCSelector)(const char *) = NULL; +__private_extern__ void * (*__CFSendObjCMsg)(const void *, SEL, ...) = NULL; + +__private_extern__ struct objc_class *__CFRuntimeObjCClassTable[__CFMaxRuntimeTypes] = {NULL}; + +#endif + +// Compiler uses this symbol name +int __CFConstantStringClassReference[10] = {0}; + +#if defined(__MACH__) +static struct objc_class __CFNSTypeClass = {{0, 0}, NULL, {0, 0, 0, 0, 0, 0, 0}}; +#else +static void *__CFNSTypeClass[10] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; +#endif + +//static CFSpinLock_t __CFRuntimeLock = 0; + +CFTypeID _CFRuntimeRegisterClass(const CFRuntimeClass * const cls) { +// version field must be 0 +// className must be pure ASCII string, non-null + if (__CFMaxRuntimeTypes <= __CFRuntimeClassTableCount) { + CFLog(0, CFSTR("*** CoreFoundation class table full; registration failing for class '%s'. Program will crash soon."), cls->className); + return _kCFRuntimeNotATypeID; + } + __CFRuntimeClassTable[__CFRuntimeClassTableCount++] = (CFRuntimeClass *)cls; + return __CFRuntimeClassTableCount - 1; +} + +const CFRuntimeClass * _CFRuntimeGetClassWithTypeID(CFTypeID typeID) { + return __CFRuntimeClassTable[typeID]; +} + +void _CFRuntimeUnregisterClassWithTypeID(CFTypeID typeID) { + __CFRuntimeClassTable[typeID] = NULL; +} + + +#if defined(DEBUG) + +/* 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) + * bit 16: scribble allocated CF object memory + * bit 23: use 1st-order byte as scribble byte for alloc (otherwise 0xCF) + */ + +static uint32_t __CFZombieLevel = 0x0; + +static void __CFZombifyAllocatedMemory(void *cf) { + if (__CFZombieLevel & (1 << 16)) { + void *ptr = cf; + size_t size = malloc_size(cf); + uint8_t byte = 0xCF; + if (__CFZombieLevel & (1 << 23)) { + byte = (__CFZombieLevel >> 24) & 0xFF; + } + memset(ptr, byte, size); + } +} + +static void __CFZombifyDeallocatedMemory(void *cf) { + if (__CFZombieLevel & (1 << 0)) { + void *ptr = cf; + size_t size = malloc_size(cf); + uint8_t byte = 0xFC; + if (__CFZombieLevel & (1 << 1)) { + ptr += sizeof(CFRuntimeBase); + size -= sizeof(CFRuntimeBase); + } + if (__CFZombieLevel & (1 << 7)) { + byte = (__CFZombieLevel >> 8) & 0xFF; + } + memset(ptr, byte, size); + } +} + +#endif /* DEBUG */ + +CFTypeRef _CFRuntimeCreateInstance(CFAllocatorRef allocator, CFTypeID typeID, uint32_t extraBytes, unsigned char *category) { + CFRuntimeBase *memory; + Boolean usesSystemDefaultAllocator; + int32_t size; + + if (NULL == __CFRuntimeClassTable[typeID]) { + return NULL; + } + allocator = (NULL == allocator) ? __CFGetDefaultAllocator() : allocator; + usesSystemDefaultAllocator = (allocator == kCFAllocatorSystemDefault); + extraBytes = (extraBytes + (sizeof(void *) - 1)) & ~(sizeof(void *) - 1); + size = sizeof(CFRuntimeBase) + extraBytes + (usesSystemDefaultAllocator ? 0 : sizeof(CFAllocatorRef)); + memory = CFAllocatorAllocate(allocator, size, 0); + if (NULL == memory) { + return NULL; + } +#if defined(DEBUG) + __CFZombifyAllocatedMemory((void *)memory); +#endif + if (__CFOASafe && category) { + __CFSetLastAllocationEventName(memory, category); + } else if (__CFOASafe) { + __CFSetLastAllocationEventName(memory, __CFRuntimeClassTable[typeID]->className); + } + if (!usesSystemDefaultAllocator) { + *(CFAllocatorRef *)((char *)memory) = CFRetain(allocator); + memory = (CFRuntimeBase *)((char *)memory + sizeof(CFAllocatorRef)); + } + memory->_isa = __CFISAForTypeID(typeID); + memory->_rc = 1; + memory->_info = 0; + __CFBitfieldSetValue(memory->_info, 15, 8, typeID); + if (usesSystemDefaultAllocator) { + __CFBitfieldSetValue(memory->_info, 7, 7, 1); + } + if (NULL != __CFRuntimeClassTable[typeID]->init) { + (__CFRuntimeClassTable[typeID]->init)(memory); + } + return memory; +} + +void _CFRuntimeSetInstanceTypeID(CFTypeRef cf, CFTypeID typeID) { + __CFBitfieldSetValue(((CFRuntimeBase *)cf)->_info, 15, 8, typeID); +} + +CFTypeID __CFGenericTypeID(const void *cf) { + return __CFBitfieldGetValue(((const CFRuntimeBase *)cf)->_info, 15, 8); +} + +CF_INLINE CFTypeID __CFGenericTypeID_inline(const void *cf) { + return __CFBitfieldGetValue(((const CFRuntimeBase *)cf)->_info, 15, 8); +} + +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 0x%x is not a CF object", func, cf); \ + CFAssert3(__CFGenericTypeID_inline(cf) == type, __kCFLogAssertion, "%s(): pointer 0x%x 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 0x%x is not a CF object", __PRETTY_FUNCTION__, cf); + +#if !defined(__MACH__) + +#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) + +#endif + +#if defined(__MACH__) + +CF_INLINE int CFTYPE_IS_OBJC(const void *obj) { + CFTypeID typeID = __CFGenericTypeID_inline(obj); + return CF_IS_OBJC(typeID, obj) && __CFSendObjCMsg; +} + +#define CFTYPE_OBJC_FUNCDISPATCH0(rettype, obj, sel) \ + if (CFTYPE_IS_OBJC(obj)) \ + {rettype (*func)(void *, SEL) = (void *)__CFSendObjCMsg; \ + static SEL s = NULL; if (!s) s = __CFGetObjCSelector(sel); \ + return func((void *)obj, s);} +#define CFTYPE_OBJC_FUNCDISPATCH1(rettype, obj, sel, a1) \ + if (CFTYPE_IS_OBJC(obj)) \ + {rettype (*func)(void *, SEL, ...) = (void *)__CFSendObjCMsg; \ + static SEL s = NULL; if (!s) s = __CFGetObjCSelector(sel); \ + return func((void *)obj, s, (a1));} + +#endif + +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(kCFAllocatorDefault, __CFRuntimeClassTable[type]->className, kCFStringEncodingASCII); +} + +static CFSpinLock_t __CFGlobalRetainLock = 0; +static CFMutableDictionaryRef __CFRuntimeExternRefCountTable = NULL; + +#define DISGUISE(object) ((void *)(((unsigned)object) + 1)) +#define UNDISGUISE(disguised) ((id)(((unsigned)disguised) - 1)) + +extern void _CFDictionaryIncrementValue(CFMutableDictionaryRef dict, const void *key); +extern int _CFDictionaryDecrementValue(CFMutableDictionaryRef dict, const void *key); + +// Bit 31 (highest bit) in second word of cf instance indicates external ref count + +CFTypeRef CFRetain(CFTypeRef cf) { + CFIndex lowBits = 0; + bool is_threaded = __is_threaded; +#if defined(DEBUG) + if (NULL == cf) HALT; +#endif + CFTYPE_OBJC_FUNCDISPATCH0(CFTypeRef, cf, "retain"); + __CFGenericAssertIsCF(cf); + if (is_threaded) __CFSpinLock(&__CFGlobalRetainLock); + lowBits = ((CFRuntimeBase *)cf)->_rc; + if (0 == lowBits) { // Constant CFTypeRef + if (is_threaded) __CFSpinUnlock(&__CFGlobalRetainLock); + return cf; + } + lowBits++; + if ((lowBits & 0x07fff) == 0) { + // Roll over another bit to the external ref count + _CFDictionaryIncrementValue(__CFRuntimeExternRefCountTable, DISGUISE(cf)); + lowBits = 0x8000; // Bit 16 indicates external ref count + } + ((CFRuntimeBase *)cf)->_rc = lowBits; + if (is_threaded) __CFSpinUnlock(&__CFGlobalRetainLock); + if (__CFOASafe) { + uint64_t compositeRC; + compositeRC = (lowBits & 0x7fff) + ((uint64_t)(uintptr_t)CFDictionaryGetValue(__CFRuntimeExternRefCountTable, DISGUISE(cf)) << 15); + if (compositeRC > (uint64_t)0x7fffffff) compositeRC = (uint64_t)0x7fffffff; + __CFRecordAllocationEvent(__kCFRetainEvent, (void *)cf, 0, compositeRC, NULL); + } + return cf; +} + +__private_extern__ void __CFAllocatorDeallocate(CFTypeRef cf); + +void CFRelease(CFTypeRef cf) { + CFIndex lowBits = 0; + bool is_threaded = __is_threaded; +#if defined(DEBUG) + if (NULL == cf) HALT; +#endif + CFTYPE_OBJC_FUNCDISPATCH0(void, cf, "release"); + __CFGenericAssertIsCF(cf); + if (is_threaded) __CFSpinLock(&__CFGlobalRetainLock); + lowBits = ((CFRuntimeBase *)cf)->_rc; + if (0 == lowBits) { // Constant CFTypeRef + if (is_threaded) __CFSpinUnlock(&__CFGlobalRetainLock); + return; + } + if (1 == lowBits) { + if (is_threaded) __CFSpinUnlock(&__CFGlobalRetainLock); + if (__CFOASafe) __CFRecordAllocationEvent(__kCFReleaseEvent, (void *)cf, 0, 0, NULL); + if (__kCFAllocatorTypeID_CONST == __CFGenericTypeID_inline(cf)) { +#if defined(DEBUG) + __CFZombifyDeallocatedMemory((void *)cf); + if (!(__CFZombieLevel & (1 << 4))) { + __CFAllocatorDeallocate((void *)cf); + } +#else + __CFAllocatorDeallocate((void *)cf); +#endif + } else { + CFAllocatorRef allocator; + if (NULL != __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)]->finalize) { + __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)]->finalize(cf); + } + if (__CFBitfieldGetValue(((const CFRuntimeBase *)cf)->_info, 7, 7)) { + allocator = kCFAllocatorSystemDefault; + } else { + allocator = CFGetAllocator(cf); + (intptr_t)cf -= sizeof(CFAllocatorRef); + } +#if defined(DEBUG) + __CFZombifyDeallocatedMemory((void *)cf); + if (!(__CFZombieLevel & (1 << 4))) { + CFAllocatorDeallocate(allocator, (void *)cf); + } +#else + CFAllocatorDeallocate(allocator, (void *)cf); +#endif + if (kCFAllocatorSystemDefault != allocator) { + CFRelease(allocator); + } + } + } else { + if (0x8000 == lowBits) { + // Time to remove a bit from the external ref count + if (0 == _CFDictionaryDecrementValue(__CFRuntimeExternRefCountTable, DISGUISE(cf))) { + lowBits = 0x07fff; + } else { + lowBits = 0x0ffff; + } + } else { + lowBits--; + } + ((CFRuntimeBase *)cf)->_rc = lowBits; + if (is_threaded) __CFSpinUnlock(&__CFGlobalRetainLock); + if (__CFOASafe) { + uint64_t compositeRC; + compositeRC = (lowBits & 0x7fff) + ((uint64_t)(uintptr_t)CFDictionaryGetValue(__CFRuntimeExternRefCountTable, DISGUISE(cf)) << 15); + if (compositeRC > (uint64_t)0x7fffffff) compositeRC = (uint64_t)0x7fffffff; + __CFRecordAllocationEvent(__kCFReleaseEvent, (void *)cf, 0, compositeRC, NULL); + } + } +} + +static uint64_t __CFGetFullRetainCount(CFTypeRef cf) { + uint32_t lowBits = 0; + uint64_t highBits = 0, compositeRC; + lowBits = ((CFRuntimeBase *)cf)->_rc; + if (0 == lowBits) { + return (uint64_t)0x00FFFFFFFFFFFFFFULL; + } + if ((lowBits & 0x08000) != 0) { + highBits = (uint64_t)(uintptr_t)CFDictionaryGetValue(__CFRuntimeExternRefCountTable, DISGUISE(cf)); + } + compositeRC = (lowBits & 0x7fff) + (highBits << 15); + return compositeRC; +} + +CFIndex CFGetRetainCount(CFTypeRef cf) { + uint64_t rc; + CFIndex result; +#if defined(DEBUG) + if (NULL == cf) HALT; +#endif + CFTYPE_OBJC_FUNCDISPATCH0(CFIndex, cf, "retainCount"); + __CFGenericAssertIsCF(cf); + rc = __CFGetFullRetainCount(cf); + result = (rc < (uint64_t)0x7FFFFFFF) ? (CFIndex)rc : (CFIndex)0x7FFFFFFF; + return result; +} + +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) { +#if defined(DEBUG) + if (NULL == cf) HALT; +#endif + CFTYPE_OBJC_FUNCDISPATCH0(CFHashCode, cf, "hash"); + __CFGenericAssertIsCF(cf); + if (NULL != __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)]->hash) { + return __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)]->hash(cf); + } + return (CFHashCode)cf; +} + +// definition: produces a normally non-NULL debugging description of the object +CFStringRef CFCopyDescription(CFTypeRef cf) { +#if defined(DEBUG) + if (NULL == cf) HALT; +#endif + CFTYPE_OBJC_FUNCDISPATCH0(CFStringRef, cf, "_copyDescription"); + __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(kCFAllocatorDefault, 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 +#if defined(__MACH__) + if (CFTYPE_IS_OBJC(cf)) { + static SEL s = NULL, r = NULL; + CFStringRef (*func)(void *, SEL, ...) = (void *)__CFSendObjCMsg; + if (!s) s = __CFGetObjCSelector("_copyFormattingDescription:"); + if (!r) r = __CFGetObjCSelector("respondsToSelector:"); + if (s && func((void *)cf, r, s)) return func((void *)cf, s, formatOptions); + return NULL; + } +#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 defined(DEBUG) + if (NULL == cf) HALT; +#endif +// CF: need to get allocator from objc objects in better way...how? +// -> bridging of CFAllocators and malloc_zone_t will help this + if (CFTYPE_IS_OBJC(cf)) return __CFGetDefaultAllocator(); + 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 __CFDataInitialize(void); +extern void __CFDateInitialize(void); +extern void __CFDictionaryInitialize(void); +extern void __CFNumberInitialize(void); +extern void __CFSetInitialize(void); +extern void __CFStorageInitialize(void); +extern void __CFTimeZoneInitialize(void); +extern void __CFTreeInitialize(void); +extern void __CFURLInitialize(void); +extern void __CFXMLNodeInitialize(void); +extern void __CFXMLParserInitialize(void); +#if defined(__MACH__) +extern void __CFMessagePortInitialize(void); +extern void __CFMachPortInitialize(void); +#endif +#if defined(__MACH__) || defined(__WIN32__) +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 defined(DEBUG) +#define DO_SYSCALL_TRACE_HELPERS 1 +#endif +#if defined(DO_SYSCALL_TRACE_HELPERS) && defined(__MACH__) +extern void ptrace(int, int, int, int); +#define SYSCALL_TRACE(N) do ptrace(N, 0, 0, 0); while (0) +#else +#define SYSCALL_TRACE(N) do {} while (0) +#endif + +#if defined(__MACH__) && defined(PROFILE) +static void _CF_mcleanup(void) { + monitor(0,0,0,0,0); +} +#endif + +extern CFTypeID CFTimeZoneGetTypeID(void); +extern CFTypeID CFNumberGetTypeID(void); +extern CFTypeID CFBooleanGetTypeID(void); + +const void *__CFArgStuff = NULL; +__private_extern__ void *__CFAppleLanguages = NULL; +__private_extern__ void *__CFSessionID = NULL; + +#if defined(__LINUX__) || defined(__FREEBSD__) +static void __CFInitialize(void) __attribute__ ((constructor)); +static +#endif +#if defined(__WIN32__) +CF_EXPORT +#endif +void __CFInitialize(void) { + static int __done = 0; + if (sizeof(int) != sizeof(long) || 4 != sizeof(long)) __HALT(); + if (!__done) { + CFIndex idx, cnt; + + __done = 1; + SYSCALL_TRACE(0xC000); + + __setonlyClocaleconv(1); +#if defined(DEBUG) + { + const char *value = getenv("CFZombieLevel"); + if (NULL != value) { + __CFZombieLevel = strtoul(value, NULL, 0); + } + if (0x0 == __CFZombieLevel) __CFZombieLevel = 0xCF00FC00; // default + } +#endif +#if defined(__MACH__) && defined(PROFILE) + { + const char *v = getenv("DYLD_IMAGE_SUFFIX"); + const char *p = getenv("CFPROF_ENABLE"); + // ckane: People were upset that I added this feature to allow for the profiling of + // libraries using unprofiled apps/executables, so ensure they cannot get this accidentally. + if (v && p && 0 == strcmp("_profile", v) && 0 == strcmp(crypt(p + 2, p) + 2, "eQJhkVvMm.w")) { + // Unfortunately, no way to know if this has already been done, + // or I would not do it. Not much information will be lost. + atexit(_CF_mcleanup); + moninit(); + } + } +#endif + + __CFBaseInitialize(); + +#if defined(__MACH__) + for (idx = 0; idx < __CFMaxRuntimeTypes; idx++) { + __CFRuntimeObjCClassTable[idx] = &__CFNSTypeClass; + } +#endif + + /* Here so that two runtime classes get indices 0, 1. */ + __kCFNotATypeTypeID = _CFRuntimeRegisterClass(&__CFNotATypeClass); + __kCFTypeTypeID = _CFRuntimeRegisterClass(&__CFTypeClass); + + /* Here so that __kCFAllocatorTypeID gets index 2. */ + __CFAllocatorInitialize(); + + /* Basic collections need to be up before CFString. */ + __CFDictionaryInitialize(); + __CFArrayInitialize(); + __CFDataInitialize(); + __CFSetInitialize(); + +#if defined(__MACH__) + { + char **args = *_NSGetArgv(); + cnt = *_NSGetArgc(); + for (idx = 1; idx < cnt - 1; idx++) { + if (0 == strcmp(args[idx], "-AppleLanguages")) { + CFIndex length = strlen(args[idx + 1]); + __CFAppleLanguages = malloc(length + 1); + memmove(__CFAppleLanguages, args[idx + 1], length + 1); + break; + } + } + } +#endif + +#if defined(__MACH__) + // Pre-initialize for possible future toll-free bridging + // These have to be initialized before the *Initialize functions below are called + __CFRuntimeObjCClassTable[CFDictionaryGetTypeID()] = (struct objc_class *)calloc(sizeof(struct objc_class), 1); + __CFRuntimeObjCClassTable[CFArrayGetTypeID()] = (struct objc_class *)calloc(sizeof(struct objc_class), 1); + __CFRuntimeObjCClassTable[CFDataGetTypeID()] = (struct objc_class *)calloc(sizeof(struct objc_class), 1); + __CFRuntimeObjCClassTable[CFSetGetTypeID()] = (struct objc_class *)calloc(sizeof(struct objc_class), 1); + __CFRuntimeObjCClassTable[0x7] = (struct objc_class *)&__CFConstantStringClassReference; + __CFRuntimeObjCClassTable[0x8] = (struct objc_class *)calloc(sizeof(struct objc_class), 1); + __CFRuntimeObjCClassTable[0x9] = (struct objc_class *)calloc(sizeof(struct objc_class), 1); + __CFRuntimeObjCClassTable[0xa] = (struct objc_class *)calloc(sizeof(struct objc_class), 1); + __CFRuntimeObjCClassTable[0xb] = (struct objc_class *)calloc(sizeof(struct objc_class), 1); + __CFRuntimeObjCClassTable[0xc] = (struct objc_class *)calloc(sizeof(struct objc_class), 1); +#endif + + // Creating this lazily in CFRetain causes recursive call to CFRetain + __CFRuntimeExternRefCountTable = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, NULL); + + /*** _CFRuntimeCreateInstance() can finally be called generally after this line. ***/ + + __CFStringInitialize(); // CFString's TypeID must be 0x7, now and forever + __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(); // See above for hard-coding of this position + __CFTimeZoneInitialize(); // See above for hard-coding of this position + + __CFBinaryHeapInitialize(); + __CFBitVectorInitialize(); + __CFBagInitialize(); + __CFCharacterSetInitialize(); + __CFStorageInitialize(); + __CFTreeInitialize(); + __CFURLInitialize(); + __CFXMLNodeInitialize(); + __CFXMLParserInitialize(); + __CFBundleInitialize(); + __CFPlugInInitialize(); + __CFPlugInInstanceInitialize(); + __CFUUIDInitialize(); +#if defined(__MACH__) + __CFMessagePortInitialize(); + __CFMachPortInitialize(); +#endif +#if defined(__MACH__) || defined(__WIN32__) + __CFRunLoopInitialize(); + __CFRunLoopObserverInitialize(); + __CFRunLoopSourceInitialize(); + __CFRunLoopTimerInitialize(); + __CFSocketInitialize(); +#endif + +#if defined(__MACH__) + // Pre-initialize for possible future toll-free bridging, continued + __CFRuntimeObjCClassTable[CFRunLoopTimerGetTypeID()] = (struct objc_class *)calloc(sizeof(struct objc_class), 1); + __CFRuntimeObjCClassTable[CFMachPortGetTypeID()] = (struct objc_class *)calloc(sizeof(struct objc_class), 1); + __CFRuntimeObjCClassTable[CFURLGetTypeID()] = (struct objc_class *)calloc(sizeof(struct objc_class), 1); + __CFRuntimeObjCClassTable[CFCharacterSetGetTypeID()] = (struct objc_class *)calloc(sizeof(struct objc_class), 1); +#endif + + SYSCALL_TRACE(0xC001); + +#if defined(__MACH__) + { + char **args = *_NSGetArgv(); + CFIndex count; + cnt = *_NSGetArgc(); + CFStringRef *list, buffer[256]; + list = (cnt <= 256) ? buffer : malloc(cnt * sizeof(CFStringRef)); + for (idx = 0, count = 0; idx < cnt && args[idx]; idx++) { + 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); + } + + __CFSessionID = getenv("SECURITYSESSIONID"); +#endif + _CFProcessPath(); // cache this early + +#if defined(__MACH__) + __CFOAInitialize(); + SYSCALL_TRACE(0xC003); +#endif + +#if defined(DEBUG) && !defined(__WIN32__) + // Don't log on MacOS 8 as this will create a log file unnecessarily + CFLog (0, CFSTR("Assertions enabled")); +#endif + SYSCALL_TRACE(0xC0FF); + } +} + +#if defined(__WIN32__) + +/* We have to call __CFInitialize when library is attached to the process. + * (Sergey Zubarev) + */ +WINBOOL WINAPI DllMain( HINSTANCE hInstance, DWORD dwReason, LPVOID pReserved ) { + if (dwReason == DLL_PROCESS_ATTACH) { + __CFInitialize(); + } else if (dwReason == DLL_PROCESS_DETACH) { + } + 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 defined(DEBUG) + if (NULL == cf1) HALT; + if (NULL == cf2) HALT; +#endif + if (cf1 == cf2) return true; + __CFGenericAssertIsCF(cf1); + __CFGenericAssertIsCF(cf2); + if (__CFGenericTypeID(cf1) != __CFGenericTypeID(cf2)) return false; + if (NULL != __CFRuntimeClassTable[__CFGenericTypeID(cf1)]->equal) { + return __CFRuntimeClassTable[__CFGenericTypeID(cf1)]->equal(cf1, cf2); + } + return false; +} + +CFIndex _CFGetRetainCount(CFTypeRef cf) { + uint64_t rc; + CFIndex result; + rc = __CFGetFullRetainCount(cf); + result = (rc < (uint64_t)0x7FFFFFFF) ? (CFIndex)rc : (CFIndex)0x7FFFFFFF; + return result; +} + +CFHashCode _CFHash(CFTypeRef cf) { + if (NULL != __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)]->hash) { + return __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)]->hash(cf); + } + return (CFHashCode)cf; +} + +CF_EXPORT CFTypeRef _CFRetain(CFTypeRef cf) { + CFIndex lowBits = 0; + bool is_threaded = __is_threaded; +#if defined(DEBUG) + if (NULL == cf) HALT; +#endif + if (is_threaded) __CFSpinLock(&__CFGlobalRetainLock); + lowBits = ((CFRuntimeBase *)cf)->_rc; + if (0 == lowBits) { // Constant CFTypeRef + if (is_threaded) __CFSpinUnlock(&__CFGlobalRetainLock); + return cf; + } + lowBits++; + if ((lowBits & 0x07fff) == 0) { + // Roll over another bit to the external ref count + _CFDictionaryIncrementValue(__CFRuntimeExternRefCountTable, DISGUISE(cf)); + lowBits = 0x8000; // Bit 16 indicates external ref count + } + ((CFRuntimeBase *)cf)->_rc = lowBits; + if (is_threaded) __CFSpinUnlock(&__CFGlobalRetainLock); + if (__CFOASafe) { + uint64_t compositeRC; + compositeRC = (lowBits & 0x7fff) + ((uint64_t)(uintptr_t)CFDictionaryGetValue(__CFRuntimeExternRefCountTable, DISGUISE(cf)) << 15); + if (compositeRC > (uint64_t)0x7fffffff) compositeRC = (uint64_t)0x7fffffff; + __CFRecordAllocationEvent(__kCFRetainEvent, (void *)cf, 0, compositeRC, NULL); + } + return cf; +} + +CF_EXPORT void _CFRelease(CFTypeRef cf) { + CFIndex lowBits = 0; + bool is_threaded = __is_threaded; +#if defined(DEBUG) + if (NULL == cf) HALT; +#endif + if (is_threaded) __CFSpinLock(&__CFGlobalRetainLock); + lowBits = ((CFRuntimeBase *)cf)->_rc; + if (0 == lowBits) { // Constant CFTypeRef + if (is_threaded) __CFSpinUnlock(&__CFGlobalRetainLock); + return; + } + if (1 == lowBits) { + if (is_threaded) __CFSpinUnlock(&__CFGlobalRetainLock); + if (__CFOASafe) __CFRecordAllocationEvent(__kCFReleaseEvent, (void *)cf, 0, 0, NULL); + if (__kCFAllocatorTypeID_CONST == __CFGenericTypeID(cf)) { +#if defined(DEBUG) + __CFZombifyDeallocatedMemory((void *)cf); + if (!(__CFZombieLevel & (1 << 4))) { + __CFAllocatorDeallocate((void *)cf); + } +#else + __CFAllocatorDeallocate((void *)cf); +#endif + } else { + CFAllocatorRef allocator; + if (NULL != __CFRuntimeClassTable[__CFGenericTypeID(cf)]->finalize) { + __CFRuntimeClassTable[__CFGenericTypeID(cf)]->finalize(cf); + } + if (__CFBitfieldGetValue(((const CFRuntimeBase *)cf)->_info, 7, 7)) { + allocator = kCFAllocatorSystemDefault; + } else { + allocator = CFGetAllocator(cf); + (intptr_t)cf -= sizeof(CFAllocatorRef); + } +#if defined(DEBUG) + __CFZombifyDeallocatedMemory((void *)cf); + if (!(__CFZombieLevel & (1 << 4))) { + CFAllocatorDeallocate(allocator, (void *)cf); + } +#else + CFAllocatorDeallocate(allocator, (void *)cf); +#endif + if (kCFAllocatorSystemDefault != allocator) { + CFRelease(allocator); + } + } + } else { + if (0x8000 == lowBits) { + // Time to remove a bit from the external ref count + if (0 == _CFDictionaryDecrementValue(__CFRuntimeExternRefCountTable, DISGUISE(cf))) { + lowBits = 0x07fff; + } else { + lowBits = 0x0ffff; + } + } else { + lowBits--; + } + ((CFRuntimeBase *)cf)->_rc = lowBits; + if (is_threaded) __CFSpinUnlock(&__CFGlobalRetainLock); + if (__CFOASafe) { + uint64_t compositeRC; + compositeRC = (lowBits & 0x7fff) + ((uint64_t)(uintptr_t)CFDictionaryGetValue(__CFRuntimeExternRefCountTable, DISGUISE(cf)) << 15); + if (compositeRC > (uint64_t)0x7fffffff) compositeRC = (uint64_t)0x7fffffff; + __CFRecordAllocationEvent(__kCFReleaseEvent, (void *)cf, 0, compositeRC, NULL); + } + } +} + +#undef DO_SYSCALL_TRACE_HELPERS +#undef SYSCALL_TRACE +#undef __kCFAllocatorTypeID_CONST +#undef __CFGenericAssertIsCF diff --git a/Base.subproj/CFRuntime.h b/Base.subproj/CFRuntime.h new file mode 100644 index 0000000..b25340b --- /dev/null +++ b/Base.subproj/CFRuntime.h @@ -0,0 +1,320 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFRuntime.h + Copyright (c) 1999-2003, Apple, Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFRUNTIME__) +#define __COREFOUNDATION_CFRUNTIME__ 1 + +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +enum { + _kCFRuntimeNotATypeID = 0 +}; + +typedef struct __CFRuntimeClass { // Version 0 struct + CFIndex version; + const char *className; + void (*init)(CFTypeRef cf); + CFTypeRef (*copy)(CFAllocatorRef allocator, CFTypeRef cf); +#if MAC_OS_X_VERSION_10_2 <= MAC_OS_X_VERSION_MAX_ALLOWED + void (*finalize)(CFTypeRef cf); +#else + void (*dealloc)(CFTypeRef cf); +#endif + Boolean (*equal)(CFTypeRef cf1, CFTypeRef cf2); + CFHashCode (*hash)(CFTypeRef cf); + CFStringRef (*copyFormattingDesc)(CFTypeRef cf, CFDictionaryRef formatOptions); // str with retain + CFStringRef (*copyDebugDesc)(CFTypeRef cf); // str with retain +} CFRuntimeClass; + +/* Note that CF runtime class registration and unregistration is not currently + * thread-safe, which should not currently be a problem, as long as unregistration + * is done only when valid to do so. + */ + +CF_EXPORT CFTypeID _CFRuntimeRegisterClass(const CFRuntimeClass * const cls); + /* Registers a new class with the CF runtime. Pass in a + * pointer to a CFRuntimeClass structure. The pointer is + * remembered by the CF runtime -- the structure is NOT + * copied. + * + * - version field must be zero currently. + * - className field points to a null-terminated C string + * containing only ASCII (0 - 127) characters; this field + * may NOT be NULL. + * - init field points to a function which classes can use to + * apply some generic initialization to instances as they + * are created; this function is called by both + * _CFRuntimeCreateInstance and _CFRuntimeInitInstance; if + * this field is NULL, no function is called; the instance + * has been initialized enough that the polymorphic funcs + * CFGetTypeID(), CFRetain(), CFRelease(), CFGetRetainCount(), + * and CFGetAllocator() are valid on it when the init + * function if any is called. + * - finalize field points to a function which destroys an + * instance when the retain count has fallen to zero; if + * this is NULL, finalization does nothing. Note that if + * the class-specific functions which create or initialize + * instances more fully decide that a half-initialized + * instance must be destroyed, the finalize function for + * that class has to be able to deal with half-initialized + * instances. The finalize function should NOT destroy the + * memory for the instance itself; that is done by the + * CF runtime after this finalize callout returns. + * - equal field points to an equality-testing function; this + * field may be NULL, in which case only pointer/reference + * equality is performed on instances of this class. + * Pointer equality is tested, and the type IDs are checked + * for equality, before this function is called (so, the + * two instances are not pointer-equal but are of the same + * class before this function is called). + * NOTE: the equal function must implement an immutable + * equality relation, satisfying the reflexive, symmetric, + * and transitive properties, and remains the same across + * time and immutable operations (that is, if equal(A,B) at + * some point, then later equal(A,B) provided neither + * A or B has been mutated). + * - hash field points to a hash-code-computing function for + * instances of this class; this field may be NULL in which + * case the pointer value of an instance is converted into + * a hash. + * NOTE: the hash function and equal function must satisfy + * the relationship "equal(A,B) implies hash(A) == hash(B)"; + * that is, if two instances are equal, their hash codes must + * be equal too. (However, the converse is not true!) + * - copyFormattingDesc field points to a function returning a + * CFStringRef with a human-readable description of the + * instance; if this is NULL, the type does not have special + * human-readable string-formats. + * - copyDebugDesc field points to a function returning a + * CFStringRef with a debugging description of the instance; + * if this is NULL, a simple description is generated. + * + * This function returns _kCFRuntimeNotATypeID on failure, or + * on success, returns the CFTypeID for the new class. This + * CFTypeID is what the class uses to allocate or initialize + * instances of the class. It is also returned from the + * conventional *GetTypeID() function, which returns the + * class's CFTypeID so that clients can compare the + * CFTypeID of instances with that of a class. + * + * The function to compute a human-readable string is very + * optional, and is really only interesting for classes, + * like strings or numbers, where it makes sense to format + * the instance using just its contents. + */ + +CF_EXPORT const CFRuntimeClass * _CFRuntimeGetClassWithTypeID(CFTypeID typeID); + /* Returns the pointer to the CFRuntimeClass which was + * assigned the specified CFTypeID. + */ + +CF_EXPORT void _CFRuntimeUnregisterClassWithTypeID(CFTypeID typeID); + /* Unregisters the class with the given type ID. It is + * undefined whether type IDs are reused or not (expect + * that they will be). + * + * Whether or not unregistering the class is a good idea or + * not is not CF's responsibility. In particular you must + * be quite sure all instances are gone, and there are no + * valid weak refs to such in other threads. + */ + +/* All CF "instances" start with this structure. Never refer to + * these fields directly -- they are for CF's use and may be added + * to or removed or change format without warning. Binary + * compatibility for uses of this struct is not guaranteed from + * release to release. + */ +typedef struct __CFRuntimeBase { + void *_isa; +#if defined(__ppc__) + uint16_t _rc; + uint16_t _info; +#elif defined(__i386__) + uint16_t _info; + uint16_t _rc; +#else +#error unknown architecture +#endif +} CFRuntimeBase; + +#if defined(__ppc__) +#define INIT_CFRUNTIME_BASE(isa, info, rc) { isa, info, rc } +#elif defined(__i386__) +#define INIT_CFRUNTIME_BASE(isa, info, rc) { isa, rc, info } +#elif +#error unknown architecture +#endif + +CF_EXPORT CFTypeRef _CFRuntimeCreateInstance(CFAllocatorRef allocator, CFTypeID typeID, uint32_t extraBytes, unsigned char *category); + /* Creates a new CF instance of the class specified by the + * given CFTypeID, using the given allocator, and returns it. + * If the allocator returns NULL, this function returns NULL. + * A CFRuntimeBase structure is initialized at the beginning + * of the returned instance. extraBytes is the additional + * number of bytes to allocate for the instance (BEYOND that + * needed for the CFRuntimeBase). If the specified CFTypeID + * is unknown to the CF runtime, this function returns NULL. + * No part of the new memory other than base header is + * initialized (the extra bytes are not zeroed, for example). + * All instances created with this function must be destroyed + * only through use of the CFRelease() function -- instances + * must not be destroyed by using CFAllocatorDeallocate() + * directly, even in the initialization or creation functions + * of a class. Pass NULL for the category parameter. + */ + +CF_EXPORT void _CFRuntimeSetInstanceTypeID(CFTypeRef cf, CFTypeID typeID); + /* This function changes the typeID of the given instance. + * If the specified CFTypeID is unknown to the CF runtime, + * this function does nothing. This function CANNOT be used + * to initialize an instance. It is for advanced usages such + * as faulting. + */ + +#if 0 +// ========================= EXAMPLE ========================= + +// Example: EXRange -- a "range" object, which keeps the starting +// location and length of the range. ("EX" as in "EXample"). + +// ---- API ---- + +typedef const struct __EXRange * EXRangeRef; + +CFTypeID EXRangeGetTypeID(void); + +EXRangeRef EXRangeCreate(CFAllocatorRef allocator, uint32_t location, uint32_t length); + +uint32_t EXRangeGetLocation(EXRangeRef rangeref); +uint32_t EXRangeGetLength(EXRangeRef rangeref); + + +// ---- implementation ---- + +#include +#include + +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("{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); + if (NULL == newrange) { + return NULL; + } + newrange->_location = location; + newrange->_length = length; + return (EXRangeRef)newrange; +} + +uint32_t EXRangeGetLocation(EXRangeRef rangeref) { + return rangeref->_location; +} + +uint32_t EXRangeGetLength(EXRangeRef rangeref) { + return rangeref->_length; +} + +#endif + +#if defined(__cplusplus) +} +#endif + +#endif /* ! __COREFOUNDATION_CFRUNTIME__ */ diff --git a/Base.subproj/CFSortFunctions.c b/Base.subproj/CFSortFunctions.c new file mode 100644 index 0000000..47eb122 --- /dev/null +++ b/Base.subproj/CFSortFunctions.c @@ -0,0 +1,833 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFSortFunctions.c + Copyright 1999-2002, Apple, Inc. All rights reserved. + Responsibility: Christopher Kane +*/ + +/* This file contains code copied from the Libc project's files + qsort.c, merge.c, and heapsort.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. We may not keep the bsd functions + separate eventually. +*/ + +#include +#if defined(__MACH__) || defined(__LINUX__) || defined(__FREEBSD__) +#include +#else +#define EINVAL 22 +#endif +#include +#include +#include +#include +#include "CFInternal.h" + +static int bsd_mergesort(void *base, int nmemb, int size, CFComparatorFunction cmp, void *context); + +/* Comparator is passed the address of the values. */ +void CFMergeSortArray(void *list, CFIndex count, CFIndex elementSize, CFComparatorFunction comparator, void *context) { + bsd_mergesort(list, count, elementSize, comparator, context); +} + +#if 0 +// non-recursing qsort with side index list which is the actual thing sorted +// XXX kept here for possible later reference +void cjk_big_isort(char *list, int size, int *idx_list, int (*comp)(const void *, const void *, void *), void *context, int lo, int hi) { + int t, idx, idx2; + char *v1; + for (idx = lo + 1; idx < hi + 1; idx++) { + t = idx_list[idx]; + v1 = list + t * size; + for (idx2 = idx; lo < idx2 && (comp(v1, list + idx_list[idx2 - 1] * size, context) < 0); idx2--) + idx_list[idx2] = idx_list[idx2 - 1]; + idx_list[idx2] = t; + } +} + +void cjk_big_qsort(char *list, int size, int *idx_list, int (*comp)(const void *, const void *, void *), void *context, int lo, int hi, int *recurse) { + int p, idx, mid, g1, g2, g3, r1, r2; + char *v1, *v2, *v3; + g1 = lo; + g2 = (hi + lo) / 2; //random() % (hi - lo + 1) + lo; + g3 = hi; + v1 = list + idx_list[g1] * size; + v2 = list + idx_list[g2] * size; + v3 = list + idx_list[g3] * size; + if (comp(v1, v2, context) < 0) { + p = comp(v2, v3, context) < 0 ? g2 : (comp(v1, v3, context) < 0 ? g3 : g1); + } else { + p = comp(v2, v3, context) > 0 ? g2 : (comp(v1, v3, context) < 0 ? g1 : g3); + } + if (lo != p) {int t = idx_list[lo]; idx_list[lo] = idx_list[p]; idx_list[p] = t;} + r2 = 1; + v2 = list + idx_list[lo] * size; + for (mid = lo, idx = lo + 1; idx < hi + 1; idx++) { + v1 = list + idx_list[idx] * size; + r1 = comp(v1, v2, context); + r2 = r2 && (0 == r1); + if (r1 < 0) { + mid++; + if (idx != mid) {int t = idx_list[idx]; idx_list[idx] = idx_list[mid]; idx_list[mid] = t;} + } + } + if (lo != mid) {int t = idx_list[lo]; idx_list[lo] = idx_list[mid]; idx_list[mid] = t;} + *recurse = !r2 ? mid : -1; +} + +void cjk_big_sort(char *list, int n, int size, int (*comp)(const void *, const void *, void *), void *context) { + int len = 0, los[40], his[40]; + // 40 is big enough for 2^40 elements, in theory; in practice, much + // lower but still sufficient; should recurse if the 40 limit is hit + int *idx_list = malloc(sizeof(int) * n); + char *tmp_list = malloc(n * size); + int idx; + for (idx = 0; idx < n; idx++) { + idx_list[idx] = idx; + } + los[len] = 0; + his[len++] = n - 1; + while (0 < len) { + int lo, hi; + len--; + lo = los[len]; + hi = his[len]; + if (5 < (hi - lo)) { + int mid; + cjk_big_qsort(list, size, idx_list, comp, context, lo, hi, &mid); + if (0 < mid) { + los[len] = lo; + his[len++] = mid - 1; + los[len] = mid + 1; + his[len++] = hi; + } + } else { + cjk_big_isort(list, size, idx_list, comp, context, lo, hi); + } + } + for (idx = 0; idx < n; idx++) + memmove(tmp_list + idx * size, list + idx_list[idx] * size, size); + memmove(list, tmp_list, n * size); + free(tmp_list); + free(idx_list); +} +#endif + + +/* 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 !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); \ + register TYPE *pi = (TYPE *) (parmi); \ + register TYPE *pj = (TYPE *) (parmj); \ + do { \ + register 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; + +CF_INLINE void +swapfunc(char *a, char *b, int n, int 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) + +CF_INLINE char * +med3(char *a, char *b, char *c, int (*cmp)(const void *, const void *, void *), void *context) { + return cmp(a, b, context) < 0 ? + (cmp(b, c, context) < 0 ? b : (cmp(a, c, context) < 0 ? c : a )) + :(cmp(b, c, context) > 0 ? b : (cmp(a, c, context) < 0 ? a : c )); +} + +/* Comparator is passed the address of the values. */ +void CFQSortArray(void *aVoidStar, CFIndex n, CFIndex es, CFComparatorFunction cmp, void *context) +{ + char *a = (char *)aVoidStar; + char *pa, *pb, *pc, *pd, *pl, *pm, *pn; + int d, r, swaptype, swap_cnt; + +loop: SWAPINIT(a, es); + swap_cnt = 0; + if (n < 7) { + for (pm = a + es; pm < (char *) a + n * es; pm += es) + for (pl = pm; pl > (char *) a && cmp(pl - es, pl, context) > 0; + pl -= es) + swap(pl, pl - es); + return; + } + pm = a + (n / 2) * es; + if (n > 7) { + pl = a; + pn = a + (n - 1) * es; + if (n > 40) { + d = (n / 8) * es; + pl = med3(pl, pl + d, pl + 2 * d, cmp, context); + pm = med3(pm - d, pm, pm + d, cmp, context); + pn = med3(pn - 2 * d, pn - d, pn, cmp, context); + } + pm = med3(pl, pm, pn, cmp, context); + } + swap(a, pm); + pa = pb = a + es; + + pc = pd = a + (n - 1) * es; + for (;;) { + while (pb <= pc && (r = cmp(pb, a, context)) <= 0) { + if (r == 0) { + swap_cnt = 1; + swap(pa, pb); + pa += es; + } + pb += es; + } + while (pb <= pc && (r = cmp(pc, a, context)) >= 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 = a + es; pm < (char *) a + n * es; pm += es) + for (pl = pm; pl > (char *) a && cmp(pl - es, pl, context) > 0; + pl -= es) + swap(pl, pl - es); + return; + } + + pn = a + n * es; + r = min(pa - (char *)a, pb - pa); + vecswap(a, pb - r, r); + r = min(pd - pc, pn - pd - es); + vecswap(pb, pn - r, r); + if ((r = pb - pa) > es) + CFQSortArray(a, r / es, es, cmp, context); + if ((r = pd - pc) > es) { + /* Iterate rather than recurse to save stack space */ + a = pn - r; + n = r / es; + goto loop; + } +/* CFQSortArray(pn - r, r / es, es, cmp, context);*/ +} + +#undef min +#undef swapcode +#undef SWAPINIT +#undef swap +#undef vecswap + +/* 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. + */ + + +/* + * 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.) + */ + + +#define ISIZE sizeof(int) +#define PSIZE sizeof(uint8_t *) +#define ICOPY_LIST(src, dst, last) \ + do \ + *(int*)dst = *(int*)src, src += ISIZE, dst += ISIZE; \ + while(src < last) +#define ICOPY_ELT(src, dst, i) \ + do \ + *(int*) dst = *(int*) 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) (uint8_t **) \ + ((uint8_t *)0 + \ + (((uint8_t *)p + PSIZE - 1 - (uint8_t *) 0) & ~(PSIZE - 1))) + + +#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); \ +} + +/* + * This is to avoid out-of-bounds addresses in sorting the + * last 4 elements. + */ +static void +insertionsort(char *a, int n, int size, int (*cmp)(const void *, const void *, void *), void *context) { + char *ai, *s, *t, *u, tmp; + int 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); + } +} + +/* + * 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(char *list1, char *list2, int n, int size, int (*cmp)(const void *, const void *, void *), void *context) { + int i, length, size2, tmp, sense; + char *f1, *f2, *s, *l2, *last, *p2; + + size2 = size*2; + if (n <= 5) { + insertionsort(list1, n, size, cmp, context); + *EVAL(list2) = (uint8_t*) 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 */ +} + +/* + * Arguments are as for qsort. + */ +static int +bsd_mergesort(void *base, int nmemb, int size, CFComparatorFunction cmp, void *context) { + register int i, sense; + int big, iflag; + register uint8_t *f1, *f2, *t, *b, *tp2, *q, *l1, *l2; + uint8_t *list2, *list1, *p2, *p, *last, **p1; + + if (size < (int)PSIZE / 2) { /* Pointers must fit into 2 * size. */ + errno = EINVAL; + return (-1); + } + + /* + * XXX + * Stupid subtraction for the Cray. + */ + iflag = 0; + if (!(size % ISIZE) && !(((char *)base - (char *)0) % ISIZE)) + iflag = 1; + + if ((list2 = malloc(nmemb * size + PSIZE)) == NULL) + return (-1); + + list1 = 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 */ +/* LINEAR: */ 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; +/* SLOWCASE: */ 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 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 + +#if 0 +/* stdlib.subproj/heapsort.c =========================================== */ + +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Ronnie Kon at Mindcraft Inc., Kevin Lew and Elmer Yglesias. + * + * 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. + */ + +/* + * Swap two areas of size number of bytes. Although qsort(3) permits random + * blocks of memory to be sorted, sorting pointers is almost certainly the + * common case (and, were it not, could easily be made so). Regardless, it + * isn't worth optimizing; the SWAP's get sped up by the cache, and pointer + * arithmetic gets lost in the time required for comparison function calls. + */ +#define SWAP(a, b, count, size, tmp) { \ + count = size; \ + do { \ + tmp = *a; \ + *a++ = *b; \ + *b++ = tmp; \ + } while (--count); \ +} + +/* Copy one block of size size to another. */ +#define COPY(a, b, count, size, tmp1, tmp2) { \ + count = size; \ + tmp1 = a; \ + tmp2 = b; \ + do { \ + *tmp1++ = *tmp2++; \ + } while (--count); \ +} + +/* + * Build the list into a heap, where a heap is defined such that for + * the records K1 ... KN, Kj/2 >= Kj for 1 <= j/2 <= j <= N. + * + * There two cases. If j == nmemb, select largest of Ki and Kj. If + * j < nmemb, select largest of Ki, Kj and Kj+1. + */ +#define CREATE(initval, nmemb, par_i, child_i, par, child, size, count, tmp) { \ + for (par_i = initval; (child_i = par_i * 2) <= nmemb; \ + par_i = child_i) { \ + child = base + child_i * size; \ + if (child_i < nmemb && compar(child, child + size, context) < 0) { \ + child += size; \ + ++child_i; \ + } \ + par = base + par_i * size; \ + if (compar(child, par, context) <= 0) \ + break; \ + SWAP(par, child, count, size, tmp); \ + } \ +} + +/* + * Select the top of the heap and 'heapify'. Since by far the most expensive + * action is the call to the compar function, a considerable optimization + * in the average case can be achieved due to the fact that k, the displaced + * elememt, is ususally quite small, so it would be preferable to first + * heapify, always maintaining the invariant that the larger child is copied + * over its parent's record. + * + * Then, starting from the *bottom* of the heap, finding k's correct place, + * again maintianing the invariant. As a result of the invariant no element + * is 'lost' when k is assigned its correct place in the heap. + * + * The time savings from this optimization are on the order of 15-20% for the + * average case. See Knuth, Vol. 3, page 158, problem 18. + * + * XXX Don't break the #define SELECT line, below. Reiser cpp gets upset. + */ +#define SELECT(par_i, child_i, nmemb, par, child, size, k, count, tmp1, tmp2) { \ + for (par_i = 1; (child_i = par_i * 2) <= nmemb; par_i = child_i) { \ + child = base + child_i * size; \ + if (child_i < nmemb && compar(child, child + size, context) < 0) { \ + child += size; \ + ++child_i; \ + } \ + par = base + par_i * size; \ + COPY(par, child, count, size, tmp1, tmp2); \ + } \ + for (;;) { \ + child_i = par_i; \ + par_i = child_i / 2; \ + child = base + child_i * size; \ + par = base + par_i * size; \ + if (child_i == 1 || compar(k, par, context) < 0) { \ + COPY(child, k, count, size, tmp1, tmp2); \ + break; \ + } \ + COPY(child, par, count, size, tmp1, tmp2); \ + } \ +} + +/* + * Heapsort -- Knuth, Vol. 3, page 145. Runs in O (N lg N), both average + * and worst. While heapsort is faster than the worst case of quicksort, + * the BSD quicksort does median selection so that the chance of finding + * a data set that will trigger the worst case is nonexistent. Heapsort's + * only advantage over quicksort is that it requires little additional memory. + */ +static int +bsd_heapsort(void *vbase, int nmemb, int size, int (*compar)(const void *, const void *, void *), void *context) { + register int cnt, i, j, l; + register char tmp, *tmp1, *tmp2; + char *base, *k, *p, *t; + + if (nmemb <= 1) + return (0); + + if (!size) { + errno = EINVAL; + return (-1); + } + + if ((k = malloc(size)) == NULL) + return (-1); + + /* + * Items are numbered from 1 to nmemb, so offset from size bytes + * below the starting address. + */ + base = (char *)vbase - size; + + for (l = nmemb / 2 + 1; --l;) + CREATE(l, nmemb, i, j, t, p, size, cnt, tmp); + + /* + * For each element of the heap, save the largest element into its + * final slot, save the displaced element (k), then recreate the + * heap. + */ + while (nmemb > 1) { + COPY(k, base + nmemb * size, cnt, size, tmp1, tmp2); + COPY(base + nmemb * size, base + size, cnt, size, tmp1, tmp2); + --nmemb; + SELECT(i, j, nmemb, t, p, size, k, cnt, tmp1, tmp2); + } + free(k); + return (0); +} + +#undef SWAP +#undef COPY +#undef CREATE +#undef SELECT + +#endif + +/* ===================================================================== */ + +#undef EINVAL + diff --git a/Base.subproj/CFUUID.c b/Base.subproj/CFUUID.c new file mode 100644 index 0000000..405fed1 --- /dev/null +++ b/Base.subproj/CFUUID.c @@ -0,0 +1,383 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFUUID.c + Copyright 1999-2002, Apple, Inc. All rights reserved. + Responsibility: Doug Davidson +*/ + +#include +#include "CFInternal.h" + +/* uuid_t already defined in RPCDCE.H (WIN32 header) + * (Aleksey Dukhnyakov) + */ +#if defined(__WIN32__) +#define UUID_T_DEFINED 1 +#endif + +#if !defined(UUID_T_DEFINED) +#define UUID_T_DEFINED 1 + +/* uuid + * + * Universal Unique ID. Note this definition will result is a 16-byte + * structure regardless what platform it is on. + */ + +struct uuid_t { + unsigned long time_low; + unsigned short time_mid; + unsigned short time_hi_and_version; + unsigned char clock_seq_hi_and_reserved; + unsigned char clock_seq_low; + unsigned char node[6]; +}; + +typedef struct uuid_t uuid_t; + +#endif + +extern unsigned long _CFGenerateUUID(uuid_t *uuid); + +static CFMutableDictionaryRef _uniquedUUIDs = NULL; +static CFSpinLock_t CFUUIDGlobalDataLock = 0; + +struct __CFUUID { + CFRuntimeBase _base; + CFUUIDBytes _bytes; +}; + +static Boolean __CFisEqualUUIDBytes(const void *ptr1, const void *ptr2) { + CFUUIDBytes *p1 = (CFUUIDBytes *)ptr1; + CFUUIDBytes *p2 = (CFUUIDBytes *)ptr2; + + return (((p1->byte0 == p2->byte0) && (p1->byte1 == p2->byte1) && (p1->byte2 == p2->byte2) && (p1->byte3 == p2->byte3) && (p1->byte4 == p2->byte4) && (p1->byte5 == p2->byte5) && (p1->byte6 == p2->byte6) && (p1->byte7 == p2->byte7) && (p1->byte8 == p2->byte8) && (p1->byte9 == p2->byte9) && (p1->byte10 == p2->byte10) && (p1->byte11 == p2->byte11) && (p1->byte12 == p2->byte12) && (p1->byte13 == p2->byte13) && (p1->byte14 == p2->byte14) && (p1->byte15 == p2->byte15)) ? true : false); +} + +static CFHashCode __CFhashUUIDBytes(const void *ptr) { + return CFHashBytes((uint8_t *)ptr, 16); +} + +static CFDictionaryKeyCallBacks __CFUUIDBytesDictionaryKeyCallBacks = {0, NULL, NULL, NULL, __CFisEqualUUIDBytes, __CFhashUUIDBytes}; +static CFDictionaryValueCallBacks __CFnonRetainedUUIDDictionaryValueCallBacks = {0, NULL, NULL, CFCopyDescription, CFEqual}; + +static void __CFUUIDAddUniqueUUID(CFUUIDRef uuid) { + __CFSpinLock(&CFUUIDGlobalDataLock); + if (_uniquedUUIDs == NULL) { + /* Allocate table from default allocator */ + _uniquedUUIDs = CFDictionaryCreateMutable(NULL, 0, &__CFUUIDBytesDictionaryKeyCallBacks, &__CFnonRetainedUUIDDictionaryValueCallBacks); + } + CFDictionarySetValue(_uniquedUUIDs, &(uuid->_bytes), uuid); + __CFSpinUnlock(&CFUUIDGlobalDataLock); +} + +static void __CFUUIDRemoveUniqueUUID(CFUUIDRef uuid) { + __CFSpinLock(&CFUUIDGlobalDataLock); + if (_uniquedUUIDs != NULL) { + CFDictionaryRemoveValue(_uniquedUUIDs, &(uuid->_bytes)); + } + __CFSpinUnlock(&CFUUIDGlobalDataLock); +} + +static CFUUIDRef __CFUUIDGetUniquedUUID(CFUUIDBytes *bytes) { + CFUUIDRef uuid = NULL; + __CFSpinLock(&CFUUIDGlobalDataLock); + if (_uniquedUUIDs != NULL) { + uuid = CFDictionaryGetValue(_uniquedUUIDs, bytes); + } + __CFSpinUnlock(&CFUUIDGlobalDataLock); + return uuid; +} + +static void __CFUUIDDeallocate(CFTypeRef cf) { + struct __CFUUID *uuid = (struct __CFUUID *)cf; + __CFUUIDRemoveUniqueUUID(uuid); +} + +static CFStringRef __CFUUIDCopyDescription(CFTypeRef cf) { + CFStringRef uuidStr = CFUUIDCreateString(CFGetAllocator(cf), (CFUUIDRef)cf); + CFStringRef desc = CFStringCreateWithFormat(NULL, NULL, CFSTR(" %@"), cf, uuidStr); + CFRelease(uuidStr); + return desc; +} + +static CFStringRef __CFUUIDCopyFormattingDescription(CFTypeRef cf, CFDictionaryRef formatOptions) { + return CFUUIDCreateString(CFGetAllocator(cf), (CFUUIDRef)cf); +} + +static CFTypeID __kCFUUIDTypeID = _kCFRuntimeNotATypeID; + +static const CFRuntimeClass __CFUUIDClass = { + 0, + "CFUUID", + NULL, // init + NULL, // copy + __CFUUIDDeallocate, + NULL, // equal + NULL, // hash + __CFUUIDCopyFormattingDescription, + __CFUUIDCopyDescription +}; + +__private_extern__ void __CFUUIDInitialize(void) { + __kCFUUIDTypeID = _CFRuntimeRegisterClass(&__CFUUIDClass); +} + +CFTypeID CFUUIDGetTypeID(void) { + return __kCFUUIDTypeID; +} + +static CFUUIDRef __CFUUIDCreateWithBytesPrimitive(CFAllocatorRef allocator, CFUUIDBytes bytes, Boolean isConst) { + struct __CFUUID *uuid = (struct __CFUUID *)__CFUUIDGetUniquedUUID(&bytes); + + if (uuid == NULL) { + UInt32 size; + size = sizeof(struct __CFUUID) - sizeof(CFRuntimeBase); + uuid = (struct __CFUUID *)_CFRuntimeCreateInstance(allocator, __kCFUUIDTypeID, size, NULL); + + if (NULL == uuid) return NULL; + + uuid->_bytes = bytes; + + __CFUUIDAddUniqueUUID(uuid); + } else if (!isConst) { + CFRetain(uuid); + } + + return (CFUUIDRef)uuid; +} + +CFUUIDRef CFUUIDCreate(CFAllocatorRef alloc) { + /* Create a new bytes struct and then call the primitive. */ + CFUUIDBytes bytes; + unsigned long retval = 0; + + __CFSpinLock(&CFUUIDGlobalDataLock); + retval = _CFGenerateUUID((uuid_t *)&bytes); + __CFSpinUnlock(&CFUUIDGlobalDataLock); + + return (retval == 0) ? __CFUUIDCreateWithBytesPrimitive(alloc, bytes, false) : NULL; +} + +CFUUIDRef CFUUIDCreateWithBytes(CFAllocatorRef alloc, uint8_t byte0, uint8_t byte1, uint8_t byte2, uint8_t byte3, uint8_t byte4, uint8_t byte5, uint8_t byte6, uint8_t byte7, uint8_t byte8, uint8_t byte9, uint8_t byte10, uint8_t byte11, uint8_t byte12, uint8_t byte13, uint8_t byte14, uint8_t byte15) { + CFUUIDBytes bytes; + // CodeWarrior can't handle the structure assignment of bytes, so we must explode this - REW, 10/8/99 + bytes.byte0 = byte0; + bytes.byte1 = byte1; + bytes.byte2 = byte2; + bytes.byte3 = byte3; + bytes.byte4 = byte4; + bytes.byte5 = byte5; + bytes.byte6 = byte6; + bytes.byte7 = byte7; + bytes.byte8 = byte8; + bytes.byte9 = byte9; + bytes.byte10 = byte10; + bytes.byte11 = byte11; + bytes.byte12 = byte12; + bytes.byte13 = byte13; + bytes.byte14 = byte14; + bytes.byte15 = byte15; + + return __CFUUIDCreateWithBytesPrimitive(alloc, bytes, false); +} + +static void _intToHexChars(UInt32 in, UniChar *out, int digits) { + int shift; + UInt32 d; + + while (--digits >= 0) { + shift = digits << 2; + d = 0x0FL & (in >> shift); + if (d <= 9) { + *out++ = (UniChar)'0' + d; + } else { + *out++ = (UniChar)'A' + (d - 10); + } + } +} + +static uint8_t _byteFromHexChars(UniChar *in) { + uint8_t result = 0; + UniChar c; + uint8_t d; + CFIndex i; + + for (i=0; i<2; i++) { + c = in[i]; + if ((c >= (UniChar)'0') && (c <= (UniChar)'9')) { + d = c - (UniChar)'0'; + } else if ((c >= (UniChar)'a') && (c <= (UniChar)'f')) { + d = c - ((UniChar)'a' - 10); + } else if ((c >= (UniChar)'A') && (c <= (UniChar)'F')) { + d = c - ((UniChar)'A' - 10); + } else { + return 0; + } + result = (result << 4) | d; + } + + return result; +} + +CF_INLINE Boolean _isHexChar(UniChar c) { + return ((((c >= (UniChar)'0') && (c <= (UniChar)'9')) || ((c >= (UniChar)'a') && (c <= (UniChar)'f')) || ((c >= (UniChar)'A') && (c <= (UniChar)'F'))) ? true : false); +} + +#define READ_A_BYTE(into) if (i+1 < len) { \ + (into) = _byteFromHexChars(&(chars[i])); \ + i+=2; \ +} + +CFUUIDRef CFUUIDCreateFromString(CFAllocatorRef alloc, CFStringRef uuidStr) { + /* Parse the string into a bytes struct and then call the primitive. */ + CFUUIDBytes bytes; + UniChar chars[100]; + CFIndex len; + CFIndex i = 0; + + if (uuidStr == NULL) return NULL; + + len = CFStringGetLength(uuidStr); + if (len > 100) { + len = 100; + } else if (len == 0) { + return NULL; + } + CFStringGetCharacters(uuidStr, CFRangeMake(0, len), chars); + memset((void *)&bytes, 0, sizeof(bytes)); + + /* Skip initial random stuff */ + while (!_isHexChar(chars[i]) && (i < len)) { + i++; + } + + READ_A_BYTE(bytes.byte0); + READ_A_BYTE(bytes.byte1); + READ_A_BYTE(bytes.byte2); + READ_A_BYTE(bytes.byte3); + + i++; + + READ_A_BYTE(bytes.byte4); + READ_A_BYTE(bytes.byte5); + + i++; + + READ_A_BYTE(bytes.byte6); + READ_A_BYTE(bytes.byte7); + + i++; + + READ_A_BYTE(bytes.byte8); + READ_A_BYTE(bytes.byte9); + + i++; + + READ_A_BYTE(bytes.byte10); + READ_A_BYTE(bytes.byte11); + READ_A_BYTE(bytes.byte12); + READ_A_BYTE(bytes.byte13); + READ_A_BYTE(bytes.byte14); + READ_A_BYTE(bytes.byte15); + + return __CFUUIDCreateWithBytesPrimitive(alloc, bytes, false); +} + +CFStringRef CFUUIDCreateString(CFAllocatorRef alloc, CFUUIDRef uuid) { + CFMutableStringRef str = CFStringCreateMutable(alloc, 0); + UniChar buff[12]; + + // First segment (4 bytes, 8 digits + 1 dash) + _intToHexChars(uuid->_bytes.byte0, buff, 2); + _intToHexChars(uuid->_bytes.byte1, &(buff[2]), 2); + _intToHexChars(uuid->_bytes.byte2, &(buff[4]), 2); + _intToHexChars(uuid->_bytes.byte3, &(buff[6]), 2); + buff[8] = (UniChar)'-'; + CFStringAppendCharacters(str, buff, 9); + + // Second segment (2 bytes, 4 digits + 1 dash) + _intToHexChars(uuid->_bytes.byte4, buff, 2); + _intToHexChars(uuid->_bytes.byte5, &(buff[2]), 2); + buff[4] = (UniChar)'-'; + CFStringAppendCharacters(str, buff, 5); + + // Third segment (2 bytes, 4 digits + 1 dash) + _intToHexChars(uuid->_bytes.byte6, buff, 2); + _intToHexChars(uuid->_bytes.byte7, &(buff[2]), 2); + buff[4] = (UniChar)'-'; + CFStringAppendCharacters(str, buff, 5); + + // Fourth segment (2 bytes, 4 digits + 1 dash) + _intToHexChars(uuid->_bytes.byte8, buff, 2); + _intToHexChars(uuid->_bytes.byte9, &(buff[2]), 2); + buff[4] = (UniChar)'-'; + CFStringAppendCharacters(str, buff, 5); + + // Fifth segment (6 bytes, 12 digits) + _intToHexChars(uuid->_bytes.byte10, buff, 2); + _intToHexChars(uuid->_bytes.byte11, &(buff[2]), 2); + _intToHexChars(uuid->_bytes.byte12, &(buff[4]), 2); + _intToHexChars(uuid->_bytes.byte13, &(buff[6]), 2); + _intToHexChars(uuid->_bytes.byte14, &(buff[8]), 2); + _intToHexChars(uuid->_bytes.byte15, &(buff[10]), 2); + CFStringAppendCharacters(str, buff, 12); + + return str; +} + +CFUUIDRef CFUUIDGetConstantUUIDWithBytes(CFAllocatorRef alloc, uint8_t byte0, uint8_t byte1, uint8_t byte2, uint8_t byte3, uint8_t byte4, uint8_t byte5, uint8_t byte6, uint8_t byte7, uint8_t byte8, uint8_t byte9, uint8_t byte10, uint8_t byte11, uint8_t byte12, uint8_t byte13, uint8_t byte14, uint8_t byte15) { + CFUUIDBytes bytes; + // CodeWarrior can't handle the structure assignment of bytes, so we must explode this - REW, 10/8/99 + bytes.byte0 = byte0; + bytes.byte1 = byte1; + bytes.byte2 = byte2; + bytes.byte3 = byte3; + bytes.byte4 = byte4; + bytes.byte5 = byte5; + bytes.byte6 = byte6; + bytes.byte7 = byte7; + bytes.byte8 = byte8; + bytes.byte9 = byte9; + bytes.byte10 = byte10; + bytes.byte11 = byte11; + bytes.byte12 = byte12; + bytes.byte13 = byte13; + bytes.byte14 = byte14; + bytes.byte15 = byte15; + + return __CFUUIDCreateWithBytesPrimitive(alloc, bytes, true); +} + +CFUUIDBytes CFUUIDGetUUIDBytes(CFUUIDRef uuid) { + return uuid->_bytes; +} + +CF_EXPORT CFUUIDRef CFUUIDCreateFromUUIDBytes(CFAllocatorRef alloc, CFUUIDBytes bytes) { + return __CFUUIDCreateWithBytesPrimitive(alloc, bytes, false); +} + +#undef READ_A_BYTE + diff --git a/Base.subproj/CFUUID.h b/Base.subproj/CFUUID.h new file mode 100644 index 0000000..6310f8b --- /dev/null +++ b/Base.subproj/CFUUID.h @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFUUID.h + Copyright (c) 1999-2003, Apple, Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFUUID__) +#define __COREFOUNDATION_CFUUID__ 1 + +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +typedef const struct __CFUUID * CFUUIDRef; + +typedef struct { + UInt8 byte0; + UInt8 byte1; + UInt8 byte2; + UInt8 byte3; + UInt8 byte4; + UInt8 byte5; + UInt8 byte6; + UInt8 byte7; + UInt8 byte8; + UInt8 byte9; + UInt8 byte10; + UInt8 byte11; + UInt8 byte12; + UInt8 byte13; + UInt8 byte14; + UInt8 byte15; +} CFUUIDBytes; +/* The CFUUIDBytes struct is a 128-bit struct that contains the +raw UUID. A CFUUIDRef can provide such a struct from the +CFUUIDGetUUIDBytes() function. This struct is suitable for +passing to APIs that expect a raw UUID. +*/ + +CF_EXPORT +CFTypeID CFUUIDGetTypeID(void); + +CF_EXPORT +CFUUIDRef CFUUIDCreate(CFAllocatorRef alloc); + /* Create and return a brand new unique identifier */ + +CF_EXPORT +CFUUIDRef CFUUIDCreateWithBytes(CFAllocatorRef alloc, UInt8 byte0, UInt8 byte1, UInt8 byte2, UInt8 byte3, UInt8 byte4, UInt8 byte5, UInt8 byte6, UInt8 byte7, UInt8 byte8, UInt8 byte9, UInt8 byte10, UInt8 byte11, UInt8 byte12, UInt8 byte13, UInt8 byte14, UInt8 byte15); + /* Create and return an identifier with the given contents. This may return an existing instance with its ref count bumped because of uniquing. */ + +CF_EXPORT +CFUUIDRef CFUUIDCreateFromString(CFAllocatorRef alloc, CFStringRef uuidStr); + /* Converts from a string representation to the UUID. This may return an existing instance with its ref count bumped because of uniquing. */ + +CF_EXPORT +CFStringRef CFUUIDCreateString(CFAllocatorRef alloc, CFUUIDRef uuid); + /* Converts from a UUID to its string representation. */ + +CF_EXPORT +CFUUIDRef CFUUIDGetConstantUUIDWithBytes(CFAllocatorRef alloc, UInt8 byte0, UInt8 byte1, UInt8 byte2, UInt8 byte3, UInt8 byte4, UInt8 byte5, UInt8 byte6, UInt8 byte7, UInt8 byte8, UInt8 byte9, UInt8 byte10, UInt8 byte11, UInt8 byte12, UInt8 byte13, UInt8 byte14, UInt8 byte15); + /* This returns an immortal CFUUIDRef that should not be released. It can be used in headers to declare UUID constants with #define. */ + +CF_EXPORT +CFUUIDBytes CFUUIDGetUUIDBytes(CFUUIDRef uuid); + +CF_EXPORT +CFUUIDRef CFUUIDCreateFromUUIDBytes(CFAllocatorRef alloc, CFUUIDBytes bytes); + +#if defined(__cplusplus) +} +#endif + +#endif /* ! __COREFOUNDATION_CFUUID__ */ + diff --git a/Base.subproj/CFUtilities.c b/Base.subproj/CFUtilities.c new file mode 100644 index 0000000..4a937ab --- /dev/null +++ b/Base.subproj/CFUtilities.c @@ -0,0 +1,533 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFUtilities.c + Copyright 1998-2002, Apple, Inc. All rights reserved. + Responsibility: Christopher Kane +*/ + +#include "CFUtilities.h" +#include "CFInternal.h" +#include +#include +#include +#include +#include +#include +#include +#if defined(__MACH__) + #include + #include + #include + #include + #include + #include +#endif +#if defined(__WIN32__) + #include + #include +#endif +#if defined(__LINUX__) || defined(__FREEBSD__) + #include + #include + #include +#endif + +extern char **_CFGetProgname(void); + +#define LESS16(A, W) do { if (A < ((uint64_t)1 << (W))) LESS8(A, (W) - 8); LESS8(A, (W) + 8); } while (0) +#define LESS8(A, W) do { if (A < ((uint64_t)1 << (W))) LESS4(A, (W) - 4); LESS4(A, (W) + 4); } while (0) +#define LESS4(A, W) do { if (A < ((uint64_t)1 << (W))) LESS2(A, (W) - 2); LESS2(A, (W) + 2); } while (0) +#define LESS2(A, W) do { if (A < ((uint64_t)1 << (W))) LESS1(A, (W) - 1); LESS1(A, (W) + 1); } while (0) +#define LESS1(A, W) do { if (A < ((uint64_t)1 << (W))) return (W) - 1; return (W); } while (0) + +uint32_t CFLog2(uint64_t x) { + if (x < ((uint64_t)1 << 32)) + LESS16(x, 16); + LESS16(x, 48); + return 0; +} + +#if 0 +// faster version for PPC +int Lg2d(unsigned x) { +// use PPC-specific instruction to count leading zeros + int ret; + if (0 == x) return 0; + __asm__ volatile("cntlzw %0,%1" : "=r" (ret) : "r" (x)); + return 31 - ret; +} +#endif + +#undef LESS1 +#undef LESS2 +#undef LESS4 +#undef LESS8 +#undef LESS16 + +/* Comparator is passed the address of the values. */ +/* Binary searches a sorted-increasing array of some type. + Return value is either 1) the index of the element desired, + if the target value exists in the list, 2) greater than or + equal to count, if the element is greater than all the values + in the list, or 3) the index of the element greater than the + target value. + + For example, a search in the list of integers: + 2 3 5 7 11 13 17 + + For... Will Return... + 2 0 + 5 2 + 23 7 + 1 0 + 9 4 + + For instance, if you just care about found/not found: + index = CFBSearch(list, count, elem); + if (count <= index || list[index] != elem) { + * Not found * + } else { + * Found * + } + + This isn't optimal yet. +*/ +__private_extern__ CFIndex CFBSearch(const void *element, CFIndex elementSize, const void *list, CFIndex count, CFComparatorFunction comparator, void *context) { + SInt32 idx, lg; + const char *ptr = (const char *)list; + if (count < 4) { + switch (count) { + case 3: if (comparator(ptr + elementSize * 2, element, context) < 0) return 3; + case 2: if (comparator(ptr + elementSize * 1, element, context) < 0) return 2; + case 1: if (comparator(ptr + elementSize * 0, element, context) < 0) return 1; + } + return 0; + } + if (comparator(ptr + elementSize * (count - 1), element, context) < 0) return count; + if (comparator(element, ptr + elementSize * 0, context) < 0) return 0; + lg = CFLog2(count); /* This takes about 1/3rd of the time, discounting calls to comparator */ + idx = (comparator(ptr + elementSize * (-1 + (1 << lg)), element, context) < 0) ? count - (1 << lg) : -1; + switch (--lg) { + case 30: if (comparator(ptr + elementSize * (idx + (1 << 30)), element, context) < 0) idx += (1 << 30); + case 29: if (comparator(ptr + elementSize * (idx + (1 << 29)), element, context) < 0) idx += (1 << 29); + case 28: if (comparator(ptr + elementSize * (idx + (1 << 28)), element, context) < 0) idx += (1 << 28); + case 27: if (comparator(ptr + elementSize * (idx + (1 << 27)), element, context) < 0) idx += (1 << 27); + case 26: if (comparator(ptr + elementSize * (idx + (1 << 26)), element, context) < 0) idx += (1 << 26); + case 25: if (comparator(ptr + elementSize * (idx + (1 << 25)), element, context) < 0) idx += (1 << 25); + case 24: if (comparator(ptr + elementSize * (idx + (1 << 24)), element, context) < 0) idx += (1 << 24); + case 23: if (comparator(ptr + elementSize * (idx + (1 << 23)), element, context) < 0) idx += (1 << 23); + case 22: if (comparator(ptr + elementSize * (idx + (1 << 22)), element, context) < 0) idx += (1 << 22); + case 21: if (comparator(ptr + elementSize * (idx + (1 << 21)), element, context) < 0) idx += (1 << 21); + case 20: if (comparator(ptr + elementSize * (idx + (1 << 20)), element, context) < 0) idx += (1 << 20); + case 19: if (comparator(ptr + elementSize * (idx + (1 << 19)), element, context) < 0) idx += (1 << 19); + case 18: if (comparator(ptr + elementSize * (idx + (1 << 18)), element, context) < 0) idx += (1 << 18); + case 17: if (comparator(ptr + elementSize * (idx + (1 << 17)), element, context) < 0) idx += (1 << 17); + case 16: if (comparator(ptr + elementSize * (idx + (1 << 16)), element, context) < 0) idx += (1 << 16); + case 15: if (comparator(ptr + elementSize * (idx + (1 << 15)), element, context) < 0) idx += (1 << 15); + case 14: if (comparator(ptr + elementSize * (idx + (1 << 14)), element, context) < 0) idx += (1 << 14); + case 13: if (comparator(ptr + elementSize * (idx + (1 << 13)), element, context) < 0) idx += (1 << 13); + case 12: if (comparator(ptr + elementSize * (idx + (1 << 12)), element, context) < 0) idx += (1 << 12); + case 11: if (comparator(ptr + elementSize * (idx + (1 << 11)), element, context) < 0) idx += (1 << 11); + case 10: if (comparator(ptr + elementSize * (idx + (1 << 10)), element, context) < 0) idx += (1 << 10); + case 9: if (comparator(ptr + elementSize * (idx + (1 << 9)), element, context) < 0) idx += (1 << 9); + case 8: if (comparator(ptr + elementSize * (idx + (1 << 8)), element, context) < 0) idx += (1 << 8); + case 7: if (comparator(ptr + elementSize * (idx + (1 << 7)), element, context) < 0) idx += (1 << 7); + case 6: if (comparator(ptr + elementSize * (idx + (1 << 6)), element, context) < 0) idx += (1 << 6); + case 5: if (comparator(ptr + elementSize * (idx + (1 << 5)), element, context) < 0) idx += (1 << 5); + case 4: if (comparator(ptr + elementSize * (idx + (1 << 4)), element, context) < 0) idx += (1 << 4); + case 3: if (comparator(ptr + elementSize * (idx + (1 << 3)), element, context) < 0) idx += (1 << 3); + case 2: if (comparator(ptr + elementSize * (idx + (1 << 2)), element, context) < 0) idx += (1 << 2); + case 1: if (comparator(ptr + elementSize * (idx + (1 << 1)), element, context) < 0) idx += (1 << 1); + case 0: if (comparator(ptr + elementSize * (idx + (1 << 0)), element, context) < 0) idx += (1 << 0); + } + return ++idx; +} + + +#define ELF_STEP(B) T1 = (H << 4) + B; T2 = T1 & 0xF0000000; if (T2) T1 ^= (T2 >> 24); T1 &= (~T2); H = T1; + +CFHashCode CFHashBytes(uint8_t *bytes, CFIndex length) { + /* The ELF hash algorithm, used in the ELF object file format */ + UInt32 H = 0, T1, T2; + SInt32 rem = length; + while (3 < rem) { + ELF_STEP(bytes[length - rem]); + ELF_STEP(bytes[length - rem + 1]); + ELF_STEP(bytes[length - rem + 2]); + ELF_STEP(bytes[length - rem + 3]); + rem -= 4; + } + switch (rem) { + case 3: ELF_STEP(bytes[length - 3]); + case 2: ELF_STEP(bytes[length - 2]); + case 1: ELF_STEP(bytes[length - 1]); + case 0: ; + } + return H; +} + +#undef ELF_STEP + +#if defined(__WIN32__) +struct _args { + void *func; + void *arg; + HANDLE handle; +}; +static __stdcall unsigned __CFWinThreadFunc(void *arg) { + struct _args *args = arg; + ((void (*)(void *))args->func)(args->arg); + CloseHandle(args->handle); + CFAllocatorDeallocate(kCFAllocatorSystemDefault, arg); + _endthreadex(0); + return 0; +} +#endif + +__private_extern__ void *__CFStartSimpleThread(void *func, void *arg) { +#if defined(__MACH__) || defined(__LINUX__) || defined(__FREEBSD__) + pthread_attr_t attr; + pthread_t tid; + pthread_attr_init(&attr); + pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_attr_setstacksize(&attr, 60 * 1024); // 60K stack for our internal threads is sufficient + pthread_create(&tid, &attr, func, arg); + pthread_attr_destroy(&attr); +//warning CF: we dont actually know that a pthread_t is the same size as void * + return (void *)tid; +#else + unsigned tid; + struct _args *args = CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(struct _args), 0); + if (__CFOASafe) __CFSetLastAllocationEventName(args, "CFUtilities (thread-args)"); + HANDLE handle; + args->func = func; + args->arg = arg; + /* The thread is created suspended, because otherwise there would be a race between the assignment below of the handle field, and it's possible use in the thread func above. */ + args->handle = (HANDLE)_beginthreadex(NULL, 0, (void *)__CFWinThreadFunc, args, CREATE_SUSPENDED, &tid); + handle = args->handle; + ResumeThread(handle); + return handle; +#endif +} + +__private_extern__ CFStringRef _CFCreateLimitedUniqueString() { + /* this unique string is only unique to the current host during the current boot */ + uint64_t tsr = __CFReadTSR(); + UInt32 tsrh = (tsr >> 32), tsrl = (tsr & (int64_t)0xFFFFFFFF); + return CFStringCreateWithFormat(NULL, NULL, CFSTR("CFUniqueString-%lu%lu$"), tsrh, tsrl); +} + + +// Looks for localized version of "nonLocalized" in the SystemVersion bundle +// If not found, and returnNonLocalizedFlag == true, will return the non localized string (retained of course), otherwise NULL +// If bundlePtr != NULL, will use *bundlePtr and will return the bundle in there; otherwise bundle is created and released + +static CFStringRef _CFCopyLocalizedVersionKey(CFBundleRef *bundlePtr, CFStringRef nonLocalized) { + CFStringRef localized = NULL; + CFBundleRef locBundle = bundlePtr ? *bundlePtr : NULL; + if (!locBundle) { + CFURLRef url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, CFSTR("/System/Library/CoreServices/SystemVersion.bundle"), kCFURLPOSIXPathStyle, false); + if (url) { + locBundle = CFBundleCreate(kCFAllocatorDefault, url); + CFRelease(url); + } + } + if (locBundle) { + localized = CFBundleCopyLocalizedString(locBundle, nonLocalized, nonLocalized, CFSTR("SystemVersion")); + if (bundlePtr) *bundlePtr = locBundle; else CFRelease(locBundle); + } + return localized ? localized : CFRetain(nonLocalized); +} + +// Note, if this function is changed to cache the computed data using frozen named data support, it should really be called once per path (that is why the results are cached below in the functions calling this). +// +static CFDictionaryRef _CFCopyVersionDictionary(CFStringRef path) { + CFPropertyListRef plist = NULL; + CFDataRef data; + CFURLRef url; + + url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, path, kCFURLPOSIXPathStyle, false); + if (url && CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, url, &data, NULL, NULL, NULL)) { + plist = CFPropertyListCreateFromXMLData(kCFAllocatorSystemDefault, data, kCFPropertyListMutableContainers, NULL); + CFRelease(data); + } + if (url) CFRelease(url); + + if (plist) { + CFBundleRef locBundle = NULL; + CFStringRef fullVersion, vers, versExtra, build; + CFStringRef versionString = _CFCopyLocalizedVersionKey(&locBundle, _kCFSystemVersionProductVersionStringKey); + CFStringRef buildString = _CFCopyLocalizedVersionKey(&locBundle, _kCFSystemVersionBuildStringKey); + CFStringRef fullVersionString = _CFCopyLocalizedVersionKey(&locBundle, CFSTR("FullVersionString")); + if (locBundle) CFRelease(locBundle); + + // Now build the full version string + if (CFEqual(fullVersionString, CFSTR("FullVersionString"))) { + CFRelease(fullVersionString); + fullVersionString = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@ %%@ (%@ %%@)"), versionString, buildString); + } + vers = CFDictionaryGetValue(plist, _kCFSystemVersionProductVersionKey); + versExtra = CFDictionaryGetValue(plist, _kCFSystemVersionProductVersionExtraKey); + if (vers && versExtra) vers = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@ %@"), vers, versExtra); + build = CFDictionaryGetValue(plist, _kCFSystemVersionBuildVersionKey); + fullVersion = CFStringCreateWithFormat(NULL, NULL, fullVersionString, (vers ? vers : CFSTR("?")), build ? build : CFSTR("?")); + if (vers && versExtra) CFRelease(vers); + + CFDictionarySetValue((CFMutableDictionaryRef)plist, _kCFSystemVersionProductVersionStringKey, versionString); + CFDictionarySetValue((CFMutableDictionaryRef)plist, _kCFSystemVersionBuildStringKey, buildString); + CFDictionarySetValue((CFMutableDictionaryRef)plist, CFSTR("FullVersionString"), fullVersion); + CFRelease(versionString); + CFRelease(buildString); + CFRelease(fullVersionString); + CFRelease(fullVersion); + } + return plist; +} + +CFStringRef CFCopySystemVersionString(void) { + CFStringRef versionString; + CFDictionaryRef dict = _CFCopyServerVersionDictionary(); + if (!dict) dict = _CFCopySystemVersionDictionary(); + versionString = CFDictionaryGetValue(dict, CFSTR("FullVersionString")); + if (versionString) CFRetain(versionString); + CFRelease(dict); + return versionString; +} + +// These two functions cache the dictionaries to avoid calling _CFCopyVersionDictionary() more than once per dict desired + +CFDictionaryRef _CFCopySystemVersionDictionary(void) { + static CFPropertyListRef plist = NULL; // Set to -1 for failed lookup + if (!plist) { + plist = _CFCopyVersionDictionary(CFSTR("/System/Library/CoreServices/SystemVersion.plist")); + if (!plist) plist = (CFPropertyListRef)(-1); + } + return (plist == (CFPropertyListRef)(-1)) ? NULL : CFRetain(plist); +} + +CFDictionaryRef _CFCopyServerVersionDictionary(void) { + static CFPropertyListRef plist = NULL; // Set to -1 for failed lookup + if (!plist) { + plist = _CFCopyVersionDictionary(CFSTR("/System/Library/CoreServices/ServerVersion.plist")); + if (!plist) plist = (CFPropertyListRef)(-1); + } + return (plist == (CFPropertyListRef)(-1)) ? NULL : CFRetain(plist); +} + +CONST_STRING_DECL(_kCFSystemVersionProductNameKey, "ProductName") +CONST_STRING_DECL(_kCFSystemVersionProductCopyrightKey, "ProductCopyright") +CONST_STRING_DECL(_kCFSystemVersionProductVersionKey, "ProductVersion") +CONST_STRING_DECL(_kCFSystemVersionProductVersionExtraKey, "ProductVersionExtra") +CONST_STRING_DECL(_kCFSystemVersionProductUserVisibleVersionKey, "ProductUserVisibleVersion") +CONST_STRING_DECL(_kCFSystemVersionBuildVersionKey, "ProductBuildVersion") +CONST_STRING_DECL(_kCFSystemVersionProductVersionStringKey, "Version") +CONST_STRING_DECL(_kCFSystemVersionBuildStringKey, "Build") + +#if defined(__MACH__) +CFLibraryVersion CFGetExecutableLinkedLibraryVersion(CFStringRef libraryName) { + CFLibraryVersion ret = {0xFFFF, 0xFF, 0xFF}; + struct mach_header *mh; + struct load_command *lc; + unsigned int idx; + char library[CFMaxPathSize]; // search specs larger than this are pointless + + if (!CFStringGetCString(libraryName, library, sizeof(library), kCFStringEncodingUTF8)) return ret; + mh = _dyld_get_image_header(0); // image header #0 is the executable + if (NULL == mh) return ret; + lc = (struct load_command *)((char *)mh + sizeof(struct mach_header)); + for (idx = 0; idx < mh->ncmds; idx++) { + if (lc->cmd == LC_LOAD_DYLIB) { + struct dylib_command *dl = (struct dylib_command *)lc; + char *path = (char *)lc + dl->dylib.name.offset; + if (NULL != strstr(path, library)) { + ret.primaryVersion = dl->dylib.current_version >> 16; + ret.secondaryVersion = (dl->dylib.current_version >> 8) & 0xff; + ret.tertiaryVersion = dl->dylib.current_version & 0xff; + return ret; + } + } + lc = (struct load_command *)((char *)lc + lc->cmdsize); + } + return ret; +} + +CFLibraryVersion CFGetExecutingLibraryVersion(CFStringRef libraryName) { + CFLibraryVersion ret = {0xFFFF, 0xFF, 0xFF}; + struct mach_header *mh; + struct load_command *lc; + unsigned int idx1, idx2, cnt; + char library[CFMaxPathSize]; // search specs larger than this are pointless + + if (!CFStringGetCString(libraryName, library, sizeof(library), kCFStringEncodingUTF8)) return ret; + cnt = _dyld_image_count(); + for (idx1 = 1; idx1 < cnt; idx1++) { + char *image_name = _dyld_get_image_name(idx1); + if (NULL == image_name || NULL == strstr(image_name, library)) continue; + mh = _dyld_get_image_header(idx1); + if (NULL == mh) return ret; + lc = (struct load_command *)((char *)mh + sizeof(struct mach_header)); + for (idx2 = 0; idx2 < mh->ncmds; idx2++) { + if (lc->cmd == LC_ID_DYLIB) { + struct dylib_command *dl = (struct dylib_command *)lc; + ret.primaryVersion = dl->dylib.current_version >> 16; + ret.secondaryVersion = (dl->dylib.current_version >> 8) & 0xff; + ret.tertiaryVersion = dl->dylib.current_version & 0xff; + return ret; + } + } + } + return ret; +} + + +/* +If + (vers != 0xFFFF): We know the version number of the library this app was linked against + and (versionInfo[version].VERSIONFIELD != 0xFFFF): And we know what version number started the specified release + and ((version == 0) || (versionInfo[version-1].VERSIONFIELD < versionInfo[version].VERSIONFIELD)): And it's distinct from the prev release +Then + If the version the app is linked against is less than the version recorded for the specified release + Then stop checking and return false + Else stop checking and return YES +Else + Continue checking (the next library) +*/ +#define checkLibrary(LIBNAME, VERSIONFIELD) \ + {int vers = CFGetExecutableLinkedLibraryVersion(LIBNAME).primaryVersion; \ + if ((vers != 0xFFFF) && (versionInfo[version].VERSIONFIELD != 0xFFFF) && ((version == 0) || (versionInfo[version-1].VERSIONFIELD < versionInfo[version].VERSIONFIELD))) return (results[version] = ((vers < versionInfo[version].VERSIONFIELD) ? false : true)); } + +CF_EXPORT Boolean _CFExecutableLinkedOnOrAfter(CFSystemVersion version) { + // The numbers in the below table should be the numbers for any version of the framework in the release. + // When adding new entries to this table for a new build train, it's simplest to use the versions of the + // first new versions of projects submitted to the new train. These can later be updated. One thing to watch for is that software updates + // for the previous release do not increase numbers beyond the number used for the next release! + // For a given train, don't ever use the last versions submitted to the previous train! (This to assure room for software updates.) + // If versions are the same as previous release, use 0xFFFF; this will assure the answer is a conservative NO. + // NOTE: Also update the CFM check below, perhaps to the previous release... (???) + static const struct { + uint16_t libSystemVersion; + uint16_t cocoaVersion; + uint16_t appkitVersion; + uint16_t fouVersion; + uint16_t cfVersion; + uint16_t carbonVersion; + uint16_t applicationServicesVersion; + uint16_t coreServicesVersion; + uint16_t iokitVersion; + } versionInfo[] = { + {50, 5, 577, 397, 196, 113, 16, 9, 52}, /* CFSystemVersionCheetah (used the last versions) */ + {55, 7, 620, 425, 226, 122, 16, 10, 67}, /* CFSystemVersionPuma (used the last versions) */ + {56, 8, 631, 431, 232, 122, 17, 11, 73}, /* CFSystemVersionJaguar */ + {67, 9, 704, 481, 281, 126, 19, 16, 159}, /* CFSystemVersionPanther */ + {0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF} /* CFSystemVersionMerlot */ + }; + static char results[CFSystemVersionMax] = {-2, -2, -2, -2, -2}; /* We cache the results per-release; there are only a few of these... */ + if (version >= CFSystemVersionMax) return false; /* Actually, we don't know the answer, and something scary is going on */ + if (results[version] != -2) return results[version]; + + if (_CFIsCFM()) { + results[version] = (version <= CFSystemVersionJaguar) ? true : false; + return results[version]; + } + + checkLibrary(CFSTR("/libSystem."), libSystemVersion); // Pretty much everyone links with this + checkLibrary(CFSTR("/Cocoa."), cocoaVersion); + checkLibrary(CFSTR("/AppKit."), appkitVersion); + checkLibrary(CFSTR("/Foundation."), fouVersion); + checkLibrary(CFSTR("/CoreFoundation."), cfVersion); + checkLibrary(CFSTR("/Carbon."), carbonVersion); + checkLibrary(CFSTR("/ApplicationServices."), applicationServicesVersion); + checkLibrary(CFSTR("/CoreServices."), coreServicesVersion); + checkLibrary(CFSTR("/IOKit."), iokitVersion); + + /* If not found, then simply return NO to indicate earlier --- compatibility by default, unfortunately */ + return false; +} +#endif + + + + +void CFShow(const void *obj) { // ??? supposed to use stderr for Logging ? + CFStringRef str; + CFIndex idx, cnt; + CFStringInlineBuffer buffer; + bool lastNL = false; + + if (obj) { + if (CFGetTypeID(obj) == CFStringGetTypeID()) { + // Makes Ali marginally happier + str = __CFCopyFormattingDescription(obj, NULL); + if (!str) str = CFCopyDescription(obj); + } else { + str = CFCopyDescription(obj); + } + } else { + str = CFRetain(CFSTR("(null)")); + } + cnt = CFStringGetLength(str); + + CFStringInitInlineBuffer(str, &buffer, CFRangeMake(0, cnt)); + for (idx = 0; idx < cnt; idx++) { + UniChar ch = __CFStringGetCharacterFromInlineBufferQuick(&buffer, idx); + if (ch < 128) { + fprintf (stderr, "%c", ch); + lastNL = (ch == '\n'); + } else { + fprintf (stderr, "\\u%04x", ch); + } + } + if (!lastNL) fprintf(stderr, "\n"); + + if (str) CFRelease(str); +} + +void CFLog(int p, CFStringRef format, ...) { + CFStringRef result; + va_list argList; + static CFSpinLock_t lock = 0; + + va_start(argList, format); + result = CFStringCreateWithFormatAndArguments(NULL, NULL, format, argList); + va_end(argList); + + __CFSpinLock(&lock); +#if defined(__WIN32__) + printf("*** CFLog (%d): %s[%d] ", p, __argv[0], GetCurrentProcessId()); +#else + // Date format: YYYY '-' MM '-' DD ' ' hh ':' mm ':' ss.fff + CFTimeZoneRef tz = CFTimeZoneCopySystem(); // specifically choose system time zone for logs + CFGregorianDate gdate = CFAbsoluteTimeGetGregorianDate(CFAbsoluteTimeGetCurrent(), tz); + CFRelease(tz); + gdate.second = gdate.second + 0.0005; + fprintf(stderr, "%04d-%02d-%02d %02d:%02d:%06.3f %s[%d] CFLog (%d): ", (int)gdate.year, gdate.month, gdate.day, gdate.hour, gdate.minute, gdate.second, *_CFGetProgname(), getpid(), p); +#endif + + CFShow(result); + __CFSpinUnlock(&lock); + CFRelease(result); +} + diff --git a/Base.subproj/CFUtilities.h b/Base.subproj/CFUtilities.h new file mode 100644 index 0000000..7d002f8 --- /dev/null +++ b/Base.subproj/CFUtilities.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFUtilities.h + Copyright (c) 1998-2003, Apple, Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFUTILITIES__) +#define __COREFOUNDATION_CFUTILITIES__ 1 + +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +CF_EXPORT uint32_t CFLog2(uint64_t x); + +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); + +#if defined(__MACH__) +typedef struct { + uint16_t primaryVersion; + uint8_t secondaryVersion; + uint8_t tertiaryVersion; +} CFLibraryVersion; + +CF_EXPORT CFLibraryVersion CFGetExecutableLinkedLibraryVersion(CFStringRef libraryName); +CF_EXPORT CFLibraryVersion CFGetExecutingLibraryVersion(CFStringRef libraryName); + +/* _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. +*/ +typedef enum { + CFSystemVersionCheetah = 0, /* 10.0 */ + CFSystemVersionPuma = 1, /* 10.1 */ + CFSystemVersionJaguar = 2, /* 10.2? TBD */ + CFSystemVersionPanther = 3, /* Post-Jaguar */ + CFSystemVersionPinot = 3, /* Deprecated name for Panther */ + CFSystemVersionMerlot = 4, /* Post-Panther */ + CFSystemVersionMax /* This should bump up when new entries are added */ +} CFSystemVersion; + +CF_EXPORT Boolean _CFExecutableLinkedOnOrAfter(CFSystemVersion version); + +#endif /* __MACH__ */ + + +#if defined(__cplusplus) +} +#endif + +#endif /* ! __COREFOUNDATION_CFUTILITIES__ */ + diff --git a/Base.subproj/CoreFoundation.h b/Base.subproj/CoreFoundation.h new file mode 100644 index 0000000..b8bee17 --- /dev/null +++ b/Base.subproj/CoreFoundation.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CoreFoundation.h + Copyright (c) 1998-2003, Apple, Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_COREFOUNDATION__) +#define __COREFOUNDATION_COREFOUNDATION__ 1 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(__STDC_VERSION__) && (199901L <= __STDC_VERSION__) + +#include +//#include +#include +#include + +#endif + +#include +#include +#include +#include +#include +#include +#include + +#if defined(__MACH__) +#include +#endif + +#if defined(__MACH__) || defined(__WIN32__) +#include +#include +#include +#include +#endif + + +#endif /* ! __COREFOUNDATION_COREFOUNDATION__ */ + diff --git a/Base.subproj/ForFoundationOnly.h b/Base.subproj/ForFoundationOnly.h new file mode 100644 index 0000000..e7a6e50 --- /dev/null +++ b/Base.subproj/ForFoundationOnly.h @@ -0,0 +1,339 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* ForFoundationOnly.h + Copyright (c) 1998-2003, Apple, Inc. All rights reserved. +*/ + +#if !CF_BUILDING_CF && !NSBUILDINGFOUNDATION + #error The header file ForFoundationOnly.h is for the exclusive use of the + #error CoreFoundation and Foundation projects. No other project should include it. +#endif + +#if !defined(__COREFOUNDATION_FORFOUNDATIONONLY__) +#define __COREFOUNDATION_FORFOUNDATIONONLY__ 1 + +#include +#include +#include +#include +#include + +// NOTE: miscellaneous declarations are at the end + + +// ---- CFBundle material ---------------------------------------- + +#include "CFBundlePriv.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +CF_EXPORT const CFStringRef _kCFBundleExecutablePathKey; +CF_EXPORT const CFStringRef _kCFBundleInfoPlistURLKey; +CF_EXPORT const CFStringRef _kCFBundleNumericVersionKey; +CF_EXPORT const CFStringRef _kCFBundleResourcesFileMappedKey; +CF_EXPORT const CFStringRef _kCFBundleCFMLoadAsBundleKey; +CF_EXPORT const CFStringRef _kCFBundleAllowMixedLocalizationsKey; + + +CF_EXPORT CFArrayRef _CFBundleCopyLanguageSearchListInDirectory(CFAllocatorRef alloc, CFURLRef url, UInt8 *version); +CF_EXPORT CFArrayRef _CFBundleGetLanguageSearchList(CFBundleRef bundle); + +#if defined(__cplusplus) +} +#endif + + +// ---- CFString material ---------------------------------------- + +#if defined(__cplusplus) +extern "C" { +#endif + +/* Create a byte stream from a CFString backing. Can 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 char specified by lossByte; if 0, then lossy conversion is not allowed + and conversion stops, returning partial results. + generatingExternalFile indicates that any extra stuff to allow this data to be + persistent (for instance, BOM) should be included. + Pass buffer==NULL if you don't care about the converted string (but just the + convertability, or number of bytes required, indicated by usedBufLen). + Does not zero-terminate. If you want to create Pascal or C string, allow one + extra byte at start or end. +*/ +CF_EXPORT CFIndex __CFStringEncodeByteStream(CFStringRef string, CFIndex rangeLoc, CFIndex rangeLen, Boolean generatingExternalFile, CFStringEncoding encoding, char lossByte, UInt8 *buffer, CFIndex max, CFIndex *usedBufLen); + +CF_INLINE Boolean __CFStringEncodingIsSupersetOfASCII(CFStringEncoding encoding) { + switch (encoding & 0x0000FF00) { + case 0x0: // MacOS Script range + return true; + + case 0x100: // Unicode range + if (encoding == kCFStringEncodingUnicode) return false; + return true; + + case 0x200: // ISO range + return true; + + case 0x600: // National standards range + if (encoding != kCFStringEncodingASCII) return false; + return true; + + case 0x800: // ISO 2022 range + return false; // It's modal encoding + + case 0xA00: // Misc standard range + return true; + + case 0xB00: + if (encoding == kCFStringEncodingNonLossyASCII) return false; + return true; + + case 0xC00: // EBCDIC + return false; + + default: + return ((encoding & 0x0000FF00) > 0x0C00 ? false : true); + } +} + +/* Desperately using extern here */ +CF_EXPORT CFStringEncoding __CFDefaultEightBitStringEncoding; +CF_EXPORT CFStringEncoding __CFStringComputeEightBitStringEncoding(void); + +CF_INLINE CFStringEncoding __CFStringGetEightBitStringEncoding(void) { + if (__CFDefaultEightBitStringEncoding == kCFStringEncodingInvalidId) __CFStringComputeEightBitStringEncoding(); + return __CFDefaultEightBitStringEncoding; +} + +enum { + __kCFVarWidthLocalBufferSize = 1008 +}; + +typedef struct { /* A simple struct to maintain ASCII/Unicode versions of the same buffer. */ + union { + UInt8 *ascii; + UniChar *unicode; + } chars; + Boolean isASCII; /* This really does mean 7-bit ASCII, not _NSDefaultCStringEncoding() */ + Boolean shouldFreeChars; /* If the number of bytes exceeds __kCFVarWidthLocalBufferSize, bytes are allocated */ + Boolean _unused1; + Boolean _unused2; + CFAllocatorRef allocator; /* Use this allocator to allocate, reallocate, and deallocate the bytes */ + UInt32 numChars; /* This is in terms of ascii or unicode; that is, if isASCII, it is number of 7-bit chars; otherwise it is number of UniChars; note that the actual allocated space might be larger */ + UInt8 localBuffer[__kCFVarWidthLocalBufferSize]; /* private; 168 ISO2022JP chars, 504 Unicode chars, 1008 ASCII chars */ +} CFVarWidthCharBuffer; + + +/* Convert a byte stream to ASCII (7-bit!) or Unicode, with a CFVarWidthCharBuffer struct on the stack. false return indicates an error occured during the conversion. Depending on .isASCII, follow .chars.ascii or .chars.unicode. If .shouldFreeChars is returned as true, free the returned buffer when done with it. If useClientsMemoryPtr is provided as non-NULL, and the provided memory can be used as is, this is set to true, and the .ascii or .unicode buffer in CFVarWidthCharBuffer is set to bytes. +!!! If the stream is Unicode and has no BOM, the data is assumed to be big endian! Could be trouble on Intel if someone didn't follow that assumption. +!!! __CFStringDecodeByteStream2() needs to be deprecated and removed post-Jaguar. +*/ +CF_EXPORT Boolean __CFStringDecodeByteStream2(const UInt8 *bytes, UInt32 len, CFStringEncoding encoding, Boolean alwaysUnicode, CFVarWidthCharBuffer *buffer, Boolean *useClientsMemoryPtr); +CF_EXPORT Boolean __CFStringDecodeByteStream3(const UInt8 *bytes, UInt32 len, CFStringEncoding encoding, Boolean alwaysUnicode, CFVarWidthCharBuffer *buffer, Boolean *useClientsMemoryPtr, UInt32 converterFlags); + + +/* Convert single byte to Unicode; assumes one-to-one correspondence (that is, can only be used with 1-byte encodings). You can use the function if it's not NULL. The table is always safe to use; calling __CFSetCharToUniCharFunc() updates it. +*/ +CF_EXPORT Boolean (*__CFCharToUniCharFunc)(UInt32 flags, UInt8 ch, UniChar *unicodeChar); +CF_EXPORT void __CFSetCharToUniCharFunc(Boolean (*func)(UInt32 flags, UInt8 ch, UniChar *unicodeChar)); +CF_EXPORT UniChar __CFCharToUniCharTable[256]; + +/* Character class functions UnicodeData-2_1_5.txt +*/ +CF_INLINE Boolean __CFIsWhitespace(UniChar theChar) { + return ((theChar < 0x21) || (theChar > 0x7E && theChar < 0xA1) || (theChar >= 0x2000 && theChar <= 0x200B) || (theChar == 0x3000)) ? true : false; +} + +/* Same as CFStringGetCharacterFromInlineBuffer() but returns 0xFFFF on out of bounds access +*/ +CF_INLINE UniChar __CFStringGetCharacterFromInlineBufferAux(CFStringInlineBuffer *buf, CFIndex idx) { + if (buf->directBuffer) { + if (idx < 0 || idx >= buf->rangeToBuffer.length) return 0xFFFF; + return buf->directBuffer[idx + buf->rangeToBuffer.location]; + } + if (idx >= buf->bufferedRangeEnd || idx < buf->bufferedRangeStart) { + if (idx < 0 || idx >= buf->rangeToBuffer.length) return 0xFFFF; + 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]; +} + +/* Same as CFStringGetCharacterFromInlineBuffer(), but without the bounds checking (will return garbage or crash) +*/ +CF_INLINE UniChar __CFStringGetCharacterFromInlineBufferQuick(CFStringInlineBuffer *buf, CFIndex idx) { + if (buf->directBuffer) return buf->directBuffer[idx + buf->rangeToBuffer.location]; + if (idx >= buf->bufferedRangeEnd || idx < buf->bufferedRangeStart) { + 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]; +} + + +/* These two allow specifying an alternate description function (instead of CFCopyDescription); used by NSString +*/ +CF_EXPORT void _CFStringAppendFormatAndArgumentsAux(CFMutableStringRef outputString, CFStringRef (*copyDescFunc)(void *, CFDictionaryRef), CFDictionaryRef formatOptions, CFStringRef formatString, va_list args); +CF_EXPORT CFStringRef _CFStringCreateWithFormatAndArgumentsAux(CFAllocatorRef alloc, CFStringRef (*copyDescFunc)(void *, CFDictionaryRef), CFDictionaryRef formatOptions, CFStringRef format, va_list arguments); + +enum {_CFStringErrNone = 0, _CFStringErrNotMutable = 1, _CFStringErrNilArg = 2, _CFStringErrBounds = 3}; + +CF_EXPORT Boolean __CFStringNoteErrors(void); // Should string errors raise? + +#if defined(__cplusplus) +} +#endif + + +// ---- Binary plist material ---------------------------------------- + +typedef const struct __CFKeyedArchiverUID * CFKeyedArchiverUIDRef; +extern CFTypeID _CFKeyedArchiverUIDGetTypeID(void); +extern CFKeyedArchiverUIDRef _CFKeyedArchiverUIDCreate(CFAllocatorRef allocator, uint32_t value); +extern uint32_t _CFKeyedArchiverUIDGetValue(CFKeyedArchiverUIDRef uid); + + +enum { + kCFBinaryPlistMarkerNull = 0x00, + kCFBinaryPlistMarkerFalse = 0x08, + kCFBinaryPlistMarkerTrue = 0x09, + kCFBinaryPlistMarkerFill = 0x0F, + kCFBinaryPlistMarkerInt = 0x10, + kCFBinaryPlistMarkerReal = 0x20, + kCFBinaryPlistMarkerDate = 0x33, + kCFBinaryPlistMarkerData = 0x40, + kCFBinaryPlistMarkerASCIIString = 0x50, + kCFBinaryPlistMarkerUnicode16String = 0x60, + kCFBinaryPlistMarkerUID = 0x80, + kCFBinaryPlistMarkerArray = 0xA0, + kCFBinaryPlistMarkerDict = 0xD0 +}; + +typedef struct { + uint8_t _magic[6]; + uint8_t _version[2]; +} CFBinaryPlistHeader; + +typedef struct { + uint8_t _unused[6]; + uint8_t _offsetIntSize; + uint8_t _objectRefSize; + uint64_t _numObjects; + uint64_t _topObject; + uint64_t _offsetTableOffset; +} CFBinaryPlistTrailer; + + +// ---- Miscellaneous material ---------------------------------------- + +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +CF_EXPORT CFTypeID CFTypeGetTypeID(void); + +CF_EXPORT void _CFArraySetCapacity(CFMutableArrayRef array, CFIndex cap); +CF_EXPORT void _CFBagSetCapacity(CFMutableBagRef bag, CFIndex cap); +CF_EXPORT void _CFDictionarySetCapacity(CFMutableDictionaryRef dict, CFIndex cap); +CF_EXPORT void _CFSetSetCapacity(CFMutableSetRef set, CFIndex cap); + +CF_EXPORT void _CFArrayReplaceValues(CFMutableArrayRef array, CFRange range, const void **newValues, CFIndex newCount); + + +/* For use by NSNumber and CFNumber. + Hashing algorithm for CFNumber: + M = Max CFHashCode (assumed to be unsigned) + For positive integral values: N mod M + For negative integral values: (-N) mod M + For floating point numbers that are not integral: hash(integral part) + hash(float part * M) +*/ +CF_INLINE CFHashCode _CFHashInt(int i) { + return (i > 0) ? (CFHashCode)(i) : (CFHashCode)(-i); +} + +CF_INLINE CFHashCode _CFHashDouble(double d) { + double dInt; + if (d < 0) d = -d; + dInt = rint(d); + return (CFHashCode)(fmod(dInt, (double)0xFFFFFFFF) + ((d - dInt) * 0xFFFFFFFF)); +} + + +typedef void (*CFRunLoopPerformCallBack)(void *info); + + +#if defined(__MACH__) +#include +CF_INLINE UInt64 __CFReadTSR(void) { + return mach_absolute_time(); +} +#else +CF_INLINE UInt64 __CFReadTSR(void) { + union { + UInt64 time64; + UInt32 word[2]; + } now; +#if defined(__i386__) + /* Read from Pentium and Pentium Pro 64-bit timestamp counter. */ + /* The counter is set to 0 at processor reset and increments on */ + /* every clock cycle. */ + __asm__ volatile("rdtsc" : : : "eax", "edx"); + __asm__ volatile("movl %%eax,%0" : "=m" (now.word[0]) : : "eax"); + __asm__ volatile("movl %%edx,%0" : "=m" (now.word[1]) : : "edx"); +#elif defined(__ppc__) + /* Read from PowerPC 64-bit time base register. The increment */ + /* rate of the time base is implementation-dependent, but is */ + /* 1/4th the bus clock cycle on 603/604/750 processors. */ + UInt32 t3; + do { + __asm__ volatile("mftbu %0" : "=r" (now.word[0])); + __asm__ volatile("mftb %0" : "=r" (now.word[1])); + __asm__ volatile("mftbu %0" : "=r" (t3)); + } while (now.word[0] != t3); +#else +// ??? Do not know how to read a time stamp register on this architecture + now.time64 = (uint64_t)0; +#endif + return now.time64; +} +#endif + +#if defined(__cplusplus) +} +#endif + +#endif /* ! __COREFOUNDATION_FORFOUNDATIONONLY__ */ + diff --git a/Base.subproj/uuid.c b/Base.subproj/uuid.c new file mode 100644 index 0000000..490d2c7 --- /dev/null +++ b/Base.subproj/uuid.c @@ -0,0 +1,1297 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* uuid.c + Copyright 1999-2002, Apple, Inc. All rights reserved. + Responsibility: Doug Davidson +*/ + +#if defined(__WIN32__) +/* _CFGenerateUUID function just calls the COM library's UUID generator + * (Aleksey Dukhnyakov) + */ +#include +#include +#include + +unsigned long _CFGenerateUUID(uuid_t *uuid) { + RPC_STATUS rStatus; + + /* call GetScode() function to get RPC_STATUS, because + * CoCreateGuid(uuid) function return HRESULT type + */ + rStatus = GetScode(CoCreateGuid(uuid)); + + /* We accept only following results RPC_S_OK, RPC_S_UUID_LOCAL_ONLY + */ + if ( rStatus == RPC_S_UUID_NO_ADDRESS) + return rStatus; + + return 0; +}; + +#else + +/* uuid.c + * + * Modifications made by William Woody to make this thing + * work on the Macintosh. + */ + +/* + * + * (c) Copyright 1989 OPEN SOFTWARE FOUNDATION, INC. + * (c) Copyright 1989 HEWLETT-PACKARD COMPANY + * (c) Copyright 1989 DIGITAL EQUIPMENT CORPORATION + * To anyone who acknowledges that this file is provided "AS IS" + * without any express or implied warranty: + * permission to use, copy, modify, and distribute this + * file for any purpose is hereby granted without fee, provided that + * the above copyright notices and this notice appears in all source + * code copies, and that none of the names of Open Software + * Foundation, Inc., Hewlett-Packard Company, or Digital Equipment + * Corporation be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior + * permission. Neither Open Software Foundation, Inc., Hewlett- + * Packard Company, nor Digital Equipment Corporation makes any + * representations about the suitability of this software for any + * purpose. + * + */ +/* + */ +/* +** +** NAME: +** +** uuid.c +** +** FACILITY: +** +** UUID +** +** ABSTRACT: +** +** UUID - routines that manipulate uuid's +** +** +*/ + +#include +#include +#include "CFInternal.h" +#include + + +/* uuid_t already defined in RPCDCE.H (WIN32 header) + * (Aleksey Dukhnyakov) + */ +#if defined(__WIN32__) +#define UUID_T_DEFINED 1 +#endif + +#if !defined(UUID_T_DEFINED) +#define UUID_T_DEFINED 1 + +/* uuid + * + * Universal Unique ID. Note this definition will result is a 16-byte + * structure regardless what platform it is on. + */ + +struct uuid_t { + unsigned long time_low; + unsigned short time_mid; + unsigned short time_hi_and_version; + unsigned char clock_seq_hi_and_reserved; + unsigned char clock_seq_low; + unsigned char node[6]; +}; + +typedef struct uuid_t uuid_t; + +#endif + +enum { + kUUIDInternalError = -21001, + kUUIDInvalidString = -21002 +}; + +extern unsigned long _CFGenerateUUID(uuid_t *uuid); + +typedef struct +{ + unsigned char eaddr[6]; /* 6 bytes of ethernet hardware address */ +} uuid_address_t; + +typedef struct { + unsigned long lo; + unsigned long hi; +} uuid_time_t; + +static OSErr GenRandomEthernet(uuid_address_t *addr); +static OSErr GetEthernetAddr(uuid_address_t *addr); + +static OSErr ReadPrefData(void); + +/* + * Preferences file management + */ + +static uuid_address_t GSavedENetAddr = {{0, 0, 0, 0, 0, 0}}; +static uuid_time_t GLastTime = {0, 0}; /* Clock state info */ +static unsigned short GTimeAdjust = 0; +static unsigned short GClockSeq = 0; + + +/* + * Internal structure of universal unique IDs (UUIDs). + * + * There are three "variants" of UUIDs that this code knows about. The + * variant #0 is what was defined in the 1989 HP/Apollo Network Computing + * Architecture (NCA) specification and implemented in NCS 1.x and DECrpc + * v1. Variant #1 is what was defined for the joint HP/DEC specification + * for the OSF (in DEC's "UID Architecture Functional Specification Version + * X1.0.4") and implemented in NCS 2.0, DECrpc v2, and OSF 1.0 DCE RPC. + * Variant #2 is defined by Microsoft. + * + * This code creates only variant #1 UUIDs. + * + * The three UUID variants can exist on the same wire because they have + * distinct values in the 3 MSB bits of octet 8 (see table below). Do + * NOT confuse the version number with these 3 bits. (Note the distinct + * use of the terms "version" and "variant".) Variant #0 had no version + * field in it. Changes to variant #1 (should any ever need to be made) + * can be accomodated using the current form's 4 bit version field. + * + * The UUID record structure MUST NOT contain padding between fields. + * The total size = 128 bits. + * + * To minimize confusion about bit assignment within octets, the UUID + * record definition is defined only in terms of fields that are integral + * numbers of octets. + * + * Depending on the network data representation, the multi-octet unsigned + * integer fields are subject to byte swapping when communicated between + * dissimilar endian machines. Note that all three UUID variants have + * the same record structure; this allows this byte swapping to occur. + * (The ways in which the contents of the fields are generated can and + * do vary.) + * + * The following information applies to variant #1 UUIDs: + * + * The lowest addressed octet contains the global/local bit and the + * unicast/multicast bit, and is the first octet of the address transmitted + * on an 802.3 LAN. + * + * The adjusted time stamp is split into three fields, and the clockSeq + * is split into two fields. + * + * |<------------------------- 32 bits -------------------------->| + * + * +--------------------------------------------------------------+ + * | low 32 bits of time | 0-3 .time_low + * +-------------------------------+------------------------------- + * | mid 16 bits of time | 4-5 .time_mid + * +-------+-----------------------+ + * | vers. | hi 12 bits of time | 6-7 .time_hi_and_version + * +-------+-------+---------------+ + * |Res| clkSeqHi | 8 .clock_seq_hi_and_reserved + * +---------------+ + * | clkSeqLow | 9 .clock_seq_low + * +---------------+----------...-----+ + * | node ID | 8-16 .node + * +--------------------------...-----+ + * + * -------------------------------------------------------------------------- + * + * The structure layout of all three UUID variants is fixed for all time. + * I.e., the layout consists of a 32 bit int, 2 16 bit ints, and 8 8 + * bit ints. The current form version field does NOT determine/affect + * the layout. This enables us to do certain operations safely on the + * variants of UUIDs without regard to variant; this increases the utility + * of this code even as the version number changes (i.e., this code does + * NOT need to check the version field). + * + * The "Res" field in the octet #8 is the so-called "reserved" bit-field + * and determines whether or not the uuid is a old, current or other + * UUID as follows: + * + * MS-bit 2MS-bit 3MS-bit Variant + * --------------------------------------------- + * 0 x x 0 (NCS 1.5) + * 1 0 x 1 (DCE 1.0 RPC) + * 1 1 0 2 (Microsoft) + * 1 1 1 unspecified + * + * -------------------------------------------------------------------------- + * + * Internal structure of variant #0 UUIDs + * + * The first 6 octets are the number of 4 usec units of time that have + * passed since 1/1/80 0000 GMT. The next 2 octets are reserved for + * future use. The next octet is an address family. The next 7 octets + * are a host ID in the form allowed by the specified address family. + * + * Note that while the family field (octet 8) was originally conceived + * of as being able to hold values in the range [0..255], only [0..13] + * were ever used. Thus, the 2 MSB of this field are always 0 and are + * used to distinguish old and current UUID forms. + * + * +--------------------------------------------------------------+ + * | high 32 bits of time | 0-3 .time_high + * +-------------------------------+------------------------------- + * | low 16 bits of time | 4-5 .time_low + * +-------+-----------------------+ + * | reserved | 6-7 .reserved + * +---------------+---------------+ + * | family | 8 .family + * +---------------+----------...-----+ + * | node ID | 9-16 .node + * +--------------------------...-----+ + * + */ + +/*************************************************************************** + * + * Local definitions + * + **************************************************************************/ + +static const long uuid_c_version = 1; + +/* + * local defines used in uuid bit-diddling + */ +#define HI_WORD(w) ((w) >> 16) +#define RAND_MASK 0x3fff /* same as CLOCK_SEQ_LAST */ + +#define TIME_MID_MASK 0x0000ffff +#define TIME_HIGH_MASK 0x0fff0000 +#define TIME_HIGH_SHIFT_COUNT 16 + +/* + * The following was modified in order to prevent overlap because + * our clock is (theoretically) accurate to 1us (or 1s in CarbonLib) + */ + + +#define MAX_TIME_ADJUST 9 /* Max adjust before tick */ + +#define CLOCK_SEQ_LOW_MASK 0xff +#define CLOCK_SEQ_HIGH_MASK 0x3f00 +#define CLOCK_SEQ_HIGH_SHIFT_COUNT 8 +#define CLOCK_SEQ_FIRST 1 +#define CLOCK_SEQ_LAST 0x3fff /* same as RAND_MASK */ + +/* + * Note: If CLOCK_SEQ_BIT_BANG == true, then we can avoid the modulo + * operation. This should save us a divide instruction and speed + * things up. + */ + +#ifndef CLOCK_SEQ_BIT_BANG +#define CLOCK_SEQ_BIT_BANG 1 +#endif + +#if CLOCK_SEQ_BIT_BANG +#define CLOCK_SEQ_BUMP(seq) ((*seq) = ((*seq) + 1) & CLOCK_SEQ_LAST) +#else +#define CLOCK_SEQ_BUMP(seq) ((*seq) = ((*seq) + 1) % (CLOCK_SEQ_LAST+1)) +#endif + +#define UUID_VERSION_BITS (uuid_c_version << 12) +#define UUID_RESERVED_BITS 0x80 + +#define IS_OLD_UUID(uuid) (((uuid)->clock_seq_hi_and_reserved & 0xc0) != 0x80) + +/**************************************************************************** + * + * local data declarations + * + ****************************************************************************/ + +typedef struct { + unsigned long lo; + unsigned long hi; +} unsigned64_t; + +/* + * declarations used in UTC time calculations + */ + +static uuid_time_t time_now = {0, 0}; /* utc time as of last query */ +//static uuid_time_t time_last; /* utc time last time I looked */ +//static unsigned short time_adjust; /* 'adjustment' to ensure uniqness */ +//static unsigned short clock_seq; /* 'adjustment' for backwards clocks*/ + +/* + * true_random variables + */ + +static unsigned long rand_m = 0; /* multiplier */ +static unsigned long rand_ia = 0; /* adder #1 */ +static unsigned long rand_ib = 0; /* adder #2 */ +static unsigned long rand_irand = 0; /* random value */ + +typedef enum +{ + uuid_e_less_than, uuid_e_equal_to, uuid_e_greater_than +} uuid_compval_t; + + + + +/**************************************************************************** + * + * local function declarations + * + ****************************************************************************/ + +/* + * I N I T + * + * Startup initialization routine for UUID module. + */ + +static OSErr init (void); + +/* + * T R U E _ R A N D O M _ I N I T + */ + +static void true_random_init (void); + +/* + * T R U E _ R A N D O M + */ +static unsigned short true_random (void); + + +/* + * N E W _ C L O C K _ S E Q + * + * Ensure clock_seq is up-to-date + * + * Note: clock_seq is architected to be 14-bits (unsigned) but + * I've put it in here as 16-bits since there isn't a + * 14-bit unsigned integer type (yet) + */ +static void new_clock_seq ( unsigned short * /*clock_seq*/); + + +/* + * T I M E _ C M P + * + * Compares two UUID times (64-bit DEC UID UTC values) + */ +static uuid_compval_t time_cmp ( + uuid_time_t * /*time1*/, + uuid_time_t * /*time2*/ + ); + + +/************************************************************************/ +/* */ +/* New Routines */ +/* */ +/************************************************************************/ + +/* + * saved copy of our IEEE 802 address for quick reference + */ + +static uuid_address_t saved_addr = {{0, 0, 0, 0, 0, 0}}; +static int got_address = false; +static int last_addr_result = false; + + +/* +**++ +** +** ROUTINE NAME: uuid_get_address +** +** SCOPE: PUBLIC +** +** DESCRIPTION: +** +** Return our IEEE 802 address. +** +** This function is not really "public", but more like the SPI functions +** -- available but not part of the official API. We've done this so +** that other subsystems (of which there are hopefully few or none) +** that need the IEEE 802 address can use this function rather than +** duplicating the gore it does (or more specifically, the gore that +** "uuid__get_os_address" does). +** +** INPUTS: none +** +** INPUTS/OUTPUTS: none +** +** OUTPUTS: +** +** addr IEEE 802 address +** +** status return status value +** +** IMPLICIT INPUTS: none +** +** IMPLICIT OUTPUTS: none +** +** FUNCTION VALUE: none +** +** SIDE EFFECTS: none +** +**-- +**/ + +static int uuid_get_address(uuid_address_t *addr) +{ + + /* + * just return address we determined previously if we've + * already got one + */ + + if (got_address) { + memmove (addr, &saved_addr, sizeof (uuid_address_t)); + return last_addr_result; + } + + /* + * Otherwise, call the system specific routine. + */ + + last_addr_result = GetEthernetAddr(addr); + + /* + * Was this an error? If so, I need to generate a random + * sequence to use in place of an Ethernet address. + */ + if (last_addr_result) { + last_addr_result = GenRandomEthernet(addr); + } + + got_address = true; + if (last_addr_result == 0) { + /* On no error copy */ + memmove (&saved_addr, addr, sizeof (uuid_address_t)); + } + return last_addr_result; +} + +static OSErr GenRandomEthernet(uuid_address_t *addr) { + unsigned int i; + for (i = 0; i < 6; i++) { + addr->eaddr[i] = (unsigned char)(true_random() & 0xff); + } + return 0; +} + +__private_extern__ CFStringRef __CFCopyRegularEthernetAddrString(void) { + uuid_address_t addr; + static CFStringRef string = NULL; + static Boolean lookedUpAddr = false; + + if (!lookedUpAddr) { + // dont use the cache, since a random enet addr might have been put in it + if (GetEthernetAddr(&addr) == 0) { + string = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%02x:%02x:%02x:%02x:%02x:%02x"), addr.eaddr[0], addr.eaddr[1], addr.eaddr[2], addr.eaddr[3], addr.eaddr[4], addr.eaddr[5]); + } + lookedUpAddr = true; + } + return (string ? CFRetain(string) : NULL); +} + +__private_extern__ CFStringRef __CFCopyEthernetAddrString(void) { + uuid_address_t addr; + static CFStringRef string = NULL; + static Boolean lookedUpAddr = false; + + if (!lookedUpAddr) { + // dont use the cache, since a random enet addr might have been put in it + if (GetEthernetAddr(&addr) == 0) { + string = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%02x%02x%02x%02x%02x%02x"), addr.eaddr[0], addr.eaddr[1], addr.eaddr[2], addr.eaddr[3], addr.eaddr[4], addr.eaddr[5]); + } + lookedUpAddr = true; + } + return (string ? CFRetain(string) : NULL); +} + +/***************************************************************************** + * + * Macro definitions + * + ****************************************************************************/ + +/* + * ensure we've been initialized + */ +static int uuid_init_done = false; + +#define EmptyArg +#define UUID_VERIFY_INIT(Arg) \ + if (! uuid_init_done) \ + { \ + init (status); \ + if (*status != uuid_s_ok) \ + { \ + return Arg; \ + } \ + } + +/* + * Check the reserved bits to make sure the UUID is of the known structure. + */ + +#define CHECK_STRUCTURE(uuid) \ +( \ + (((uuid)->clock_seq_hi_and_reserved & 0x80) == 0x00) || /* var #0 */ \ + (((uuid)->clock_seq_hi_and_reserved & 0xc0) == 0x80) || /* var #1 */ \ + (((uuid)->clock_seq_hi_and_reserved & 0xe0) == 0xc0) /* var #2 */ \ +) + +/* + * The following macros invoke CHECK_STRUCTURE(), check that the return + * value is okay and if not, they set the status variable appropriately + * and return either a boolean false, nothing (for void procedures), + * or a value passed to the macro. This has been done so that checking + * can be done more simply and values are returned where appropriate + * to keep compilers happy. + * + * bCHECK_STRUCTURE - returns boolean false + * vCHECK_STRUCTURE - returns nothing (void) + * rCHECK_STRUCTURE - returns 'r' macro parameter + */ + +#define bCHECK_STRUCTURE(uuid, status) \ +{ \ + if (!CHECK_STRUCTURE (uuid)) \ + { \ + *(status) = uuid_s_bad_version; \ + return (false); \ + } \ +} + +#define vCHECK_STRUCTURE(uuid, status) \ +{ \ + if (!CHECK_STRUCTURE (uuid)) \ + { \ + *(status) = uuid_s_bad_version; \ + return; \ + } \ +} + +#define rCHECK_STRUCTURE(uuid, status, result) \ +{ \ + if (!CHECK_STRUCTURE (uuid)) \ + { \ + *(status) = uuid_s_bad_version; \ + return (result); \ + } \ +} + + +/* + * Define constant designation difference in Unix and DTSS base times: + * DTSS UTC base time is October 15, 1582. + * Unix base time is January 1, 1970. + */ +#define uuid_c_os_base_time_diff_lo 0x13814000 +#define uuid_c_os_base_time_diff_hi 0x01B21DD2 + +#ifndef UUID_C_100NS_PER_SEC +#define UUID_C_100NS_PER_SEC 10000000 +#endif + +#ifndef UUID_C_100NS_PER_USEC +#define UUID_C_100NS_PER_USEC 10 +#endif + + + + + +/* + * UADD_UVLW_2_UVLW - macro to add two unsigned 64-bit long integers + * (ie. add two unsigned 'very' long words) + * + * Important note: It is important that this macro accommodate (and it does) + * invocations where one of the addends is also the sum. + * + * This macro was snarfed from the DTSS group and was originally: + * + * UTCadd - macro to add two UTC times + * + * add lo and high order longword separately, using sign bits of the low-order + * longwords to determine carry. sign bits are tested before addition in two + * cases - where sign bits match. when the addend sign bits differ the sign of + * the result is also tested: + * + * sign sign + * addend 1 addend 2 carry? + * + * 1 1 true + * 1 0 true if sign of sum clear + * 0 1 true if sign of sum clear + * 0 0 false + */ +#define UADD_UVLW_2_UVLW(add1, add2, sum) \ + if (!(((add1)->lo&0x80000000UL) ^ ((add2)->lo&0x80000000UL))) \ + { \ + if (((add1)->lo&0x80000000UL)) \ + { \ + (sum)->lo = (add1)->lo + (add2)->lo ; \ + (sum)->hi = (add1)->hi + (add2)->hi+1 ; \ + } \ + else \ + { \ + (sum)->lo = (add1)->lo + (add2)->lo ; \ + (sum)->hi = (add1)->hi + (add2)->hi ; \ + } \ + } \ + else \ + { \ + (sum)->lo = (add1)->lo + (add2)->lo ; \ + (sum)->hi = (add1)->hi + (add2)->hi ; \ + if (!((sum)->lo&0x80000000UL)) \ + (sum)->hi++ ; \ + } + +/* + * UADD_UW_2_UVLW - macro to add a 16-bit unsigned integer to + * a 64-bit unsigned integer + * + * Note: see the UADD_UVLW_2_UVLW() macro + * + */ +#define UADD_UW_2_UVLW(add1, add2, sum) \ +{ \ + (sum)->hi = (add2)->hi; \ + if ((add2)->lo & 0x80000000UL) \ + { \ + (sum)->lo = (*add1) + (add2)->lo; \ + if (!((sum)->lo & 0x80000000UL)) \ + { \ + (sum)->hi++; \ + } \ + } \ + else \ + { \ + (sum)->lo = (*add1) + (add2)->lo; \ + } \ +} + +/* + * U U I D _ _ G E T _ O S _ T I M E + * + * Get OS time - contains platform-specific code. + */ + +static const double utc_conversion_factor = 429.4967296; // 2^32 / 10^7 + +static void uuid__get_os_time (uuid_time_t * uuid_time) +{ + unsigned64_t utc, + os_basetime_diff; + CFAbsoluteTime at = CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970; + double utc_at = at / utc_conversion_factor; + + /* Convert 'at' in double seconds to 100ns units in utc */ + utc.hi = (unsigned long)utc_at; + utc_at -= (double)utc.hi; + utc_at *= utc_conversion_factor; + utc_at *= 10000000.0; + utc.lo = (unsigned long)utc_at; + + /* + * Offset between DTSS formatted times and Unix formatted times. + */ + os_basetime_diff.lo = uuid_c_os_base_time_diff_lo; + os_basetime_diff.hi = uuid_c_os_base_time_diff_hi; + UADD_UVLW_2_UVLW (&utc, &os_basetime_diff, uuid_time); + +} + +/* +**++ +** +** ROUTINE NAME: init +** +** SCOPE: INTERNAL - declared locally +** +** DESCRIPTION: +** +** Startup initialization routine for the UUID module. +** +** INPUTS: none +** +** INPUTS/OUTPUTS: none +** +** OUTPUTS: +** +** status return status value +** +** uuid_s_ok +** uuid_s_coding_error +** +** IMPLICIT INPUTS: none +** +** IMPLICIT OUTPUTS: none +** +** FUNCTION VALUE: void +** +** SIDE EFFECTS: sets uuid_init_done so this won't be done again +** +**-- +**/ + +static OSErr init() +{ + /* + * init the random number generator + */ + + true_random_init(); + + /* + * Read the preferences data from the Macintosh pref file + */ + + ReadPrefData(); + + /* + * Get the time. Note that I renamed 'time_last' to + * GLastTime to indicate that I'm using it elsewhere as + * a shared library global. + */ + + if ((GLastTime.hi == 0) && (GLastTime.lo == 0)) { + uuid__get_os_time (&GLastTime); + GClockSeq = true_random(); + } + uuid_init_done = true; + return 0; +} + +/* +** New name: GenerateUID +** +**++ +** +** ROUTINE NAME: uuid_create +** +** SCOPE: PUBLIC - declared in UUID.IDL +** +** DESCRIPTION: +** +** Create a new UUID. Note: we only know how to create the new +** and improved UUIDs. +** +** INPUTS: none +** +** INPUTS/OUTPUTS: none +** +** OUTPUTS: +** +** uuid A new UUID value +** +** status return status value +** +** uuid_s_ok +** uuid_s_coding_error +** +** IMPLICIT INPUTS: none +** +** IMPLICIT OUTPUTS: none +** +** FUNCTION VALUE: void +** +** SIDE EFFECTS: none +** +**-- +**/ + +/* +PUBLIC void uuid_create +#ifdef _DCE_PROTO_ +( + uuid_t *uuid, + unsigned long *status +) +#else +(uuid, status) +uuid_t *uuid; +unsigned long *status; +#endif +*/ + +__private_extern__ unsigned long _CFGenerateUUID(uuid_t *uuid) +{ + OSErr err; + uuid_address_t eaddr; + int got_no_time = false; + + if (!uuid_init_done) { + err = init(); + if (err) return err; + } + /* + * get our hardware network address + */ + + if (0 != (err = uuid_get_address(&eaddr))) return err; + + do + { + /* + * get the current time + */ + uuid__get_os_time (&time_now); + + /* + * do stuff like: + * + * o check that our clock hasn't gone backwards and handle it + * accordingly with clock_seq + * o check that we're not generating uuid's faster than we + * can accommodate with our time_adjust fudge factor + */ + switch (time_cmp (&time_now, &GLastTime)) + { + case uuid_e_less_than: + new_clock_seq (&GClockSeq); + GTimeAdjust = 0; + break; + case uuid_e_greater_than: + GTimeAdjust = 0; + break; + case uuid_e_equal_to: + if (GTimeAdjust == MAX_TIME_ADJUST) + { + /* + * spin your wheels while we wait for the clock to tick + */ + got_no_time = true; + } + else + { + GTimeAdjust++; + } + break; + default: + return kUUIDInternalError; + } + } while (got_no_time); + + GLastTime.lo = time_now.lo; + GLastTime.hi = time_now.hi; + + if (GTimeAdjust != 0) + { + UADD_UW_2_UVLW (>imeAdjust, &time_now, &time_now); + } + + /* + * now construct a uuid with the information we've gathered + * plus a few constants + */ + uuid->time_low = time_now.lo; + uuid->time_mid = time_now.hi & TIME_MID_MASK; + + uuid->time_hi_and_version = + (time_now.hi & TIME_HIGH_MASK) >> TIME_HIGH_SHIFT_COUNT; + uuid->time_hi_and_version |= UUID_VERSION_BITS; + + uuid->clock_seq_low = GClockSeq & CLOCK_SEQ_LOW_MASK; + uuid->clock_seq_hi_and_reserved = + (GClockSeq & CLOCK_SEQ_HIGH_MASK) >> CLOCK_SEQ_HIGH_SHIFT_COUNT; + + uuid->clock_seq_hi_and_reserved |= UUID_RESERVED_BITS; + + memmove (uuid->node, &eaddr, sizeof (uuid_address_t)); + + return 0; +} + +/***************************************************************************** + * + * LOCAL MATH PROCEDURES - math procedures used internally by the UUID module + * + ****************************************************************************/ + +/* +** T I M E _ C M P +** +** Compares two UUID times (64-bit UTC values) +**/ + +static uuid_compval_t time_cmp(uuid_time_t *time1,uuid_time_t *time2) +{ + /* + * first check the hi parts + */ + if (time1->hi < time2->hi) return (uuid_e_less_than); + if (time1->hi > time2->hi) return (uuid_e_greater_than); + + /* + * hi parts are equal, check the lo parts + */ + if (time1->lo < time2->lo) return (uuid_e_less_than); + if (time1->lo > time2->lo) return (uuid_e_greater_than); + + return (uuid_e_equal_to); +} + + + +/**************************************************************************** +** +** U U I D T R U E R A N D O M N U M B E R G E N E R A T O R +** +***************************************************************************** +** +** This random number generator (RNG) was found in the ALGORITHMS Notesfile. +** +** (Note 16.7, July 7, 1989 by Robert (RDVAX::)Gries, Cambridge Research Lab, +** Computational Quality Group) +** +** It is really a "Multiple Prime Random Number Generator" (MPRNG) and is +** completely discussed in reference #1 (see below). +** +** References: +** 1) "The Multiple Prime Random Number Generator" by Alexander Hass +** pp. 368 to 381 in ACM Transactions on Mathematical Software, +** December, 1987 +** 2) "The Art of Computer Programming: Seminumerical Algorithms +** (vol 2)" by Donald E. Knuth, pp. 39 to 113. +** +** A summary of the notesfile entry follows: +** +** Gries discusses the two RNG's available for ULTRIX-C. The default RNG +** uses a Linear Congruential Method (very popular) and the second RNG uses +** a technique known as a linear feedback shift register. +** +** The first (default) RNG suffers from bit-cycles (patterns/repetition), +** ie. it's "not that random." +** +** While the second RNG passes all the emperical tests, there are "states" +** that become "stable", albeit contrived. +** +** Gries then presents the MPRNG and says that it passes all emperical +** tests listed in reference #2. In addition, the number of calls to the +** MPRNG before a sequence of bit position repeats appears to have a normal +** distribution. +** +** Note (mbs): I have coded the Gries's MPRNG with the same constants that +** he used in his paper. I have no way of knowing whether they are "ideal" +** for the range of numbers we are dealing with. +** +****************************************************************************/ + +/* +** T R U E _ R A N D O M _ I N I T +** +** Note: we "seed" the RNG with the bits from the clock and the PID +** +**/ + +static void true_random_init (void) +{ + uuid_time_t t; + unsigned short *seedp, seed=0; + + + /* + * optimal/recommended starting values according to the reference + */ + static unsigned long rand_m_init = 971; + static unsigned long rand_ia_init = 11113; + static unsigned long rand_ib_init = 104322; + static unsigned long rand_irand_init = 4181; + + rand_m = rand_m_init; + rand_ia = rand_ia_init; + rand_ib = rand_ib_init; + rand_irand = rand_irand_init; + + /* + * Generating our 'seed' value + * + * We start with the current time, but, since the resolution of clocks is + * system hardware dependent (eg. Ultrix is 10 msec.) and most likely + * coarser than our resolution (10 usec) we 'mixup' the bits by xor'ing + * all the bits together. This will have the effect of involving all of + * the bits in the determination of the seed value while remaining system + * independent. Then for good measure to ensure a unique seed when there + * are multiple processes creating UUID's on a system, we add in the PID. + */ + uuid__get_os_time(&t); + seedp = (unsigned short *)(&t); + seed ^= *seedp++; + seed ^= *seedp++; + seed ^= *seedp++; + seed ^= *seedp++; + rand_irand += seed; +} + +/* +** T R U E _ R A N D O M +** +** Note: we return a value which is 'tuned' to our purposes. Anyone +** using this routine should modify the return value accordingly. +**/ + +static unsigned short true_random (void) +{ + rand_m += 7; + rand_ia += 1907; + rand_ib += 73939; + + if (rand_m >= 9973) rand_m -= 9871; + if (rand_ia >= 99991) rand_ia -= 89989; + if (rand_ib >= 224729) rand_ib -= 96233; + + rand_irand = (rand_irand * rand_m) + rand_ia + rand_ib; + + return (HI_WORD (rand_irand) ^ (rand_irand & RAND_MASK)); +} + +/***************************************************************************** + * + * LOCAL PROCEDURES - procedures used staticly by the UUID module + * + ****************************************************************************/ + +/* +** N E W _ C L O C K _ S E Q +** +** Ensure *clkseq is up-to-date +** +** Note: clock_seq is architected to be 14-bits (unsigned) but +** I've put it in here as 16-bits since there isn't a +** 14-bit unsigned integer type (yet) +**/ + +static void new_clock_seq +#ifdef _DCE_PROTO_ +( + unsigned short *clkseq +) +#else +(clkseq) +unsigned short *clkseq; +#endif +{ + /* + * A clkseq value of 0 indicates that it hasn't been initialized. + */ + if (*clkseq == 0) + { +#ifdef UUID_NONVOLATILE_CLOCK + *clkseq = uuid__read_clock(); /* read nonvolatile clock */ + if (*clkseq == 0) /* still not init'd ??? */ + { + *clkseq = true_random(); /* yes, set random */ + } +#else + /* + * with a volatile clock, we always init to a random number + */ + *clkseq = true_random(); +#endif + } + + CLOCK_SEQ_BUMP (clkseq); + if (*clkseq == 0) + { + *clkseq = *clkseq + 1; + } + +#ifdef UUID_NONVOLATILE_CLOCK + uuid_write_clock (clkseq); +#endif +} + + + +/* ReadPrefData + * + * Read the preferences data into my global variables + */ + +static OSErr ReadPrefData(void) +{ + /* + * Zero out the saved preferences information + */ + + memset((void *)&GSavedENetAddr, 0, sizeof(GSavedENetAddr)); + memset((void *)&GLastTime, 0, sizeof(GLastTime)); + GTimeAdjust = 0; + GClockSeq = 0; + + return 0; +} + +#if 0 +// currently unused + +/* WritePrefData + * + * Write the preferences data back out to my global variables. + * This gets called a couple of times. First, this is called by + * my GetRandomEthernet routine if I generated a psudorandom MAC + * address. Second, this is called when the library is being + * terminated through the __terminate() CFM call. + * + * Note this does it's best attempt at writing the data out, + * and relies on ReadPrefData to check for integrety of the actual + * saved file. + */ + +static void WritePrefData(void) +{ +} + +#endif + + +#if defined(__MACH__) + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#if !defined(MAX) +#define MAX(a, b) ((a) < (b) ? (b) : (a)) +#endif + +#define IFR_NEXT(ifr) \ + ((struct ifreq *) ((char *) (ifr) + sizeof(*(ifr)) + \ + MAX(0, (int) (ifr)->ifr_addr.sa_len - (int) sizeof((ifr)->ifr_addr)))) + +static OSErr GetEthernetAddr(uuid_address_t *addr) { + struct ifconf ifc; + struct ifreq ifrbuf[30], *ifr; + register int s, i; + Boolean foundIt = false; + + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { + return -1; + } + + ifc.ifc_buf = (caddr_t)ifrbuf; + ifc.ifc_len = sizeof (ifrbuf); + if (ioctl(s, SIOCGIFCONF, &ifc) == -1) { + close(s); + return -1; + } + + for (ifr = (struct ifreq *)ifc.ifc_buf, i=0; (char *)ifr < &ifc.ifc_buf[ifc.ifc_len]; ifr = IFR_NEXT(ifr), i++) { + unsigned char *p, c; + + if (*ifr->ifr_name == '\0') { + continue; + } + /* + * Adapt to buggy kernel implementation (> 9 of a type) + */ + + p = &ifr->ifr_name[strlen(ifr->ifr_name)-1]; + if ((c = *p) > '0'+9) { + sprintf(p, "%d", c-'0'); + } + + if (strcmp(ifr->ifr_name, "en0") == 0) { + if (ifr->ifr_addr.sa_family == AF_LINK) { + struct sockaddr_dl *sa = ((struct sockaddr_dl *)&ifr->ifr_addr); + if (sa->sdl_type == IFT_ETHER || sa->sdl_type == IFT_FDDI || sa->sdl_type == IFT_ISO88023 || sa->sdl_type == IFT_ISO88024 || sa->sdl_type == IFT_ISO88025) { + for (i=0, p=&sa->sdl_data[sa->sdl_nlen] ; i++ < sa->sdl_alen; p++) { + addr->eaddr[i-1] = *p; + } + foundIt = true; + break; + } + } + } + } + close(s); + return (foundIt ? 0 : -1); +} + +#elif defined (__WIN32__) + +#error Dont know how to find Ethernet Address on Win32 +// MF:!!! Maybe on Windows we should just call the COM library's UUID generator... + +#elif defined (__LINUX__) || defined(__FREEBSD__) + +static OSErr GetEthernetAddr(uuid_address_t *addr) { + return -1; +} + +#endif + +#undef HI_WORD +#undef RAND_MASK +#undef TIME_MID_MASK +#undef TIME_HIGH_MASK +#undef TIME_HIGH_SHIFT_COUNT +#undef MAX_TIME_ADJUST +#undef CLOCK_SEQ_LOW_MASK +#undef CLOCK_SEQ_HIGH_MASK +#undef CLOCK_SEQ_HIGH_SHIFT_COUNT +#undef CLOCK_SEQ_FIRST +#undef CLOCK_SEQ_LAST +#undef CLOCK_SEQ_BIT_BANG +#undef CLOCK_SEQ_BUMP +#undef UUID_VERSION_BITS +#undef UUID_RESERVED_BITS +#undef IS_OLD_UUID +#undef EmptyArg +#undef UUID_VERIFY_INIT +#undef CHECK_STRUCTURE +#undef bCHECK_STRUCTURE +#undef vCHECK_STRUCTURE +#undef rCHECK_STRUCTURE +#undef uuid_c_os_base_time_diff_lo +#undef uuid_c_os_base_time_diff_hi +#undef UUID_C_100NS_PER_SEC +#undef UUID_C_100NS_PER_USEC +#undef UADD_UVLW_2_UVLW +#undef UADD_UW_2_UVLW +#undef IFR_NEXT + +#endif + diff --git a/CharacterSets/CFCharacterSetBitmaps.bitmap b/CharacterSets/CFCharacterSetBitmaps.bitmap new file mode 100644 index 0000000000000000000000000000000000000000..fc7f13aaf33f304ac8525adf537ea5fcf8868e92 GIT binary patch literal 352454 zcmeI*Uu+!5eZcX#J4%#dNk^x(tH6Tq(k4jaG!O$9iB*f*Lr}m$9s;E?(T5fx(YR<{ zg6bh?gGlgRYPB+u0$5fmS_`= z=<>+v?>Do1yLUSANb!&Ebl;(GcV>2WcII>3<5{yi`$wZj!;D*+QpUK~WY(AqIUlbN zxpk(v$FC>%8Y9~?)qQTAExym?XD9FT>FPmaipFH?vX=UC#Y28QnfL1pBYs`K->rv= z@AvD;-|_1Uzw7oNsz&R&tfihk#RuHlHPlMm`aj&-i(7NH(#xuC-FvI)@)qlBZB2V$ zlXMbm-6NgaNSSo`JBqLM)~}iG^TvAK{$Afv|Jexv1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009IL*pk5dtEA5Wi4r z$*o&!hDw*LUD2YUE-HClro-tt5o#pJ$vzUKWj4&}(n?(Rf8{|v-mo?|(5=Ig%9!Xg zU5ixaczCWdJ2zI==9ufXRVjaCwl+2? zwHb5GrnA~0?$3c0{T zcDwMZ)sDzJT+6$dzBMkW)XHz?q)c+vh2{nR{mf`Q3eUt5#g0NQj3VJ!IHpmz)I=y- zx1P~Lcb>m?Qx;_O{p`;J6Q{e@}^E!%w}eNM^w69k$q%V}L{+}hg2LSa5{3kz)> z%9fMrrI`4gZ5%yu@<%Ig)>9@)8Djk|FsU7AoH!hP&dHk|{yK@cmhkr&Nh`%*-5&;A@C$OPU#2_lw9N|ok5t`+?T@5ELw^XEqY0E7+T@vz@ zJuV@`zZ%>jWMonz$>&+kZAvZNw`m!@abx?{WJB0!G>&K$8GFMG&b}-|gL07MFj3hK zUD8q;4^mI)YK;aECGCzA*O0WS8Ee@dm)ogzAmi_HA;R-JRX_D2fB*srAbWu-5zMmKUp+xESQ)Pw*6eJPNYk3#|i2q1s}0tg_0 z00IagfB*srAbU&Fh2fW7x~4WCrG^8)V7h^F{xBvXAf+wISm^zt|P zR4Qxb2^sQSgJ>z!e7IDtv|#2Xw$QA{N77FKh&KJ#KKUdV`TZ&vOtx8(L;wK<5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1P~ZR0eJ#IPjtIEw%^to8IA9O;6^!npdB;;0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKwvurI!gH4J~x%n zU3HRwxz)6$mCFsMdY`V;wCd~S2>{#i)=>un2;2(-?H35h2d0}rYutzF=_g&};}?H+ z`TM{8`HT0@d~f=5K#=rZBrXI+Q zkLwn__1`r|6NDWW(Dy|DS`vC+K+lra@PIpyqP$G=-v7W%OI*GJxu1e5n7q6S-FkTT zv5%ZMId)&Q*sUPj5kLR|1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0U{?g%$M(|) zz3USIxC;Q%Eh3Hp0tg_000IagfB*srAb3s zh*RsY5Bbfn+u&hyK;m(F>R7oYeeReR%r&=H9ycx9&mXI50W;*5raCuPcCo0wYHe)N zrS%vRUT;fR=cDq`c4N*?Sbqd9{_b$u{cY2@hIb#5N502FL@&JR6Kl3+tj?xTD8*ZK zy*MXj?$u|!ACV5~OXXrc`7U7dMaeMcF}G}8=&wvF*u2hAG-#pqoEfy<4^)2}Exp~_ zk|Kcs0tg_000IagfB*srAbvRpP$x3ziuNx4hd&Q~{ZfAmAH6(gnkCD~Ue zb2)R^<$UXuyF*`Yxt5rzHru+_%r_d1pzCx1y2F{~UP)w+*_R}?8q2<4YmiGVQsJ#>diy zOX&phz7o(Aur1t|O6O#jq(IZMoYs}bt*uR5vGX^qUD(hGR*L6M@G=KXNg{Qh7?D%s zq(uK&&fq=uM{|$r8N2AW<>T*(gLzANbfSBUo?o6EAOBAF@~4a$DVb*cxhHzQ-82vZ z1Q0*~0R#|0009ILKmdV05b*yV!x8}m5I|rz1h#U+Y#;ndp@|3}uuTG8KaYa{3IFPN zR4O&eeCqHQOe37Q+%iFXYU^E&whcSq<<@X`1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|00D;{Us1}l=Ei-VGU=Tw90R;L{pzUGj{n!QCi~s@%Abpwk<)mYcOX zQz*6M)~z)|rAyYXXwgs?mAo#~;dGn`H4?}S=Gr1@nGLhLv=W#7UwKfEH>}MKbnCFB zGImxA#NkHGuG*<)3$AKCacV;k7I5X=F|C!y9~A7owB`ELd)BVLwlsCDJTq^Xa@Wsk z`m1l8J7&IAwFPs%BCT6}d#Unx#w6w6UOIoQIx}mt=GxrY#lv%z*}1W@DGT@Y+NzYl zFLTDJQ{`ka!!1%YPEvgHW?3x)Z- zEiANkXz7`j-vT+k&NhyoIQgTMH|x1&S9^yhY@35SK)RRmo4#$8yNdK(+{qqnZ) zwz?c-&7LSkpL609{yK@TT?lL%~C zU_&2>K~&0*p0^w`G|P3nDo+4tsYnMH{dihS_U#k$l|9}lgJK%oQDkINBFX1j&2368 z>&ROi$0so?wQnpBp@ z0dxq9e&&UyOtC{5m*sWax9)4t*h+eBr}-vb>*SXr3XjE+$vBc)pK?=D#1m7bHgt@R zcFesOGUXFcCg`|f|5ZCPDL>mE{5hMd_f#sri_x|ZgX$g3Ya;Wu#J|P5ci;b79Fm8d z$a6x9(wAn4J1803dj2;LKm-8<5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0-=x;}wk z#cYRI$fxc8tb`rXU$1H4ZU-L&F7XkzYBXHfB*srAbamE(cq1IhU zD#;jI)87=$&rAGZ>ex20Ir$0Yl?1=NDdE~$5VQO{S|)JKkC|NAW@3jTNz)|O;fD2= z?LLt{r=)K|pxLsV)|JNfiAXg&f5X~^&?Vhss+?~uDV6j~0VGqVNQk!Iuz9)lU4kfX zQ}CA_X=QRqlRDOB>z7}vW(c(HskhHukuJI4_?MmteZhiexL(Tpy7M8}pq4^Cozu++XSqV5E~6t=G@c4!Dozvu1hz#W`s=(cbx{cd z2q1s}0tg_0z`ZVz{$J9b>ubTcNd5_liKmY**5I_I{1Q0*~0R#|0009ILKmY**5I|t}1lq=4 z+LHc81HI+2LvZ6o-CCOL7j`U~Y_G-9+EZ8TTQV%=Lgi|$CXv$<4L4BZGrKG|bYIu3 zucTw6$V31E1O`AL+vT&A%?KcX00IagfB*srAbY{M7V4P6MeP80faQ*pmGd#EqlPNNYRSOIJFXGDH9_~pQ2&~XG1 zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1nxb7k$vWr^r|G!&w0u4qPj(|)VIl*u-Ggu&1lANA4xyt zB+#}etvBY%lI*A@UV0*2b6ptG3^}BgC!R=jlR=spA5Ysk-*xvOcH5n`v}Yx0lYcf$ zl1|68VKzTv4w#IoR#UAzoQijd&f0XQ)ELS~z0FH460c}n-&MY=BH2q3UA zf%uf#SZPWjfB*srAb{0e(s~C`*O1h^yO(sySeV}6C$FNcid=P>%B#3mQ8VC)?KU?%u!^Y27k3(oFxg z#6In7ZRT~4l-Dg%?|MDMy1V!7#`b&JUD{t9l7LI^%A5A?TatUJ;iv3PTaj;4$xZU> z^{&gL6mHiG(TP%O4FA13RsPky}T^Df#5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0-=9|S_PY}|a><@E4}PEqa0)6%)?#AUE1-WpwuqeBST&?Mb(-V!=czf4%zH137;3qL?xtXcgt!N(t2q3T<0u}r8 z33>h4+J+76J5Mjavm|SGd$7rs<+jnftL=}dRc`<679Mg1MoRTdvTs9PXZ(Yv zzjZ2}l0{;s+N@@;nU`x6g7m|@o7>tWXWBC)y~oY}(qY4$WM$v4d$sA~rtIpEcof9}YKK`BT zb5HEt0jU`Q1Q0*~0R#|0009ILKmdWk5%B*W!x8}m5I|rz1iFt_4bI;mdWrx7J1TI& zG{T9?ak|=%3npJQ&&pE{hTYTA>k?~*pg37IGJAne93$)7;WB@-gVu0>C&2A@sD7UeV^N#x|+cq zE8~wdKK+PR{Xk}XT&vR?`(1O^t(%Pq44Oc60;X0E#OJK$WsEg}ymUD&OSz87{S=JM zoguHT7YM0m>3~;t6KVM94lk>6zVGlMs2%|X5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~fj$*zAKOoNV5m4`H>D>-p~JK+4m#+eYU#_z#S|5LVQ47Ka!oEZtm!& z0pPyi#Df?D{%lDvf0Iw8vdR7b?#}y*34~_3Ct8z??YB1Pu1^5qn&U<#TN_3S z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1nx{Axk#X>6|6e}z~Y2X z0C3dZYs>@ycjnDr2y9g#b+dmIWdeY$nzBCv2q1s}0tg_000IagfB*srAbK$)xj{03fYF903FnKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~fo&2fYCUCLG(*PsR+HFU$H5nzr&`aB%`A#^Kx~;)Ntmf*8aqtn6);r*2vWexxMYR-KDe*QV0I}sm5)tlt>l2CAw_wd(x{+ z@am@vf1F!<)HH1Ei{BVMC6`NOMt}GLd4`I$^A%l&C(oRE_UJ7Y^jd~px&wt?>jdpV z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**21pagRVuse(#2Fxk4M)T zyz;#$_jm_r-dzwFy$i?e(L*juyUzHw5rI@Imf>#yf-d#tSA zvSwGTedWQZSl;+ay)(;J+MI57)!H#BRMy?3Evdg;RV})~5Y1gavi8A;%>mgbYQ)IH zMdQ-(_J2I473NHw{_zL>_Ne@Ghl(XFV20dMT3U=EepUU+*kn56xl$X{p<;To#E1SO zmGSOyHB2SVyAPE-i)M=Gg{T>8Va-^bq`Xi{7q7pj+ix$QF57qUBhq4h>AR;d4KqP$ z?R?QiA9KsrCtgHaev2{ZGDC;r_+42q`g3U;-=GV*y3wCWvnapPXMC?(Cje+YXYNYB zIp~fH6!i#=6X^%OJe@k|sZ^XO0tg_000IagfB*srAb(b&El_pZk6kwG6O0EjVIBY*$`2q1s}0tg_000IagfB*srAbPx# literal 0 HcmV?d00001 diff --git a/CharacterSets/CFUniCharPropertyDatabase.data b/CharacterSets/CFUniCharPropertyDatabase.data new file mode 100644 index 0000000000000000000000000000000000000000..4381d43c1be2ce6daf96a62454ab56c1a1886d0e GIT binary patch literal 17688 zcmeGjNpc%E5D+&FP1@FG+0Je|iL*IQ;%xuOEf4S^cOP<#DzEbkSgt9R%cWu|KIIAN zZY%>}FaS6!8M8GS^oHKB%n+2GJ!J^D89N^{_GCOdJ3yKrjK&i-ogHTL8;d1!7`u53 zv=EL&qwmKW+j-=Ku~WA8!F3LBy~2C`@?~)+MxJv7#8uya*Mi7%v_}%E&fB;Dz@@aP z-TA7up8(dSeXF}_ktP-1F6H!MIG+HA>+))iOB5F8(eVj8J-frs+1-1r&Fax4t!MDP z_dodXqmMs%fGePKL?8AtE+Pv^1Cg9sh9yjJ{wS=2!+;m0h!>g17O-%~|Dkl|=9B@W z$4YqpyD@c0@BKT)#ql2!w#EN%zxPswq32&I$wBMiuk+{6ZGV3Nl~B)I0ATC_9?=>62Kz*XpnO~aJ^XcSY;B)dYBBk+ zi5bx~oK?f-;dsAt=%LBWq_^pzqkQN5uh*R8haNbg5SuEl0l@P$Z&-_0SJ#)XUpF+8 z-u>_}UAHsM`<+?d6~j1}b|uz*oKS(L%e;#su2!%ef#EkR1m%IwwcsXBY2Y|T3H)t0 zGja5I%1$^OrJdis$M4G{EIi-k4<0`Hl#tIpfBeOlU$L*hfw-P<{_WH6`1e1M#CLn< zV>ajg*xZEA!cUW*fBAK;;KmY87ZoAe8k*`DN zA_v^KNWZsR&+fu`VU;*s?>NXYDa{!ujTexA06%aUl{=^g(ju{;KLJqWY{HmuMI7W$ zI4bx-C>1h0N@;YYAsdZAcUtIoh{;eUss)5Hn@sffJ@&ch^H_j(k`@KkDrzayk2bz2 z$~?o59<8Y9G=&BWkLH~U4ZR@)Y0I-iS~A(k;s4S@P;H3?21 z1EBpw7)Hb*0E5vGwT!#AV6yWmzGSj`;qOe#YJ`GwWe3Kxl{>`aR}n_P;cBl!tx|3&O`vE3y5Ht+{`yei;u>lZ$aY= za$q!+tD#z*Yrg@=A{%v;fk**vfdfs{q(T_u>qV1{Y^xF-gc-p>h6Y7%dJ&p03V>rb zPJa~aE7W3nbnE!`%>|yu8Q-${R51t@#{$ptL=56oZw!R^3`%AsjvPT-@d82qtedN0 z(=<{2H(OCs5G>|k&EcY~B0b0ke*o}xN2MleO&VlN(5PSni8i>IeTTv0!#hk|+vIhF z0j*Z8PRO1@)p|ZjwLh=KXTrnt_C=(wdFn>NS125U3n-ySvqGqDZ2ZK!J1PxiGF z)Vqq!ZjVB*l&mk7BAt|a;4X>Qz>-oA8!gP+7+RJr*nGQ<25z}vvi2t25l-VwRJV+IM94RV*X6sf$^R~jaE1~J_FY%$-OfjHYSst^r*CP z0-{4V5yIEY8ZwO`!inP8ic3k$-(9xNiKg1N!+^c=`U)r~iReq@Bu!f>S{12a^@7#! z9u>HDP;T;K^K-^#oE86^14kv|Tmu`lL;eGQRA9x|HNp8D9vbREr;ESulI1ch*e_1i zhp)U#fxvMT&E69c&uPD5b?5Dj-vQ(2$qsh@37DLzS5fTSoOW`xhsRso^RW+Jdq}8& nxr>T};Q3r_y?PT4+i`c|uy(36wx8FOr@G(kwOWDA&lvv${g^^K literal 0 HcmV?d00001 diff --git a/CharacterSets/CFUnicodeData-B.mapping b/CharacterSets/CFUnicodeData-B.mapping new file mode 100644 index 0000000000000000000000000000000000000000..919f01c8b870a3e7f8358008da4aa2be805396d6 GIT binary patch literal 81316 zcmeF)dAL?%`#Aiy?)%=AN=P!_yIosSNs=T~qcQy3Xrd*S+>e`?g8^UuEfk zlg+PBl3lwdNyo)Wve)t?xo&il+&eZ&HlJaAw=_wHvx+0QH;-g@j^sW(iaj`r`|@b^ zq~j#}@fh~v81Bzw*_+Oh9Khq)hvRr4k7r+wXFs06{+z%8Jdp!=5(n{Q4(3E2#8Wtg zlQ@*8@?cKpv;3UT@e3~CGCt2Q`2w{;@*=#@NNFcclZ<@@}NAMkfB;vZbh)%=JxT*5W{m}|L|fASOlhoADV zBuV4{{g~#MXMts`#oDaH4Oo};Sf3lR0XO2t+=LCeDI0M!HsN`l6Zc?e?#V9Pi(M&y z(<;i_v^(W%+Jo{m?MZo=?oauc9zc1R9!U9@_M^N@2T;DHgDB6^gDAh!p_EtYAv~0a z@o*l&VI0m8Jdz`6oTQ_8G)L38NXPJ4j^%M2$KyGk@;;rw6L}I(=0wWxbdpY8$5X}f zI{g<<z*P$o9FOcPPKd>&l8`|3&evtjd5QWT7D6y^AlcTdEDm=#(iFD z`Al9W9>&WV_je`Zey*Z%pI&XdYk0jl?&k&?zv)ek`?%TqTX-vP+M;zCEzj!Vm5KrTSypRuB{xBcmqkPQrxZZi<$2p%*@dds_31KF5-t&M^oc8T|(nCT}tCI{fwXU3ohfA zT+Xk!g6d1UlHYI@zvXxQo)$bOXhF$EMqOI3%NSnfOT1q^|>J%a3iVj<*(o)l;3&zl$YQ6$0)z^^LP#A zRsLE&!Rz=W<$ZoW<$Yehd4Clb8SbXL$>s<86GNxAO(w!54WaU&!c<|f1y0f%d>*KD)gm?PN5&=SD`=i9KZtApF$Z2u@=>z zLT#!)g*qI<4LFo_c`)no5X!^Cq1=#%Q9c$9=SDn&^0LsDn^1lfEr&$kRf5 zZpK|HUke?$Id`MHEp+6T+@12b(1}}f56a_0XKu?qDW3~nSkApDuM3_#g$j0~{4RL@ z6q<2wR?>5*upRedbN1l&+?OrblRI!fwq!51;{G%a3ca}_51{c-=);|OAct`{NAO6F zL8)p20JD7OOdp7xE%r z%;~&@Gk7VDx58yK&I(u1_$pjQ_$l1Pn|TXw4Mf{M9`4N}!V=m<<{FI;ZbAG{PG;RyaY1|f8 z(6}wEq;XqV#c%l?zvmD9kw5Wg{=#4R8-M3&)+9+;k|u^^QkG+$1(vZEYqJhFU|rT@ zeQwAG+=x4KINNaqccJ_%YtNC~mGZEx14nT;%Ez*f9L?P+FUvY{4ENx%?98#;lgF_O z$I<+*?09zNcy{9ntl|Xj%@f(3CvhL1%pRP`eJP*IdU6u?r3;MvVN>)e@@{5p3Q+ghl6-72XktY6t#Czdl$8LQF|A)cTsy6wRcf_ z7qxd$dl$8LQF|A)cTsy6wRcf_7dPf6Y{*U7h?}u7H|G}Il3Q_WZo_Tqq{VVJVFjDA z87sLRn{#`%;0|obR&323*@io@EqA8=F7878T-=rVx40YiYjJn#&*C1`kHtNy|B8E2 zzZJVte--zpek$%m{ZrhR`lYxZ^+$1k>WAV1)c(Z-sojhHsJ)8=sGW<0sC|nEaR`U< zU>?Flc^D7p5gf+h9KjFK6>U&f)!>%Ln)%AL7G&gpcwu&g0{Jf=_ZjpW@Se zhR^ajF5vTgfiLnUzRXwnDqrL4e1mWDExyfn_%0XnJ-*KmxQHKeF+buGe$1u(grD*= ze$Fqrj9+p&zv2ph&6WIytN1OyEx0kwGs-vNR%}S~jPgyn4I9xsqkJ>AU=x}b zlvl7No6@|Xyct`wlI8{F+p!Is)4ZU3dv>IGQMo!>zB4<~yr^8AE#HNmX! z9?K&+mLqu_kK#CbZj>L-qj^5h;svbcG*01#JewEs9A3z?RkWi0D~6>>h5M)& zPWMqUg08>fNV@)tk#zkPN740HjH2tTIGV1rVl`zqC&N_DMrDb4>X)tyT9t@2Zv2Ue;-mFirjcCIuZtklkx`mb^s^&B+6Vl(c|N_OXV+=u#OT@P;0ec6J( zN37d{`%yow^F3mnepvrOSY|FkhuUprT?bx5@b?XMOJqOaf zZrvbu;9#29tviSvIfT1&C_C}sBrPP>X`uzZpepRZ99uHaRxGeJ%eW(Ju?=f;C)QzG zZor*cm+e@OyRbgnb3^XR2JFC%xEnWSM{dI1*^r&MDfeI_cIIZ>la1Mhn{zL2!LHnr z-MAI2xHb3YHtf!Axetr%!E)})ChW-y?#HI=#b(@}mF&&!cmSKT54Wd&iFfJw+DWoi zTR$6PiQGtxu|#ep##kaZ5@Rfp8;LQN$c@ApOXNmkj3sg-F~$vB_kf zl)^MmO_7$dz}l?E4OoZuSeF~JJ~v_mZo-YZDI0P#Hsa=N%q_VEx8_#dmfNtLMXEVz z6RI_7Q>rm(CDoR+In|W31=W(YCDo9$HPw!^4b_aut5mH>+i_R6r`nKqpjwdb&d%I} z<{Igq?8?1Z#cu3Q`JDEkJWhL3{-*nBlxUaz#qu^ifbumxkn%L`NBNl!pu9{6Q9h;z zQ68p4DgV+#DDTq4cqotH;WVz&VLXx}XdI;@Y5b(4XxyZuc`V29IF9A<9LEzlo+okw zPv%LK|LH`^_w-brM$feL40@KOXKFX&Cp}v{h3E1dp3n1m5ijISUdk)@2e0APywSVR z=(k(MH}f{$$~$;FXYnrH$Jw0A`}r^*qGxCN2p{J>KE?TbhX3Y^e1R`zzCzPhxJeh&vo~Er<0V~6<0Sor)A<>VkMs+E&M&!) zUvW8&_jCon;YxnXRs5dc@kjo^pK@M!&^S(i=C7H*^EYt~SJOC6*YH35lk51GluNa5 z!aV(uE2HD(jLTdtZb0KQSC4hMA?tG^HlT5tGcI$R(zwiR#zr(QbB(zrx1e#EGcI%6 z(zwi()40qvV^eO&N*a&3=G=kCW3Clja!0o2PHaQtG1r#6(0I%Za6i=?$f+F63mD^L z8i(={#`u`QVZ4k-@^X&mRXm1QGV=arhtv1~FXV%ae16Px zZ6Edj8qUZkiT2FT7Q4Q@e9fQFIdq-*`{_FKb8WW)9}pW4`3E_Z57G7J<#pcm<{#k| zl;3&Rn}3Y1KR=JgO_z{_@Y#ILklBTeyI? z@%hXbc)R#T-a+FguRZcF(>TlujyqCsN{vy7~=`^15`Xz5XmG1Lh z#`S-|IIi(jx}J}?So|@U&^XF3rE!!uj`GG)-Z;vC$z}YC%V`|tSMVFITkjaupN7_IrruE>`CLRupf6|FSg|VY{lL*-U-9?LO2j$>(D7sl}fj^~M-z>|3rPvJzK%1QheC-Zck z#xr>at9ceLWMdc8c^c=17ioMKUZ!zfc$LO;;dL6v1<%F8yL^Z5aUnn8`}~lL_z@TLV=mz*T*}Y* zDZk+7G>!|)_!XDaI4-Q9aa>r*Z@G%!^E>{?ANVtW;;;OLzw0^OUb; zWt697wJAT#HlVyLtH-+BkoCC{8*n&x<_NZ<{43jqBiWwvuxwY3Vh75{vfVhE9Vsu% zcIOy&;<4O=W7(Oevli}Jf{f1bwPl;>p!@C^2$d@nnYXR$A<*^g7$pJ#Ib&*4Cx%R!vV!8}hRYrmp) zFKYLqb}w2~)b2&?UexYI?OxRGMeSbH?nUih)b7RDPrDbjdvOzP%uU&lo3Rl$XJc;3 zEx0we; zck18b9@MYJJ*hv7dr?0YyHWoY_ojX;?nC`m+?V>PxF7XTaewNU;sMkj#RI7yiv6hl zivy_Li-V}Wiw99V7l(2P58=T)jEC|F9?sz$#v?g`M{y*N<|xX`;%Lgp;uy-q;#kVR z;yB8?;&{rp;snaG;z^WW#fg+x#YvP;#mSUM#nUK%if6EzXYp)K;ki7A=W!}8;Q73e z(|9p2;w7BUOF4s=aVD?e<-9VNBn@~KH|EuB$ZOb$*D~_sI@`zn-@==D8*k+uyq$OP zPTtK~yqEXzKF;R-oWln=mk;qlKEj9j7$4>1oX02m1fSx3KEtQ^9G~U$T)-Fk0$=7! ze3h^8b-uFdv57uzCcdChZs&$ys^SwOBy3Et_z1*asygtk5 z`CeX&`mww=J>SbW<`&$9=11iXxfM61`B8ZzZo|!JepKFsEm*-F*pw~VjICJ7*4&Oe zvN_vudz#Obo6nVZ7P&*kQG<(;`J)#q~ax$-XDjp}oGSN3E#s;}i$ z?8UvQzLs}qZ|+0&wY;yqjQi^+9?JeEO>ut%#D{R8IPPnZ_%IF@H{n6zBRGtsIh@CE z1jq159?Ov&%cFQ4J&(%A@n{}T&y(^ecmkiK=Slf1Jdv-`^Q8PWp3K+jc~ZWFr|@HX zo|H#F8~5ck^gJnFMdQExTY8?9uc10nzLsaX-)5}lcAUcPc{ZcJ&S5K_%jln}jDCsd zhv#ki`ShGEzkr^v<d^|^GM#p zqj)Qi=537ryPe1K4n}|7$>^uMcmikfMBdGlc@IzFy*!n(`7hqb(>aG{@_trxu6~%u z2lzN2wES#7#B=#D&*P)KfRE*q9`rYm}-&$G3g((`O> zBYK{#-He`RYa3JktlgaI&pP#WT?MPyljC-RZ@Ljw;g-1IrruE^nG_-3+~4q z==<)vmfWAM==<)v);xea()ZnUZFnGeVqdmpKdMLT`m-I?>vihVx`Av@^?IH9v~Dmv zP`zHa8;7tXhjMow%=jHmi|W#A@WKxC%Bawi{*blMig~uCKR_((NPo~&Xv13EiM835 zb+|J(U^~|3F09A)tj}G!Av>@EcjHFv$c?!>H(@6>eWA4Sx*_B(c z8@FT?x8mO1n%%h#_u;neL4PPz*q0^=g`RA}{aC?XY|8!FjJ;XO1GpXgusIK;el7iO zMqSPs6Gid|eLB?B zT~dCRl;0)gcgg?h{-d+w9FsXF^VrO>na5?0%RD~wf4aXq8TV0oZyHYhN34HJ%fo+c z6LXwWd{yQJ|A=GTSiXLr_1nb$b$lWh*8d#25I?WqHrB__>vL~?-o*0Q_7MgTF}JyrH}Wdp#H$(mU&Gk{ zTE_m@G4{WnvHuN>{cmLKe-mT>n>mZOP=A--%6oVl@8#{B%{zD>@8lfb#rrvnvH#tS z{qJGye=lSI*^K?~W9&bNvH$&y{bR0J;6q|{wlr6)#h4@3;rwiQ%nj>_pBKNum>X`$ zm=kV9{asofbF_wH{apShV~*B{F*j?>KmT4{noDfy*`Hd!HDfNZEn_ZG&X`LyWy~eE zW6UKy6H0T59T;!k)czO)C(7%{_bCn;^hGURbt2Zu zpVBt#>w;ya@fzzc$<}G(_4}2^QEX@aA{9iwp#<_ZwjJ7Q5lzLE_qn^UL;!`t?ozfgN_TAKHy*Byp&xZbge^$$s z&C+?xV|nzEp;}TYEve3zEX%BwSv&K8x_^`GI2D;qGn-{rW^R|+JX2jSUDy9~|6$p2 zv|Z_VBQlT79GQ7krn+3(?&!?_>Hg#YK2H9D%-R2l^-pPe7`c&c6Mq|9iXY4LZ2oT? z+s5+s`>fw)eQqV$w(IjOmapG7mc`GJH}P|9yZ-0MkLP^W=hZ*A^K<}`2{<0 z89VYzc4E92>&&m%gv})@KDz9aIWDnuH^{E`!-`M`46Li*KxEtNp#K_rj!e5%<;xC&++s!B>o)< zY0UBTcN%j%eVx|kB-Y_%Za_Ji)}?$%>+vks=M-+pbJ&1Wxe?E2Lr!D7f4YbT+Rcyf_=D>2XYl-4)`7W@dpm% zPaMQwIGDfjApXH2T+N|e!-M%J58+=t)JwF4htZ<+@OY`lBUp>WSck(|mm^r8N3sD& za$_FFh8)dCJcf-qhFkDhZpE?OhR3nUacsil*_7j1$rIR|6WD?$vL#PqYo5$DoXEC3 zh3zy`fARDCP`UGYjzwp@KnPow&loLfxELK`?3?&*<5E1 zU>B;Zxvo?f^`v@~>&55Tn+w>7>P40N(@iOxat51mCM$V4x8s#; z&a3$>uVu8`^^Eqp(VzSpC%K!sGjC;k-p(PslS4U+2lE~t!r6@en#1U?xs3jLke+k7 zhZ%YHC?n72G1~bFMmx`EwC~fVz`O8ScHjcW`Cee0?UAZZ% z*r-Y^*^8bJ`QF@;eYh3-((@tTpW9R=`GMS)gITnE2+MgeoA6Lp@NhQe zFd9Gk5j1}CBWe8PM={!MG`Hs%w%}MAH~DdF$?qZ7n~I z##{ajw&Phe4)RlI{N>N#uAEBaB7Z)2<1}`x@{Hi_oX$>`&)^=M$uFfQWZmM`WJT*6^o%HjN!BltOwaTYp$<=BT~4a<%;l+U zz{%X0|6)U)mP-nacsd*N3~s?QxfRdiHmqimQ`m%OvnkJECC_DZPGt+8$Cf;wt$6|4 za2nh4Lbl^YY|o3?fz#QMm#`COurn`Z7tUl?UdAe3&hET|J$NO1@+$V?)$Gk{*oW7# zFK=dl-ok;rm4kU3hw%2Qq;N3r;Gw*ehx0BD<1CKg-5kk#IEwdjG-q=R@8ej`;W*yU z@tn&Ee1IqMK~CgDoWzGYnUC-^KFTxr7|-IoTvC|A$9WE);8Z@z^Esc>_!KYV(^W}f zI-lVTKFgVWj+b)*ujKQ*nlJELzR2tO5^v+HT_%uJ{vs}yt{D?1b318yJe1%K-8b9G1 z{FHC;Grq&mxsYG*eJ!{FGnwbFQp1*W))_&Q)B&Z@H4+aTUMk zcl?1r@JIf{pZE)Z=5PEZmlXcsuUyUFxQ4&;PyWHb`0rd&mT)z5tYIPEm$MdA)`|D! zRY_T0=2@QxOIZV!abwnEL)K;^*2yJhjky80U|q|%Vm)rd`YduoHemxcC$C0xT9{F9gRFV3t=Y9+jk zIbO~Jub>s>SF#STVqIR%`n-k>cr7>Pb!^D%*@!o=F>mA+yop=!W^Th<-Kgj*=*1I*nxA{k@vF`=jM`Ho%sN}@IlMF z@*!~*9~O7#qv9TXOx%<6#J%`9d((5ZRv$j8U#D`uxG$ehOTRNJe@@(Ayr3$nHIUDX z2lEB-5WXZnm@kVDKAs<1K7otHC-Ear4&5=QomQk6)^i zT36Hau-3KwO?*B75Z}mui*M#?@vW?}f1G!X_;#)p`#YET`{`PD@;{FIaFR4h(xgd_ zDf7wS|1O=o+0y#`*{Yl0weec}&7%2Yt$R3!vl;y|htV(Ep(6U_!6a!~pyM~)fRD01 z=dqGcusP?m8=q!(KFfW%fcx>migEwn@qYfm&-oK0KYrmd{>Cr)2cy4N z^INWA^!J~P{{G89VzEP-w5>z!-gbi|X}L6))J~G5ZExzIwtZM&U)ExO+P>{T*5zQ< z=MXmF!Q7aKvLO#=BWlmujj26rZ^5Iu6_4gNJcdPT&)Q9>J!?1RiL9h{t=*gx*@CCA zCADkq)||{XJdJI6I@|Gbw&#uPKti z?8Rr7FS0LRW`DlQfqaL9sb16`!uNPE-{+xR#KZX^hf$rVJ%UR(lF=`ts7}-# z&EGhN|K?a4f3?T4hU2-06ZjWT@@rP(?IPD*W4w*}jkz?L{u;duzvvbEWAbtDvMf#d zXt$bLX)@p|)=ra~u4kRh4bo(2KXKhOxiq$~mnMVGV|~lx_#39laCukLAWbIv{d~z7|QnPWIOx=r{q{(?tuwj~yLgC#~7idEIx-R%vq0&)nMePT@AL_hfFHCdU?7Op^(59Dlenbw@TylN;q% zO+}g(nI;!#=bG)(WW=j%o+j0cxxMT3M=&)l(&X@HFXQFt z&1t+G7uV6sdH!T;_wg2YOp^nrvQ3)w^ZSCDozkR#Tz}g%8TkTtcAxLDo&7fEF52m9 zwoj9@l)jo>U2iRRpuDKrEln=|ksZ_Is_2*9)8xA2*(ptKSi?QiWYR_KoF=E9$vxBL z^qbiwP0o({*egvgi}vX1{$qW&G?})DRcSKhGx|Nq5wX5|nw%!@YW8s*XRwFslYce) zI)2=DPkFL{`=!a1|7I`yNBiubCWpp$z1^4Jr_>yvU6!zq_T7jFx}KfbH%-pijs5Je zzSs2EE(eQ#ko}*ge;(rO=xS@^wU-oNS!b94T+2KS#RHwLD7yJ;PDjIwnnyn#3`#t0|AQ|MMKHJr3t_Y3X0_R5MP#?k7Ip_1wep@-p)NgfzKi zE+;tN`8?6}$MyRC)07x*C#T7g(SH+N-%&ip`R8zwb~%QpGWvCLnvAaIzw}$&-)Z(g zo~JuswCfqhNwmwE+WB`%u7OpoHctJ0OwAPe{2kABKQHne{T0`7t~_eVscCY>eLOEs zu8llDU;bRh3mADm&2g{eh3d&myvY5X!i(KshuTu0QZv<1O~TPJZ{|^{yxS@dn<; z8{O|yyh$C2`gXH%ynwf4{c)@7i*~$CKS%rAuHFB|JKRUK_nq3c3-6LIk#Do~!*br8 zCWC8uPnK8rswdH}v*lOh>wUI=kaOJ6F}z>>i~F7HK6>zhG@03+56a`HlMfl!Q8yn> zldHGjBl0iC!=u_EuH!NN_%P?$ejXopo-g=>>zT?Y)1>bb&UZc0E>9Vcd+}-gdJUh+ z#>KPR`7QBt>P_Uq0`2%GpVyA3@dfq$V!o(9BQIZazBYVWdq%&!qV7feyvkqsnsMHZ zuX}D>&o|uf27FWA4&__M)3JP8KSrH<$Nk0lcvt=&$A$7Cj{lzX#CUq&eYD~S+H*@T za{N{NP`|h2V*M2D@{xS%&L#3c`upQ-JTG;eXpc`^?+*M_|8K<4v`37i&)rwlr!QQ8 zA1<^1EPiPm#<*MV`fKqk*T0M_^xLWYTHeGsTFGelZ}dmhfmO~I+kI>NM|*##9U>3D zcV2&UUh{+YeuqCg?$!Lsb7l>HwqNAuFKKf9LHyNtx{1HJuB-UF`+l8&c>b*9ztzEr zSEtFpny^M*&g2^P`B|=&-_g&1s)s}PA9)eW|57hIaGmz(lq72s)@Ca2?_^HOPJ2kW^1-rT_TAHcevm-=H(J;#gV)Yq?FxuN-h^RL-h zJv^8V9p@x&X8osZtiSZa--*BAG- zi*|aI?P*{`raY3n zbENr3A|Q^XFKHPf2P&!6P`I!xkj9pXP8r%Mo0_Bl!YH@+CewC8_-i=kql_W%(O? zns4zLzQbp^Fv+b>)SW%4zVzux^`%cY=BsmQQ>rfq4x{>F+~roQF9$wJ^`)<0sjpUF z`c9_$()SvwFUDnVwffTcZK^NrZ{&v6Iq#Kns~eQYYi{*M94Je;}aRL=&EW>?-y^=#lD zRL=%&O!aKg0IFw$;`r*>pmV66l~m6LMSrVjgYKnzHs~R$XM+|{Jv*o|)iZTBw^}_r zDB43k8yxMRo((>U>Y3+TZnb(ge)dTQau~ej_TQvovEG; ziT)m1oof{Rp`H!-h3eUme^Wgh8s}HfhHgdmY-r@QdNygVH z^OiK(y%TR`)Suf*&;Q)&+pBZU-xuE@j`QB>`QMXwu{US25AV+6dpKEqFQ;=hALM9uZVIIau_y8YeT+d^^_w?nw>RhY(d|Vv;@`Uw~ zk57t6b3UVAo?_&~)3*Pa&-gwU?fR_cFY-Cw%>`-FsSTgEKKl0sp1~J+17G4PeA#wm z`ATUXlw18OBYv$q*S0QS7f1WQVP3SFZ!+rOTWPY#mVCQ3U&^h1M_fnzuH(h^EmWr? zFW%!7eBb;!;Rm+Ao{P*=?&F7PviC<^Y<<*~j~MN>gcbalhjS@o{CrZK+vRzF>U?n> zpIN?!pYvRPVcyoC%S!XN-0Cl@bM224FBeBYe#LUGNRwk4@oUG4_E;&d;y1jFs~C0U zTi@Si@w@6=hgkl-_+$Q%CY@uP|LFT_T<1^KxsG@7XYoM(VtwS(ui^>(jW_XkKEXfu zB>!EV>lEXBb#-prdaM!0{jBl5DE41lo$EZGe~O>se;E1y7mwyT^HcRX-Y*{4g=uNN znv3^~$8{6ussH2s;&GE$M(tEnt2(!5k+sF`S;zcVov7J><5)LM_S}W_#ILhHjhhoc~nzgo!k3fHZc!x!3yrmrZlcaANBLOOygIjUY`1-NuBSYR_lqYQ@9}=|#0%LnO}flxEAbs{Emo)E{bI>B z=KK4KcjBRJ%M-XWqkY>^eT?^ud&T{>_r11~yH@9VcVGu`^w(};<0RfMdLNdn*}Xbf zAy4A{;zaeMW{>LJfw!@V)Y-i-b_pgQOKbuQj7PLiMTe$o5S zT)bbL^alIc?o0Ns&JAwP0j2Lbxtf9EYsG{3A_rT32@m489Fiv8BL9Y#zW3zf{o>@f z9?$>D2lCMB+|bi_Sat4TQ%~d;usId`~DT@9nZ+m z6ReN%G@&{-BJ%M>MxLBx`B0uL{)iKOUyFP@r8;+Hr&;OB2 zcv^MtsAfFf@?ktfJd0%6q&oYRr49 zb0@~}W{XGizUtiZtvSbjhw*+6=UmIz`| zj^dlPdzx?YWxnnGPmG^;_&wkC{$LsxdVes8?^Wkci#&Z_d@n!Xb6n)TLexdi|BIvE zFRsp=8RP$>>Rg@3vn7_lPS5{~=W{84qUV2|xZY3w{_K5zCLcTU^XlBW(e7U`u49?^ z6qoZ$M!zoi-f$CsRh>Jp4OfWc{9k(?9Q&_iw8uAG%vC&%-+I3t?f6}qT)r8{kKUe2U_w$S8k>9_1Px3r}Gyjh9_`CNdk;i}dJ{$GI^MB@6 zT%9Jp#<7MIxyE*%)ARrGy8P4f*#AG`sKbAG-%^k3{x;q(UcEz-Ta#Aju6UF=vHXem zi~H}wg7zt(J8ox8OG8}U+Z=6$QYTGKd94j4?&{~NV$yobCo zu47B@!DGLzxDmHzV{XGexNUXL>zUHOBQU>S@2wK=!1B4wSP`-JRwe6lZ}IlrhbS5Q zMI8HuvAuV7rS)N~4`Y28>-#V^@XoNbT^QSiv0b=+eQZA{+b)dl!q_g1^+U7e!zoO{ z*e;Ci!dMZ;_F>%5k&OE}iYS?8#(uFpwm&9YAIADH)`ziv9OD4T6D4E2h-15O{rcGc zHee&y3jn+fp1x+#-v^y~Qby%#1jWA8{CQ?JN!>uAjwW z_beWn8T}gPkM;;7j`KzwMjXe9IE*;1BjPaPIBvw@zKrWQIy2%pPQ+ovwX-;kc>634 zdx~TK(V6=(FOL2QBaZ8gIE*-s8*vzMTvx5d*+I4JZ#IZi& zFyi`I97dkU_K_c9#Iaq(VZ^ar#9`!pTyNw{7;#*0#9_qsvN-H7j^lb~T{7Z0Zp2~4 zaomW*0gUZW%#1khH{vkj*e~KR;@B_ZaA3Clq|At8dBou$M*Bw|hY`npL>xvO_YrXz zag4)=!-(U)A`T;t{)ssB&b@S9?7W(f+xae_I+4 zalANgTyGpVjQm9@f5R8E$fPBiAjvKI%poaV(EG zjDCpa(GOw7u{`4Nz-;;PnGwhGh{G5k8!^U57;!9*IE;Rb<VS6dwS8^--?&$yp(5u=~~$owGl_sjzs`yKzcxi)_<&+U}0kNl7Q zV>~d%LaE(n{cY*r1(^EziY&e=^O{V*!;S6pcVyn3`C4Xd7srhC;R8Og{Ue!?HxWOc z`T9TN*#1SI@R@9R#IgOWnG64+?fknpOZinY;!XcvAOCDp94G#nqS!zF8KcNU|L)9E zT`9%3DRj)@U9e-0Qh~;1saTxK=SsZG|7`MsSncZ1iJTf!l*e~KR;@B_ZFyigA zIE>gBDXnMPP%KP0&5YRjN^zWbpKSZ1G9#9|rTWdBe2#IR+u?5${ZzWXQJMR)VEdyp zjhE8;(U}p)bw?aVTqldeh~v6PXGR?D8gaNE<9d$CloO@)`Z06=%wzu%$9W0@!$N2>yQ5~c`t@<1h{IUFV-|$i^$B4+Vs=6K8Brhbm&rPh^<*yg1;jM#BXaTu`;O7Z&RsZ*un zu~gTZG3r_vbuF$V;xJMjYG4`t{q#1`#v^P-3jMxUHIE=Vi7PB;t!Wc*EaZ=b4)svDKwGj5xN7_3O8{ zK`9Pnz2lZ*mhv8@JPl(U#PZ17_3PvO5r?rppT#WA>o?78GFt*<^Gve4j;@JK%pV&T(?VB*RugHuzwvRZBIIb_^xW21=V*fDqZ^77qhs=m$ zzlg(#WB-U_|Hpk||1kD%%h+GdDH(C>7jYPI>>qLLe~nM^8FB0vaTsyz zA93vej8E(z#{OLy`*+KXIQEN}rT<1|Y=4taY~Pcy{eGDd$M*jlUH1VVMfHGde6}PI zdQ(AVH(5eQvPtM55_<2whu(W{0@6fOKtzgC1?jzakltH1^bS%*1Zzlg-)}jq@qh32 zarn*bPTTXHb7ppSGplLMe~3ZzOU+*wHGe&$rZvB&rKYuin%4d;VbF5TP|HcpC-wX> z2CZLezP70OVvU;C{F;`U*7`NA<(D&Pd8y^QpqB4y)U=k|dl-8fb)Klld%w}2o61!AQP%E0DIRA&$Ep<5y@^z`lan3XV7w**7KT{ zvl%p>)O?cp+({|adNr;24>4$dsrgSZXubOx)IKEb!~eYI*Yk1-gO-z8PHMS@49xE@ z4{dh@)0G%h|K;fv)2zq+yP2MkT7G#7nLlhQ)AU_o(~UEXXN?z)=ilgIC7R!(b|TXn za~X3R%NQ#dwQrig>KpZ2TK?%+V=eQzS!B#a`M~B*u_Y@^gk}=Fi?K}X{qUfW?E`` zh?$m}rX2nAOHEfc)882>C;#)SjcbkTjg*W3dCJ2t{kY%l#+}C9M%ttQdD?~FgT}+g zqsB8S)P9{e(^AuMW?E|cl9`s8zG|kWrk9%O=f+=*FO0t$6W=We{`;r3eJyvz$m{S= zUo+k?-ZI`c-ZkDcJ}~}dd}!3`(fS`7wI5{W-(NBc()Og*v)8hnYr@cxp{cPA_F|B| zjeU%Ljs0*tgO=0Z9>|>xT26n1AT_P!HBBAXZ}eqoNSpG%(?-6I=6}y^w?eIBOgpUi zE%u#0vyd$agSmA;B&M;ZQGx`%RJ9JwaZo$*J!I5zw;Su-QpeZL#wOx)aqsXi+7v9 zlILH1^>2(d)b-L@=6+@!;P+FHyY^WZ`CZa$wyV}1*ALcX*M94j>w@*#cFksYt+P2? z>up(F*=$)|Z`o?P>e_1YyS58l*K7-2*KKQEk8SH*PaM51cXs!??i}v-+@WrdJItNSozGps zo!?#1?R7`FBi+&N67Ev&lJ3&(a_;i(7Fti@TenB$T+3mHSyCTT%ob{Wlmi4DC znH(>X;~H{YOO9`o<2&S7pBx+5y10hey1Isv=N0n2N}dnN^O0kkV}j#r$0Wx$j!zx4 z9g`jN9a9`j9A7w=JHB+RcdT-3cC2=6b*yo0cdT{nb)0k@a-4D;bDVY@=l5w&IMzAN zJ1#i0Ivvh;o#~v>&IZm>&X#2AB-0==O-H8b$ut|8zD1_*lW9&e4JXswWLk(!KOoa0 zWLlI=%aG}ZWLl9-E0JkUGOb0X4au|-nYJR+4rJPqOnZ=NPcl7CrZ>p+0GXyH)8l0N zzE#y0POhh|>b4?ed65iTlHpaWiEV(@%r?ks&hHh+a^0%EErbjolVMJ)i!C=f{%m!# z6(ze@WY&tz68IDIf!0vlU~8Cbnrne;m207EwQHN}uxq>Pi0h>5lIxV~GTZ;rs^r>b zRkP*eYVSF=8D}-(I%H#hXSo@_xf^TUwK}?XTU~AWxhnjE_5WrKv~{%xxkl1P|DqnO zAp3*l?j!4!y0Uqd2S}RTjX||+HVasFxJKNF>zvK( zQC6&tukrI)KxbDN+bd}G=DP7fu6+)*M_UJNH>hiO_@#qF{K`QA>Q*&f@T+Md{++4I^SBpo!Jb)y(SeinRu~y3+?tw1&7oBlj=q z2S(VMyE@ofxH{U}x;oin`OWW-U2|RSTwPr4U0tbH2dUF1*vCB7=QGqRANBYe`!kOH znL=G2LfyJY9e%=g^HP7GQ@4I+JKs=uU$d?8Z0mFSoMifw+VmB5Y~5VLY~5YMY40m& z>nmy7$7$CmXv-I!!Om2a?Rm=9M%i9-zT><>`QCH7oDV4DUz~ZJubc&)e>y*KKBc@b zP~P<@@9WOu&YP6^eP>zcPn7!$XBFpf&Kl0YoVA?KoQ<7LoR^$eoR^(foe!OlosXPP ztQglK+RF~B8o#Gqhu_X_#P3@-<2R~fU5jZaJFTv+V^(k18Ec^HC(7v$+Q%~5#{t^M zMcT)5+Q*Nyk2u=L2HM9)+Q$vr$4%PDQ`*Nf%Iy&Ks~2T+pZe6Ba+t+>W>fF#@^~NW zSv?-_%WL|T{e4OOJVo6|XH~Ripl-ZNy?9_%wX3e4v1-^Ja%}jJ<3bgdKgThFdSRnp z*gCMT3)F{^)Z^3Ch4fYjTSn@_d(?xUtS)xd>9h1FzgXREk2p4z<+xClZJ+0uKz*=L zA8Z}jkBiiUQMAR&w7n~~t=tEHx{Wq&r;Xd4RyBKS>SQ{rzCDxG$exY5NBg!1SzYZR z)UWhbKYM0tp#3f90Oug*K<8kVqpjJ4SuPFBWnj51EcZ6c(bnv#SS~HgWn{UmEcXs= zEH%gU>>Shc(bh_EOs~rEybJA(D@X239M3~(cV3R?LEFZ)2_gTIk%g0id3n;&}l*dBKU>$X00d3St z`%FjO*hZbF4|g5s*c@fm8VfKX&dzIt`i)iqpco%F4)smf%aL2I(CY-*@Cv&o;K5y`gWf_`D5zK zr_`6<=<~m#Zr!17-KB2*Lfv}7_WChjfA-g6JN)S;=d=y&?^^xsIjjNp_pAzh{#((N zi45{lk377NeAJ<0)S=SUp-R-B#?+tI)Sq@%dp;xX;L1!c`KdFx*w6gbmEzQuGSrpI z)RQLElQz_o_S8`)bu&A4vM^=$n6m3fy$hng<)EHLP>xS1$Np?P3+0!cGAl!QRi}*l zu+M|oPFBhy2e1D_UV9B*Z(m-=VD_&7+bzg4?^51{n0Em4y+?U{!1DviB$#z(AcrEX zvlQ#BO8K+@u3nV&49Ynb>(0nJW>UUIS$}EPUyX8Qzg@j4!>_F}o?h06p0-w5Pw%j) zVROQ!hs_O}5w<4mY}l!=^I>PgE{2`Z#_b<>~2(^|bSJ@bvL?^z`-g^9=Cx_YCw5@(l3|_6+q5^NjWk_l)t3@Z9o@ z^xXE0^^Eg;;u-Im;F;u^==sz$&2z^y-E-Ho$g|3`%(L3F+_T2B(zDjH!SmR&(euQ! z$@A2++4GEZ=Ka*)E!5q^)LkES_5pSFcg|zuIgkB`bDUt#aZ+)PlYw)bjGWWt;hZKf z=QM>mr-|U4rWofm#W|-b%Q?+QoWnHc9Ht59Fl{)8>BKoqXU<`IaSqd)bC@%n!`!4j z?V>H6r0u+Rta5L1tZ^S?A2zZNKeFwYj??Z}Yii8{qIl*u;ZI!e&7^*VV zOnW%(@w7+Mo=7`A?F`0GrmLK;YP!Sej;Gt3ZXX_D>;xW7UpamC^i|T=z?$hVr@xZ^ zTKemFjp0V7+?n!tD|=^nYj|sVYk6yX>v-#W>v`*Y8+aRf8+jXhn|Pagn|YgiTXnv_j?a`4|)%I z4||Vzk9v=J&v?&z&wDR;MUwHrW{_6ePo9Inq`+eE| z0JcAv?GI!7BU$zw%U)#Jc$U4wvb-nmoQdlI3pk4m#bq36^xq1mW0CvaS$c@SYq6q` zziY9gaT5*ZR#U9Yzv@^?J;p~&mC4@drv$36!6J05+; zZs+fKIJ2_smslU=W9L3{y0&7!g2nMFmc(mV8n5Grc#HM4$J;E=bq)I+E%D^LI9meaPRZICkI*+>iWyisJzC zcPWlp_#19U{{FVIy*V zBZFQS*MlVDv$yowxVo7Aba+X4_zvL`~>|f3gDMt^MWx6hY zgj|owSq{1Wl~eDc*w38hQTtf|W3eL3Y5q#c>&sc$#)TxT!t?F0Dt5qX*b%EUUo+(I zC+#h;Cbq;{Jl`4XV0WyGJ+U6IQ~O_^Y1&K92F$PhX~=fo#zy!KHs<+0*aQb)QyhrR zc%Jr(9UoE^;iJDT-% zVm_^>Gip6ua6fiMt*0BZ9@iWShu81kjl6#MUTlT?uni_*EM1MA^@j4l!{{^y8H0`d zosy;b_&X)PA;vVu%r+~O{wwqy)=N7I4d?N~n44+x2+ia0mwRZ0nP2smb{$%T$7ye& z+($;`9$K8~zF3B7$~UwuQjVcjkaif#ePHw*n^5&jBd`gb-zcO!E$v?)^kQG6976|S z5gdp`aS#^6!AO4)Is{ALP^4c79fk#QxXtop!4XW;KZK6Nd^ig8qx!>MI2yIRF-ZRv zIu@hw6O6`jsO^tOZGQr4`x8;ypM=`}r>N~uMs1(J`}A*r3e(#D=cw&}f!e-~4`bEk zQeNXwU(lI|%CiFMODU5vh{w4u8y1XTB01WoVhGPqN99L*2;=X})ULzmFZ}X%W`6lQ zGxf(|{GFLCFJ@x88j`o)EJ%9~%ZlU}_7>9K!`^27GcX6!b@5%K-G;q~_3?dUPGczZ z&*bqi_LKfF%)|6tR6XMQYgj&``mORztKZV=RDGg;g{j}FgppVo)nC#7gsH!xUWL(L z>9dM3`m3OGNPnfzCc@~if-a!?E9y&FX}13z(od;W8S`VGUTHmeX%EFUCf=6!lxp%drWr!lt+uo8bm*j+?OsZo`(i z6IoS|4drwY+p<0763aCGKv+ATKZ@;{K7k#0zA<)W`V@BJ`7_wrX>sk^3hTn- zKBOP9nqoJmFJX7)yMjG~EGt~)6N&V*YMSt-6;VK{6XLv^}hMkc18QvL7U>Br)hIeJX ze0~y6`S6Gp-W_=;ya)19!h2#c_QKTI8`EGPOox;Y*E6i}ewYaeT!RO3EgnIBGv0O_*W)SNfb>V<8}R~eLLY9%%eaNw6@y!`JZ?kf zwH+(s4y=Sbu`=#L<+vNG;vTGqd$Bt1L*=_4YvKW{g+C(wb@)M4?uW209>#ij1nc8b zRDC#x4e>ZO!V}mSPonC{DQt?Tu^FC0`tR_w7>nny9iB(kmkZbdFQV#A9Cktes3ZDo9!;D{odykXg>FuA@9O3`TObldaJ|z z)hx7OKD1*obRg|A!ikhyL=aNXBZ6@Xrb6m#L~7iDA>7wT^K<;JN4Z6C{H{lNL~#7B zM>~n&_+4)%a{R8h5;=a?WB(#JesljeD}v)Uv04!vzjYtm2#(+N|H5osJbMc{jyHH0 z-$wesh<7jsvm@ux5jl`NA~>!$(Dpd4H`sz4*BczgoJcuDxNKJAcyx2$F*k;CKd~nm zX5)hq^dS9XL@v~J!!a-BM)HZs!+pG{j}dusH0HxUF+ca`%7_Kfg9TCh$@ziqI~l?G zK}+(ED9qpeUq#LnT9HQt=LxM|Am<6K=*J_XY*uUPLj>mwt;scl^M%$QBj*dPUm@oU z+*iwr;C!J?TI778O;#+4+OJX=i=|QfTLyo`4{cVv)L0gk??+ez%W8-_U=!5qXo{t=8LIv@NBW(J7N~mL5?f#^ zr2mX)ZL|81!#4OSwzXLUXfF}5_yF7SMThLz9#ua&pk7Z$qu`?TG%|$7TQyK$X)#RC^f2 z{b-cyU{t*u!u?@X4nvXtF=7}_#^JadN7$^^U0|C`xy?wDcrY&em&xIRDNG@Kaq0y zCDz2L*dC{G-_cPx9aX+x;dz|FeMBt$n)`$l$C=3SCE^=wgtJiFnT;GrBj#{l5c=O$NRVfx8O=tds&4KaW(hvP(Ew0HLgXqi*=}eYdtFG4fqT} z_w7)An{gR#LDi$JcoVmAzmA!>9cSYXT!lNiPq^mag_m$QYW_Xk$0IlHMYa2V7=`<> z3?9Jd_#^lCQ2Rc}eK-chd2s@<-EoCeT*zvf_^;)OJYSVg_UqF$34p3 z`z=zBz4P%YCL`@JGSa5Yj8Alu9AM7EbX2-#j{ z>Q7{8T#sdN1Ad6qyU4P*2|q&WS7bSS7h^CO%VR36fT^(}hG8WP!OEBht6*BJis`T# zrpM|?xklDN`pw9imd*Ws6g|y4a-gq7RAnhQsFXqI4=)(R; zy^0)w9vq0da1iFj!I&S1U_l&;g>V>tfWxscjzHQ~EM6;bfeKpW$?zf-~@QoQGfFLY&Tdn(F&kZ_eK$ zXQ1lW*PIX0pG3}NTJ`K3)a#jrs&BJ7&gpf`VOsk!7jxjZsCM%m=S3=)d7MvqaXydl z&|6y0H-AAJzC=6zjt=|_o%kBL9>n*Otf*jzWvAUmr9#?ORBEI>M1>&x5tRlrU|OVJ zN2NpRM^t)z2QzS!DB4w2MkLRuOh}GVG**3&Fe(ccz^q74QQ45!6~(vB?Bo;mHkQJ7 zuqV2$_Ia68o#>fYt_Es3m{?M+X!jN_p>m zA}Tlb!#qg6kIIW2$D;D#aLkXRumFz1f=K>Rg>WKrUBNyX3*+Y)fm4y|3-+%t5@%u* z&ceh82+Z8dVq1U_EYd~i3Ese_cndjMx8K3$cn@3P z18j*8u@yeX*7y{;E@l52)&I~AqSXJ`QT>kt)&Hp8ss9N^^*^al{SWOVO8t-Oq57Y6 zsQxDds{hG|>VGn!`XAL#^*>pV>n?V+1FpN+e?_jl*k2*nUF^Rj*In#?BG+B?{nMxc zsCL2oHhU8CISadIMe%uwL;K5hDTfpJ+{B^%)DACIEH(fN54{f*B1qv&sReiZfPoA*~yQ{TK#h?-`zx)k7X`k~I+KK+pHhaW{h z)G$5L5A_?0^g~nVucGLObRYaE`l0%pkbY>W>IMDKz~V?hr1xV{^g~m-BK=T3t)G6V zK_8?a>Y;kDfb%5fx{&*ut2`DlpW4G>-fy(W?|FSHw`k< zK$YK*SQ7dC)S=gP2tPzVKXrVBM^Jem#R_=L;s3jrsN-0b=@VE3`TW#D1&cbxbX`2n zbOSuYbR#^=d7Ao}bND@;$EA1ym*GWRfpNGJeYhIqaV=iL^>`UK;uYMCS8*#|!|iw- zcj67)jW=;G-opKO8xP_gJdAhoDBi>4cpp#T13Za8;VFEGr|}W0-+GMdx1L}eK1J2b zXLuEVM)gzA@h1L)>aSklZTuD0Z@onITdz>{>^D?>{T(0SANUym#3%R{KE=QB87AO! ze2wZ~67dBlA^l9WMf-1xHf)J@Y>OO!9UaigbZ6xF>*$Uge;vIr75ztFbgufmBAVl`gX2*&$6v=B%*ONc z@GZ8t0N)Pwe@-3!4$~`{&dze{FbC7*9{nzELyo@=@{N9<=gBoXCz5Nl3y+|i=gBiV zl=;amI*jQH$Z^;aj~s^`99N<_4m+-6Zua9A=0S3a&Wq#{oe#++n&Ys8T%tJ+JIEor zAnW}ZISxBs;s>msemuG`kJEoga~yUgqnF<$roWDkL?=cCS-RIxbTr>?Pm4vE&WId` zo!PJ$)9+w$=BK}i<~Xc-;zV;Cc7`FxVP`mU9M-*TqB#yb3*d*Wk9Hr;ao8D&AF(|B zaWuza-HRuhE)!gk0(!`TVzApLPP$9ZQDtjF`ck>k9xKl1sLb09Y4@gc}@-Z=sr zv;I-Y=TA=6D~|Kd3CMBYISDz=J0~N@dFK@5IPX+F<2dh}iX7*i(~;x6a|X6W)i*wW za?ZkbtXK7}J${EBQ1z}OF2+u{6g%T`?1HPXE3ULtf{=Sk%HtW))qgyP!zla`-^cT^e;0qjws=wp$H=KZf;6(fj zC$ZfG{FL=2A)l`VsXp`hN|5R^pRWW3BcHDXrA9tq2}*-}z7muUr(y=2=CFb?A)l`V zWko(;33?l6U=IA6`BdL$q6@#lP@IJxoQW7gMm}E&^5O!F z#)UktcCd))61W&k{S&s26U=KFD!Ds2^^{0myMbXb^JT z4;q3T_k)HZ$NeC+7moWuqmbi%&}ihiA2b#@?gx!Sj{89qa6hX39N_g#MvnVIU*JJh zyE%kvH-~v%?dAxtXBKkY51Na|Q0?V7&c_qD5IOD#sok8yCCG6<=m$K5YCmUD{rEXt zjptdf+R+83)s8OWCXD0pE$Bmzpj~~D*JbnnTqT16nJcift1m0l2 zQ+N~4;w>Imd%BHkPj^u5=`LQzdw3P^quSL2RJ-~K)vg|*+SMc0r*`!iAL0{KyLyU0 z<1bU0GJ3Ayna=0VCi zAum#n3Hk6b=C}F3Uy@LV^;TrMuFVSWi}j54aX8Zrj17&AjE#|YkkG`~)Y#0}9BCg3 zEsQOVt&FXac9qb^*wz?pY=Zja`ggjl4e${+{P~f9RL@hkki~ z=(iUx<@w&mKE}SremIWj`x^%s2O0cpB5ZU-ZlS#bEl2 zgfER#jnj|fL&N9w6&cXFOKiBxJ@jK%@+`;p_U-Y{GcQU=u zxX8HJ_&x69`6b4sxS#1CjLVG6jl5qBrXNe-{i0vqFZ$*EqF>%G2A}77-Y@!HhxA(s z>x~zX{xjjCG0x~S#v}bAgYrKbQ=l6{dj6WG4Vl|$BWPD6}uFUiktcp*uIzGdi_%qhV=U5ki!TR_D8{)6n z7++#je1*;NH*AT&V{7~a+v1W^t(ue0ME%z@wHyEqTu!v*+0 zF2bBhfArdgOVN$XFceo{7_LGOuEAWm4#RN+=EhB!2e)8e+=jegQu}$GA9rB^q~55RqAQr~M7=cI8i^nk%$?0_zp2lcAi$(A}7R8HL41HJ}FJTG1f+g`9mckoY z8gF44yn`R&JuHh4koQZ$53w9R#u$8x`-=u`>3+s@M~&V=t_Uy|Fg-!MfNN>tjD`i2boK4#1{35S!y5Y>9)h zH4eeHI27CAFzkTCu@jEKE;tgq;VA5ZA7d{ZjeT$o_QSC_06)P&I1Y#4cpQcka0E`o zQ8)=l4X5LDoPl5AOq_wU@N1ldGx1ye z2It`{T!6E25zfKyaV{>!Z*duZhbwR%uEP1a1{dHuT!5iN5Le=1T!lw*H6F(`coNs*XDno z58ualbm1io#mnfyD;SPfF%MqDe0UuT;0-Km%=%Qa422FQsmRykAP) z9Dn2aR`@%%!9Oq-|HStA7k0$Iu`?!MSA31#F%f%W686Sq3&chQ!a6G0#&Z`sC;-{DnKg0CM`E+6i{1P)F z=fjDa@GH!WUt<>h2D9R9%!YIEE&LAO#`*XTF2wA(7<1qfd>4Pf_i#DBk1H`Jaz2^p zLe8rb-N^ZIVkmMRofw9j(Sutt7jDOJyxRUV?Ct3C)UT`kn5AFY3GRzF##JP?K`ot!wRt>*C#`0*NIIr6*fcK zb7FH$k1ddPoY)exVk@NmCUU$D$$=biLuj{&9B)J1$niFW_L|7?HY7K4yw$zL6S+Pa zQV_X58AAI^hVInhKo@3cQM{Uu1AKb-Y&t1$o0q&)mN@Z zhWw1n@C7c%SIG6qkUwxG{*9|J5m!4bs-u;-1|7H-gK-^(;Cf7l8!#ho#4NZ8-@?t9 z9k<|nxD{Qv4a0CdhT{&*i#xFZ?!pgnH+pdoM&n-O_>s5|OX7ayxRH1OKf)i8<3-{@ ztb~V<<3!?NtbtsQOvCXZk?Uz`>LJ(D(r{cz{MBitX@W0N_4F0C!QZew{*J1je_(g~ z6MN%dEU$X`cNQy+>SaP|D~;;gYo4EKSxMd){zeQVZB}SDjKZuKP2*?%NkxoBF(1># zFh3T@;aCDkVo5%S)AFT|a!o3YnXn9I#t)I8lFDLQ{0LKHIV7i~7$m2p@<_juQ~@(! zMI^VRDr|?>om3TDVKw%pna%jWjxv(wf!C0jJB(=kW z*dD1zNga@SlEig3ORtOTZ2G&iq%PPJyCU^4iR)|nyWXVkNWYiV$8M#Wfqii%_QP2? z0O==^2I03j80X;-q`ycSii>a(7{K2@)xEv#RT-VFwN~9l2()BW_>t&I+#<&)vnAY_&sq1C*H%S|e8;zTc zo3RMbZ!vB)ZZmGjqCCIDxYM}HxEtxmlJ*$)8uuCZBgf;U1I8bX2aSiY1kWEf9x)y@ z9z*)aq~pdD#*@ZVsCIbTc*c0vcn)KD{=8Aw4K;ny7>D$;Nj_t|@sjZ}R^a(7#;e9_ z#_L#-=WiHq8gChIBmH;M9phc&J>z|>%<~V7KN%kyA7K@qe{6hWd}@4#ReAnr<8!RW z^e@I2Se@x#jW3O_jJ$71vySI^-;jpmeG=~*(riNBH>BBuyl+UuaX*Ro4QX~D?;FzW zLf$u|*<)GBHu@LZd9od8=gAJFeJ4AS_M9AqwCChtq`fAmLfUI`YNS0Thal}RISta@ zlG7sXD>)s~u9DLu?I$?{(ms+iBJChK6H@<^Gb8msISW$Xld~fAJvkdvuanTmMf zNIgw{2dR(A*^zpeoCB#}$?qcdDEU34e@}iN>1UI3BK>Q!3+Zo@-AI3*9E$X-$zd3S z9;6;6=R)dBayU{il5->ZC+9(OPR@(unVb*FEjd4uS8@R)r{scI0}EkI`~YiVVXTc2 zSO>jW7bB75e{vMo$7pPTMX(_j#YR{R8)I?g{2;jmHpPgiw$IIk$*cM|j7RzHhtbpyYB6h$^*bysZC#-^hgU-LWS2 zz*^W7Yhy30gT1jX_Q87C7wcm`Y=Hf-Ar8PsI1n4-AZ&tzu_+G0W;hg^<1lQ29H*08 z;s|Vo9IumG<0x!nv+|F}wm1P}Iey!*9j3$fm>xSIizIi%1nh*rVrN{4U2rjW#U|e2(AYOPq(ZaX!w$1u=Xl9~YwfpG6pji!mC%NA*8TuqZA?^*=vgaa@M# zf0koOT!HF;R$^&fh3bD+qxzpUsQzays{dJs>VMW_3~s>kxDhMhCaj2?u@Y{<%D5HP z|7^pmxELs%aVV*@;b>VJ-7BRqlX ze@VGbx`ky#d|KmgTKk=yk=L*K+RcwdXusz2PVS@H-vgT!s&@C#W^pOUN`7SX-KIA^j z7xN?cX}(whxli-Og2;WEFBZc7_yG>U!Z;8ka1eTNFh=4KjKZN9jl-}ADxadLe2Ss+ zDUQmg1S+4BsC-JH@+pnVrwl5e4^jD)MdkAmDxY$wd}2`flt<-L0hLchR6dnZ`BX;b zQw5bzRa8FJQ2A6xNdPfb)lwNUxgM&(lnl}}w%KJ`%f)JNsh0F_TeR6dPSximrL z(iD|TGgL0kQMt50<tOLFLjGl}jutmv*RJ+M{ymfXbyKDwj^ETsot2 z>4M6oD=L?6s9d_Ea_ND}r6($vUZ`ApqjKqk%B3$Vmwu>R`lE6gfXZbcDwjd1Tn3|Z z8G_1XC@Pm>s9c7lav6cjWh5$>QK(!#M&&XZmCG1ZE@M%-e1ghl94eRbs9YwXa+!$A zWfCfvPf_h=GAf_XQ29(j+pvre6s(d%0%6BuWe7B&=cPpxVx1q{+JF0wlqRMqQsyz3g%5yKO zJoll>b3dv)51`8PM^t$pLY3oTR5>0&mE%!VIUYlm<8f3uoP`YhpOj3RQblE>iH#9J->`9?<=VCzKW{n*HHERI;#9{ zpvwOys{C)E%KtX1p5H;`a2J)sJyZ_&Q8_$7qpmO;YmCH+1F0W9z{D#WqcT_HapmO;WRnPxIX<&%u$6K5g$#MzL1;_OI1aSkM(I46=%To95^Tq-1&xYS54aUn=9acPiT;?g5I z#AQHoh|7rN5SIzbAucnLLtGXlhq$ar4sqF#9OB+a%0KQMr2ONuBjq2L11bNwcaids zdk-o9xc8Crk8>gA9_L2NJuVa}_qZ^m+~Yh*xyR)~$~`U|DfhVCNV&)5LCQTYFH-Jt z`H*st%a4?MTmhuq;|e0>9#;q{_qY#`a*r#FlzUtRQtokHq}<~ok#diVLdrcZ8ddH^ zP~~0}Rqn-5 zUl~>I)lubK169s7QRQ3?KvQ03ejRnBct<=hrk&atR+Zig!8_Na31fGX#XsB-RvD(B9q za_)jE=dP%7?uIJo?x=F^fhy;osB-Rw^gnUEQRUqSRo;D3<=qce-u;pOCvE_$+y|n{ zeGsbL2cyb;2&&wNqU!xHRQV4_)%y{sdOs5Bf8s`=a`_mQ%V<xDxYsq`OHG)GaHr998^AYQTcp}%I7;&KJ!rd%tz(30F}=|R6dJP`7B1| z^F1n`C8&IsqVo9xmCrI%KFd-0tU%?n5|z&?R6eUw`K&?Zvlf-lI#fREQTc2@<+Bl$ z&n8qpn^E~}LFKa*mCrU*KHE|G>_FwS6P3>{R6e^=`RqaEvlo@mK2$#YQTZG|%rPbE}7l~MUrLFH2w zl}|NPKGjkA)IjA^6O~UbR6ey)`P4z>Qx}y_JybsRQTa4L<RDxY4ce0rnu>4VCrFDjpYsC@dP@)>~2XCNw{L8yENqw*Po z%4aAlpJAwchNJQsfy!qjDxXoPd_G3yGa8l87*sxEQTcp=%4ZxZpYf=CCZO_}h{|UY zDxXhL`AkOT^BF3iDX4rtN9FScDxWV=`AkLSGYyr`bW}cHq4Jr5%I9lTJ~L7Ie1pno z7Al|FsC?$2@|lau=UY@h-=XrEhstL@DxU?Yd={eeS%k`GF)E*B+}Cs(F30J(0>8qQ zI0IMV*SH#I;u`!0*WxT(hqG}#&cO{h7dPU!xCy_*%{ULY;C$SQ3ve4Q#O*Pb;}z~; zdNJ7nk8aT#oy>FKa41!1Q|j5jWsL+=z#86CTFRcm%iLQQV5h za2p=S?RWxr;7R1S+I**w-)i%n#@%=Z_uyIFi|243p2z)o0T19s{1M~uAo}nS#^YhU zgh%i)9>pto46pKf?07APdxPV3Jcl>%7rcpacne?PZS>(C+nYS^qVl|l%JV)d&j+YH ze?sN?5S8a6RGyDfc|Jkq`4pAsGgO{Gqw;)?%JUago-a^&{))=;Wz3sAUoovbe?#T@ zJ1WmVPdB$f$@{E7$O`h>@$N2M%e+S7kK0A_Ud=4bf_;-;!c@Gd>E*Gd>#0GrkCtXME8&dBzuG znmpr+BYDP`K=O<)iORDSD$mlWJjfqJRz>Am4V7ngRGu|ZdDcYbSqqhCZB(9hOrCXP-sD*im1liao()iWHbmvw z2$g4J+nYR_pz>^r%Ci|N&*rE+TcGl6iORDTD$mxaJlml1Y>Ub>7L{i^RG#fod3HeM z*%6gzCsdxDW8UQ1g=yv46_sZQF#tR-gW ztvnZ_^86l^=Mq$&OHq0LfXZ_jD$nI6&lOB7&y}b=SE2G;jmmQkD$lj3JlCP}T#w3g z11isrs602J^4yHda|grh z04mQPO`Zp1-sE`*mFHnpo<~r59!2GO43+0;&WpTw1|#t-M&UV(#`9PNFJMu;h{Z4t zi=z)qU_6$@OQ`i-My>A(YJFEx>$`?p-*wdbZlKn86SclusP)}Ot?v$MeRomoyN6oe zebo9Mpw{;jYJCq;>wAP+-(%GJo}kwE6t%u*sP+AfTHkZj`hG#J?*(dozoORn61Bcp zsP+AZTHo)e_5Fcb-=C=U{e@cJ->CH^pw{;qwZ25u`jSxVOGduGV1@FVD5;H3BTxAs z&x?FVB9!kVu+D$S`Mw0(`uB0ZhroLWophfucne+_&;CG>v=D~x3|gpz9z@|}}^hQM4FQ zQ|^nVQ|^zZQ|^GL+Md*Qq}DIBUa9q{ ztOMhL^~%7yH6B>M#sk~Xcwjpk4{S^0f$eELuuY8zwi|dY+OBd|UlAzRb}7cyM+6>M z-y9gHfBk2?Q;PAXAvJ>64`>sS$Zyl0B>UMa@a9|h*=lVZGYit&Ca z#`~ujACO{vV2bfUDaHq<7$1^id}xYs9iP~T|81Z8?y`U2yMp?@^8Yo267_5zmva~< zF-&II$Z(X$$sscYULSvM`R}+R#kh_IrVMmH(0|5toD4i3l44xP$H3$1QjBLvF`hBS zc%~HN+M&R*SyGHMnmI+C(Z2p?n*IIHH2eIYY4-a+)9m|yrrH1hO!FH4GtKMx∨) zKhwOP|4j3m{xi+%`p-14?LX66e~Nmi`>X06)4IQ^?nRw>Ws0&0Jg0S}Sf)0!1j>prM~$8|5% zz<6isT3}rFL=BAVcP9el9q<1;uKS|~9`BrD|8<|#z~j1CYG7RVOAU-qS@`d`?wcBT zT=z~5jMu*V?|8!z|BmZErh$0|rI@FV?cc|BPt(9Wy02+qd}xX?)csWhkL$Ms{uzI1 z#_OV%t;7(;5NXu72KB#EWhm9}O7*!?{jF4AE7i|x8Lv_63#>y12CYxytWUeBIv8k6 znN1tgy?_0Wn>M3+|N5uN!LRC{mNRWa_x|-yn>L_(|N5udfZr^}z}1Kb47= z-Nm5Sscq>osdb-XSZ~HvHX9i<|8X;}d9E{TX3+A1^=Vq`R~DK_`@NMxWu?cpFFVb+ z<_U~zzXD~T?a>eUms4iiZeYK){nKU$yw)>JYab3W1nN}E^#&f(^Jf{fUr!md-ir*X zTiRxzJoQ-M^=MqL{Q`sL57f^x8JZ9IYJ88n}s(|+rDEvx<1xR%?-aM_G&UCKk{p!u{P*BG?C#yK|nAJ;s3Ui+=b z0^1La>p9h3tw;N)`Vg4bx-_oWrZU&K9@Bgp*Rp!eKbi5H7+8m%)3oYAz(5vShw8N2 zPC)ImmeI8KQPZ05o|)GE2gddI0|w=&Wwebz-OzKIR@rM@c?MpyR91Ri^+9!2Y8~3% z1~dK~&HB{;^&tcGsebAA{6-s#7>gQ<8H*d~Q~mRoG_szQ>-=ZEdOWahjR)4R@xV4T z9@vh?1KZMgV0#)5Y*XWb?XvEFwySZT3%s6xwyVbj+tqkryImNs%Mi=Z!5D*$7}}ZX ze%PC#F+(4Q_6(Z0X9_o@7~jZvH3rtvx<9MQ#K+hlaJZPl$KZMSsG8+FW#AGR?ONwg zZ8Gw4`@4LkT$W|Der>+t#wys(7MsT z?tq+YF31{C8aI4a>?j>+?%l`DML+JH2_%zaPxw-w$m& zFle7OuKfr+CRN7&r<;4%v7?BhFl-0I8;~QAXVBqh837WJ5ny2K89XN773|O$S&+cn z0RaI*BI99LU%A$yXf;Tdh#YMF`?6|Rb#-<38Iar>zq#s~lXnlQ50{>9?E_U6BdPwPx9{;}U)&K&NCx!sR&?898k$g4YZ>fxx1T?<+pS{KrP z9itD2UQCblNI&EJe~S?hx`wx7^yqv&j@ieWYy3EW|sn_ z=fr+Jido;?#jHoKZ)47_`H>Did*oGc{!<_Fyda=GppKr|2BR$e1 zJ<{WO?2VZIsE+EWuBH#F*GIA5YAA>Dx$h&7@~DRDIa?!-;+(IcM|z}3^_;npM{&;G zV9wkahw>lJ7{z&>M;-M=eX(`dD|WqkpA8+VqdKbR{Ws1Lx^C1L^~FynjTak_#;X~J z#-VXYkMu|{x9+`|9&7Y``a3at)F0`Q2I-Ohvlu<9qdKZHb<_{_!_G-BW_i!~_h?_r zGe4-7_}VMsz1Y?CFBboD@xNUB`-`7(qu*($H|Cu)XdPnLgyux+@m$a&J<=mR(j)yL zMvwGJkMu~7=P}O-J<=mR(jz_6J7?ncyovYDmownp=(l%{uV)b<_vd zv2}fzKJAG;NAl+}wiMx-^rWlR) z%@kw%8>_#bXe_#R^sHMC>Vw9k>(cKsQl0@>{=S8Vtckj2pxEj85t!JN+?xB4B-YdF2U!Uf!p}#}r`&;zieDn7A zDNTKS>eKj*(Dkf-zXjG-Umx%E?p1v~>+8w4Z*1##F8S5hTVMJ5r9SnszIplTT~oF4 z`Zw-e)r&o6jq8hjciOMM>*BZHo?nd~)<(mZ?^;_!*54-ThnW7WZ>+raHqURq{b=~| zT_Zi;wXAu*XQX}lT+sLY^LvK*y^r-fuULO%T|d7XtlzVw-o52Fua~*H|2^k4zOU`m z{jaY3({<8S&tLQF*zS-1dUgHA)c0I;UiXV`U60!OY}!vb)_tb$ynGsK`aR~_>w8c0 znKLPN9p?3&)U((8PW<-iKGB%-^Ozp;Xioj))#dc+cxPhqulZYQvzC$D^c%TtPy3es zqc;6xFKe*IU;pkubiPr3`-CIDH+eC6IqTu5`;Bxv7LM5OByl+6s~LwQelTOq+Wb4E Q|KMvgE^o}Z>|UJx1%{d}FaQ7m literal 0 HcmV?d00001 diff --git a/CharacterSets/CFUnicodeData-L.mapping b/CharacterSets/CFUnicodeData-L.mapping new file mode 100644 index 0000000000000000000000000000000000000000..e7ef6bd23ad827f5af75c270f91c85ed2ba0ad3a GIT binary patch literal 81316 zcmeF)d-PS~{y6$MXQ?D4Nfc5^DoK)r)TgAobVHKvx~Zg+gqBO9BuQ=|_mGfVk|e1l zAt512a=+hgoAY{qzVq`Ndz^E|Ipa6>IRBmYUi?WOK75*>Pf$481K$Mn0S*vy`_|rGJv7MI6A^9LP2t#FIFfZ8?M|^BlJ0 zP`2kVcHnS!gavcb?B49LJs<&t9Cs(|7?-=R}^t z3)!2KxPsgGB0uIzZs$w;f#7b|gZR^~pe!hKnl`*D9(V|5$3q5XG0#rM)WUh%%j9jcr=^x7#_>!JdVfn1h(LbY{^zEVr#bH zNo>oL*^V99k)7C?U3dz+@>F(XclKaU_Tp(gooBE&&txB-#lAe7{n(!aIFN%lm_v9D zhjJK)a|B27T#n*sj^TM6%kw#o<2ivBa3cS)X)oj?@kN}>i+KqP?4Bj*WxQN`1+U~( z_E3Hmr-`p-FYz@zjng?p`AmMu>v)FpSv-^1bGGsu*iSr%138z&coT>6X5OOSJdPIM z&au3c<2j#qnSM9#;k~?%_j8Kr4{(9_L0&Fi$VbGBI8FR09}_>$Ys8B=olo#ddaT`D8c7DPg{FFQS89(P2{E}bsYktFT`5k}YPy8|WXZ|Aol{fM?zQEu42mj>1 z_!s}pzxfZ7%+6!IWGuzf+=XRWmgTrB%X2qY;O^Xmd$J<;VkPd)%G`%lxG$@6KkmNF?fis0_$hbtGk(r5_$9yM*ZhXx@;m;__}stocmBa2`6vIyq;!&WE$x!S zQ<=59Qb71)Qn^DOSczTA^%vmzUDFCNKC zY|OoR6f3g{_u%n59P_M%XU1B?OBfcPfDiH^KFo!Dgp2qnALHX( z%qRFHpW@SehD-P?m-0C-CLa1>AEXtv}Sw&Hm#;#juk`E0{+Jc;AkmJ@g~FJL=P zWP4u74xGe}yojARnVoqtyYLd8!b{neQ+O&1?8eL3otLu*uV7DJ$zGhwdvH%yleJiz zhp-M0WnCV|daTa|Je&=A1RL>4Hs(=m!lT)g$FLcXWpf_K<9PyG@Io|+ob2e|_9Nx(Jyo-199^T9Qct0QD0zSxx_%Ijp5ia7Re2kBCF`wX* ze2P!=87|?oT*~LTjL&m9U*HP9$d!DFtN1cka}C$>6|Uo}T+i3Ifv@uozR8Vzi<|g1 z-{HI5%=fs3@ACtG$gTW{+xRiJ^Aqmir`*ZU_&LAem;8!f^BaE4@Ay4`;E()?Kl2y< z%HQ}q|6sX;+?5`rpTd;onQ=FkVg;7w?%aiYunhNPSyp95?#I2jKP#~s_hxlg<^kM? zbyaEQ z$@Xl;4lH6vwq_@`VP~GiGk7Y`WOts$o;;hUaVYz782fWL2XF)jawG@wTn^?a4&i8? z!)cttt6AVRyo}R%IcM++Udt;vlT&#euj0*|#anniZ{=*>#v3?~b9g&%iJTBuCT+6Y1h39h}$MIE; z=X$=x&3uFJ@l9^w1isG;_&K-n3x3QmIf-BKB7V(J`8y}_4_?SWIhp_B#r%suvChH& zbN^9~k`6 zTwcskyo959DW9%wJ=AvJ&o{Y*8~H5X;!?6@J2X+`(7*Dc5r+U*l)oz|Z+Qzu>|DINttoon{eh zu{CS64G-Z-ti!fElqa(;+wm~AXFYaceRgC6cH-ge%!cg3BX|lMu`7?{scg({Jc`}f zggtmPd$K8e@fe=QW;~t8@(ea-ZziSFq;6^HDIUg*^;nAaS(**F3lC=*He^{I!E$WG zU3nzSvoUw$QLMlw+?_{r4>skVJcbq7jC=7|R$_DR&Er^^$8#T^z$$FPeR(3QvL*Lp zEAG!CR%2^cXB!^ClXxK8@*tkf8f?dd*`77ofwkC?wb_Y>uruqh3lHTfluJ*_y4P;B zzw@TQTRG+iu!sZMnuFMegLx9=<&(BNhbL2BK5568S9{9KCmrZGPC8OvKIuex`J^-D z<&!RymrqWiynND?^76^4l$TGsQC>djPI>vH2j%6Ho|KnQdQo0Jk)KcH<&)DXFQ1%2 zdHJL_pQ5};`Vsxhw)vkG%0K3d+|HH!gfDRiSMgK6OnLfbHRb7(HI%1M)>586d4=-y z$vVo@C$CbTK3PwB`s6jr(Y>Cz~lxpS(wT`eX~`=@a?-RGvPOuTSOa6Z!h|Z*HYSB$=4XouvL?DQ4V-Hh)@{ zWw6)Squ5w{G@I}kHf3`j%j0<*Tkr(74w&h7|$CKHf zng4cV2XSY1;wkLHQ`wc>*^NEfgQu|<&*14ilf8Ks`|xb`Wqkx%n|TLo$c5g;`Qm$cH}B)UT)+qT2p94(KFTNg1fS+pe3nc2JeTnW{>GQNimP+i za*gzvMUkn&0tTe$UeOn;-du_@~@o__O#A{?32#PyU;K@gM$8`SRHDQu*>k z-aM5rPvp&0`SL{GJe4m`@ntzfa`x zQ~CQu9zT`8Pvr4adF@m~ z!TFTGPvr5_m6XR%}O*ko?I;4!Vk^?T6fpjai9Dac?$ZWgg9a*pyXx4EJR-R^_qWkIlJ1 zk7G3+&+0sZ2e1VX#-y2 zvlAPzGY@AMHsmQhf?esvlb*_y-I%dEOR)z_vnO|9FP7nHEX&hbj%RRJ_GWpW$=%q8 z6?hhRXJ78Yv$-exvmXa?00(mr&*2ab<4}&^aGuMN9L-TYk7IZ~$8tQ!@d8fZg`CKX zIEfc?GB4#NEN}{^^BP{u8N7}&c|B+G2F~VC<5Fg<}KFURWoR9GdF6L8wlF#sIKFcM1j!XGGm+=KI=SsfFReXu7 z`7+mX4cGA%uIH=Vz}NUD-{4!^$hWzP@A4hK$IX17TlgVA;78obkGYMXa63Qc4t~a+ z{DPnJD}Kpu_%*-dxBP+M^C$kuU-&cs;O{gZV25d30}|r`=>QrRNR0<1#syO20f}*e)ObK*Tp%4n;{vJifW){! zYCIq@E|3}zNQ?`l#sd=L0;%zU#JE6eJRmVHkQxt2j0>d30}|r`squisxIk(=AZbbC z0f}*e)ObKrMB@R8ae>r$Kw?}VH6D-{7f6iD;GW!rd$A(-W+m>! z%G{S#xF4&s8u#Y`tj>dYAP;5@)?!T_!rDBPb$A%-vOeqaa5mr(Y{(-hi9`d`?DVhasUT&5YOQd4&zXc;BcPHksQrYJda~|KF4xA$MFJA;DwyX zi#UlFb22aGB`k0XFXv^vl2`C5PUY2{#_7C<*K!80<4j)9S-gR>c_Zg=F88oY-NcH# znU#18EAv)X;cbl1e;4QT9^TFScrPE|{d|xM_%I*hBV5QwxrmSRF+Rb?e2P!<89vQt zxrEPgDWB&uzQE;tkt_HTSMp`9;u@~zD_qN0xsI=KJzwVrzR5TE7B}*3ZsNOqhwpJS z-{%&7$Pf4txAJ3d<0stCPq~AiaVNjv=lqIa@*955@Axf$;P?EAKk^s;%-{Gc|KRUT z4stvnxWBGbcb+Nk!2`rSd6u{rYlu(d+2Vd2%KjY20UXYO z9Kk^x$-z9ALpX}(a5TqoDbM3`9Lu#lpRaHn*Ks^w|K>B;YuPfz~fB!0<@ z_!Z^XlRtSe|HUb+sJciSGENAdI zUdt0WlPB^zw&E`KlOzr@*G#W`Hfxm?4WwOh~I#BXr3_j{A?aU*Xx{VmStJG@7|cX^+98$Va?V}8Ny zT%i0DKFl3lr2eP;+Vq`#O#B%aoBku8692?!_%om7uY8Wb@p<+B;tKJYgIyO6b{w!j z$8rGA=Rl6*Adcr?PT&w;z;if}LwO;GaT15~B97o>j^xEWmzQu9FXd>yQQLK+w)Mkj zxRFcv7N6xNF6G;Nj_+_8-{te%%;kKKFK`Q2@O{3>54e&a@+EHNDt^S5xs9v&G1qWA z*YXp-!W~@4Px&f$ay>ueYy6xW_yu2QJh;8mdX2>JDrS_w&Pq|RYt%3UZgot2?{ zbhbC;p|i@Af6n%yymMBC^3K`5lyA;j{o{W3ALm!rWNX%98`kDYJcMmohbQw;wqsqk z=V9!?dhE#h?8FA_%){A*4S5QWU{^NcsXUV1*qGgU6nn4RV`$tcX-4Bl z$+7Is<}_}kBb*kD8@VW^1>;7E@uPxqBP%N{7&mesofeE6xsOf@#*Gr=M+M_XiSeU? zaU=K9X~DRW`{=Y_+{k@&S}<;8Uqtk+MBYBLp zVBAO^BP|#=lE+93#*O4L(t>d#d5pAR+(;fHEf_bF$4CptjpQ-XLQ5JyDi}AC$4Cpt zjpQ-XLTeg7Di}AC$4CptjpQ-Xf^j2xjI?0fNFF0C7&nr~NDIb|5$9XwfCg0YD#(a_rrHap0Njauo@>Kup zZ}(<#-x6a*$&t!?FzPv%C*|o}UTiBaH_vFhw^xbDfBaoC@0ecXRqmkwdEZ@g%jfQv zTOoJ%+&yyl%>8fM*Ujg1SZ=-C`ne5q56^9wdqnPk+kR+1pJBPfb4TQk%sn@ERPN~9 z|F-?od_MA~ajl9xYq1f>_9-!@mDE|6w~;$7t}7Q?Y{b!~#As7eC;pCoCiaDrzhhsB zzoSh_S^QnH@0IK~CHv%Erj_hh|5-2o)^94Fhx}==@}vJNj_FYz#`~1iX=`yxUive| ze;e=ozm!FtD34<%_Wcs&4kBL=IfTex6dTVg##E!@>)*iOh(`FI%-?oUfbgK+7`Fhwz$2v#qG5%Zm(@|du@x`&!YZc zdOh|3(%IDiOK+h5Upj~Se`)j`_5aej)c;FwqW)j;b2-sI`i{{)`i{{)`i{{)`i{{) z`i{|F+v4`ocZ~M)xqx@^LEg=Wcn=@uyujFpZU*dZ8R&jT6^a=M=Pg!yK zkBoctpZJ%0KeMvwjK0JHjJ|}b#eIpIjK0Jn%A+q4_Y~2W zIE>MksL$w2#63myCE}hU`Vw(Z5&Qo6Ua`MNK5MC&p5&N{^F~;5zKFlq<pL5g^^nfBWD=LWl4GD z42_vYzqk1N86VNVti9tXj-N~EbN;Wf!dR|n_2o;(q)Nt|Rw_5nQ#=-9+^KjhW(i{~ zW;J6hW(8xcWGR2(i;OXnr&yBf``^jQ^p`8>YW>B%a5u(1a1YwfrFyXjE3X=>Hl~4f;*M}H}`=5U*EIDIVN7wM;*oCUeQOjj{aA_^yUBfJC5@> z9}QLSNab-ph<=0n{o=l<`~713Gy15}Hu@4JMqi`k@93A5{2hIUIB~}Nrny;esoc`J zyX2P1Et~t_wy%-T=iuC$xwUd@=N^(0Y|F(UD7_gIf|8%YDc&xZ6YUDAjjvN5-@2|s32Zf7%o!sgsTA3OV$E%+H*@^cpP3%22x zY|F3Mj$g9_zhNhS%P#zmUHJpM@kjRHPwd5?c{+b#Z~n?Y{DXb@C;L%XD;dDQIEeq| z5dOoV)XR(WVU}_vGmc^@j-lK9_>F{Y7mi~YPGDJ1WI0aauAI#Byo9@P3M=q3?#?T? z2d8pRPGcor!@W6!l{u69a2Bg@HuvQmR^?po$D6r7Z)G*kV|Cua12~@t@@^i)ds&0` z^I$GuO+Lh0T*%s7#6$QP>vAy<@m-7g&U?Z+%6RzUXT+OCj z%VW5X$8tTJa|4g#8$6yHc>*`F1>a$7Ze|;9;Ys{}ZMl^va~s=nJ3Da)J98(y@N=HR zFWHq}^HhGz9{ip?`6GMrXP(Aic{+dR8T^yI`4`XR-|R#CR(2NcQ(0fym$I{IAIkdC zzLWK*eI^?~`${&D_K|E5D{?UHBiT^eH?m>0Ph`VsU&uz#K9G&1ZJ(V>+dLb^8XQgA zI2%LTHam}XIF`0;c0O&>Y#eRNY&>nlYyxe&>;g9CMB0Ygg|zLmNo>Z8Xq#n|XBU3VN1?u5$~dHl|4$^D0_^yP4*f)a06|l>~(hG8|=zA*^L`%n`Cd%w#YWoHpt$l zbzk%jt@EOHXW$YeMt<$3SIfNh3IxX5t>$2!0T8Bm3Xx$Zk%rV?f>ni(% z)={>T)=l;qt&{9?S{K9c2FaSFfYW#;n@ui&?ws{A`nA5 z+Ll>7$7vg8p5ct=T8rX&PTR03e*3_-D~jhjZL^~I z4FuaNi|0FSqbz<4!L})i=RAL;XFTJ1)-0a){FN>BCldDnhuWO}HPM@>n)wS2pLV zJf7Xzf~T=1`>=>-u?_pOEhn%YFJK40$WC0zF7(V-(v|W2S2ue0D}IwDi|4>Ri<^1& zD>Mpi0rV_bGKle9*br{#P=3PU+`*Cjl%u$lWB3`z z(z9X7IL32g6X+SSWFq5vu}So-STdRM+}I`b%vk(JOBTBb!Rkki~Db zWbr)NHS{c5GJ}6`CTo^)zF;lRW^K;lA)L!PyqPO_D_3$J-{KuSY**(C9?84eg!l4v z-p@0*fW7$;&*Vb(;Ub>J$Jm#Pc{ZQqt9+X4xr85ZDL>>ge#GTGs=V_Bn{p-3;41d! zYM#lp?8|j*x0~|?J8}a%@eTIjMxMn@oWpmxj+^-^w{Sf_V7m&=7hJ(@T*K{L%N<ph(p*qMK_8~@^I{F{B~e3A`g z#`9U4<5`9i=pHGXNaI9VffaZI_u%cU$h%pI53n*9vI?JNRo1EK_-EWCRA!6u$1$wWv24I`Je(8QkP|J(5uC(EoUHsvx<+J; zIfX~@GB)8AJepJ4l+$<&uVFLJFrQ;Nlg&9x`Ei`h<2i>Xa4uW$W}e7f*^=|vig&Px z^VynrvkmX%NxYwJxqv70A-3Z}w&x;t;A8B_#q7i%JbDqO5Ih0>>7{BFkey`mK{>YL1ndkCXj^ghe%|AJYfAKv2&9O{M zJMNir97}UN%WwkA@dB3TM7rh{T}ap3qDidCi&%-1Sy{b{S%sIdDlg^!oWklX@IYS1 z8oZn}c?E0pO4i|2*5y^K$7yW9tJ#p(uo0)TF=wy|uVqutWHVmJ=A6ajc|BWjHe2!r z7I6;S@J6=fT(;v)?7*AZiMOx|Z(~=^XE)x(9=w~qcn?qKz3grM+(&(lqWh_jQS<=& zaRCSLK@Q?W9Kwe=lnXhWk8mUxaTFir7(T|ae4OLBm=pK}C-O;7;!~W=r+EpV;S?_6 zWtQ_)qH**zl<;$GM)x3jiIG<~I zxB0%pd%2GH^Hnb3dOpP0xR4vTh_CZ8zQM)j^Cq9s1k8zMHmmS!T)S%#4>_q~8D^5wo4P!#!c-wP;;eEA-Xe0fDizPu76UtXEzSVg;CS(W9v zzw+H!ofUW>cV`Xm!J6EYwONsMwA+hyS&8+O@686R%!b^DjaZe9)!UCvxIddJuSVZL zD5_51J19DUzHd+z`E=hi$RZ!#l97)uQXcvEHjI3{@&BU8$NT<3QRL%&Z=fjh@xCum zRF}pgv%~270a-nIzNV-?d$0j}@o=8bhV0EF*oTeSSGyzGkBvD%`B5CiCLF?}Ih0K~ zoX2n^n{kwO$8rptbFA{?C_h|uJmrRqPN45V6t$r5J`|lu-+3r%N%`Zf6{oO>mnm<} zE7*opc@n3w9j{TZJ!h~3XDaW=S?t8w?94fgvCX;aoywcpjkhZA&Ux&?JJ^%+8DpMz z^EBSeGkCvtXL5n~EI!1uxsWj)vB>lo&v=Y6p0QYYjAuN_7|(c`gSmt;p0SiMp0SKW zxm>$pT*2X7seA-iaU@ssT(0FPuH$H~XN+fT(C$3ykF*-gH^k@Dy>P1-yVximPyLiu z6Szry0o@z7n#gy=7gB$s)g*2fUqtuBtzw*Ei}+&dXSABa54>-ITX`9`@p5kG72Lrq zxsy}*Ij`cEoW`$tHNWLG{GQYKBWLhuUdvxOlfUyi{>fSVi`VmS&Sp|3DY}6f=dd(y zWEsw7Io`zb^c+Z1fwyoE-pY!+jg>f$m3cd>FrEde%6JxJf6ixh-o*oXH*4@7x(6+~ zm+n1_?xTCoqWf8w53n8=umK-rLq5bte3*^7kWKgqn{pAG@liJCV?4f0l0D8AT+Eir zpJ0*rNwyI`&9>ra*iO8J9mLPF6PL0JpQHYDt7YseUQT00>5J?pUdbNXzr z*4FA)F3|2aKEy>_$k(`t8~7Ms=VF$wY5v@mPqPA-ur`;n4wtbNm$MC5@MNxJd#++X zuI2!)<+)tPQC!c_+`ut>gXeK0$8i(K^Bqp)W?slGoWc)S;8tG2ZM>4(IgLAbHFt6$ zKj$O-l8g8?ALX~)#_#ztf8=)l%uo0$zvS=yihuHJ{>5+jHxH}jeA6gN>v30pP%lZ6 zvhD{P**@&T((KAI?8b8J!Sd|I3Ot>Aus18R4=b@RE3+S~Z~&`vF!$#2AELXOdQ6JLS9c*ZxVm2Zv2a(mi?Q zrAbsd)!lV6Qa(pr4& z$|SALL09{}7hf6Re8kO#Bt4V|Sw#Z~2^!Sy8*A_?h+8gy|ID7h@UA+mwBH48OZJNt?0pO-Xtz zN8gsD&H3l;NqQV-Oia?_dFh2odIINPoTM%I%H>IVB1_+pq%C8*&)Nlj8D>m+}D0Hi2w3A z2lEBnZwMc>Ue4j}<};M*M<(eoP8^)1!&%dEjo?e#k7Qr#<6QcYr*stC+h0fXAgD3aWm_f?=Ae+dcBpk9B;R=gZ+CR_q9B? z^B&&8=Y8Hgxz>4PK36)9?_z9^yZMUcyNAEn&iC>n>-|11@;UBj3+wd(j<>%o;11j2 zLH^MzNgrbDR}XXPSxLH()$L!8aIyM}ILzmHl(#znJ;t^^$Kz}=J4qMwSNri3Z0ERs zl2vc^{a{W%B}t#=BJ1H9K508H;S%+q<;UJaFI<|70 zzsh-|l5{#zHYzyoFDTGuD3nD5;U-h|v<>#(HzeVgk_&aM{ zl%#)fw*B%?t{9u7|Kct7@4tAu^Wndv+t_q!{*jUJoHkQ@k4C)+wbFntGBHG zQhdk!O7lwl&n`UB`l~`XZ@FBC!c>;cDDTGIo24{)#qPqz`D1%KJ&@jU5~lQadsp>v)?slXUlgKU$LH=a0id( zskU!Z-tKc9!!o^Gk2#dbvZc@2oC|my2la71<|v-PIgZyB9BRKkku%wnKe821ahw)$ zJ6rR5>#Gf~w11w&3v7?J+~59nGHZ@@J?1F;S9{hU>v|mJ_Op(>exmC!TUdXc*`8h4 zj;F9IyRsKg?yV;uevJLNNTQ1w&DLWs}R!<}&u<^0LXw{#?NUT*-l4#X(%n=eSn=Wn9PS zxnB8lZr}@igDbd^F<5?r^G?cU%-D*hSToHErP$_%jv3>8#&zl)!;jdE19!^`$MP$2bN;~NSf+>b8Smf;Y{nLB#}he}EqOOv@mChH zqVr{Ic4r$7c1XYMQR!c)bka2&fbu5YLE zTj$|!+@M}}{>&cSU3pJV7x!Xg@o7Agr*k3C;Eu{!p*MdNpUL0Yhh@%iKI8uE%bGl! z1K5uXs$_-!{E`Fsweo?i*4z1vhjK7CD<8rk;&b?IQ|B}O#bGSxIy{^$I3jl>*Y1}U z&gDzWM{zSpGy0)p_!G}#)oNK`EdLRo&oZt<|*W|U&2x1OF4p5IGF{0%*(i)m-C2b?l1Un@s+Hrzc7_2@G1`D zG!9VjYF>VDR=9?f#nbti=`;8QujSL6$+#cCjwU>-ljN#chY{e^{G%tv@H7x7U(%B_5i zKk{)#{$(*M>fb%VYJ8Gs@hLver@4vGu&eu?C9F_CD?G~+#7p^?>CbVfco{F}^W4GZ z{6YN}SkwL73cjrTMLx)tY}nlW1&5iwikI+Z-p9=i{E7?QzkMC$O|!x`d|&)6s~+$Ef(N@_{hkjr%L+fR zhxkW!G5sfw75~gT`3oZt@hhL>Z@jj7R`{K}Ij;ZUpW;8+#eLDgxa|0>@E2bY|C_Jz zZ=S<{SVjLd`NR2&DWkudG42mbaTrT;B6s1HEW-m&$O>iIL|l$XjB@?wD3<3@C%FFe zRdEHb;qH91h3h~6&~8tT8XfsX+v&utuoq{t5?3qVo5#A|Rpx=*ht*hxbIpHWb`V$P z#oUh_q^|-J_wBTRndm_uVbp7Yg>bK%grWf%?w&s4PW`#C9iYM{F3nIVhIN-^-?bt!w zp2wPB2Tm7vWLt43&ShsF&?75!VV8-n|BU=+SFY!&{6xKOtkWwibZ6Y>^x&=Ho_v|T z826W_@hhIrBU-usGwyqOvjflMDeS|^XJm!5_(1Qh&^PyNertL^W<`--v_G8b`p?xD zMSf8~kArxq>4Q0?PgWShjguq4m?iU-59R0LVf=){>4%x*7qevQ*;!#E2a3;S>U=ed zYs90OT@v|4`zOz16Vu1C?trXtK2PR2o}qj^-xE*Zq)Q{eXg?U36(;gV@rC@8lNk4L z7jerV*MHU;oE0u+KjoKjf%sC65l`XS&Wi=U?R_reV&#{!R3Y+<&M$2uzv%tMQ~6Zy z$S+!-=VXOx5%X%^U_RGy9;Y*|-!oX6*Yb|*T>m-3{I28m%4cypujlQY%_q7=e$n+# zJSSq_$mcniJ9!h2AL07X$iLpg11@*{=Rn@ZM>vnQcsrlx9sG=UGVW*Rv%B~%uH)TY z&wKa*?`6}GuKyfyZdSOTZ;Kyb{gWcU=z3~C5Ar8I#41<1{&U^=Sz#dui63Eu(OF>; z&(iKu_UB`KT>0bNI3_DB<_P6auh4)yxTvph^SCzldW&D8O^FwYmeJeN3jQpbeA^YPt zZoD!pe9UjSopWYIe$jU24(@e*TqxS3yaGQZ*J@hyZ&=jhsZBx$prq%ZR-7t<>$EmbFuP&bFTPr?sjwJ7yrk2Nd7Q!%BOG4 z3K_SHOYx|VkzX|ZmdGzU-dTn#S(bmZ95<-9D;wRG70R>HZINGeKAso(MfVTfof|qC zU*tF3ljqzX`9|gQUH|#J@=Dz0F8BZ3g85 z8F|Po*^|{+h1Gc+51=nt6j~Y1NJ`0lA-?+@X1S$u%jK45Yts+qNsM-3vsT zriU>-jOk%a?-G-F3Zq^a^}?tZmQ0WOJ;hNkjCx_z3uF2jd3k?Ey)f#9Q7?@7g;76@ z&ohwmc?L7uh0!jIc45>X#+V+)^f0D}F@01_=4eK}FzSU-FD#iJ^~Z~&UKsVls29fc z3-j_Y;#MrpSne?5GI<C$p3GQ|b8;h&`9vH>9P^1djQG$z4%>;N{m|U@jP)Da zBaAqfGvYAfm~X^k#IalvhY`p7bYRSPc&=x7ir;rcZYRci9hn<(EKkH?#PRte4r4!$ z`muk65l6j+=$EOaTswdZ%hwky;fta=P=$kjQ0)W z_=9sIO;`QQa{cQ5r?Y~>u*PH z9jEVz<9ny!>b!hsZX7Q$pE!QP*pI{5kHh%9u|8vYL*GR$o=<##IOg{Zquy-u594?U zW4^JxG2bxu-&nrbf5Vp%<)7xpazz}=75eUcvA)AzY%A`mynk-Qd*yK$=Z%;i=Z!Gp zD33Ud?GWX$9m0sCJmN4uXOze13?q*6h{HHOqCAd|FybhWIE?KW<*^;Zh@(8>FpiHp zjN>DW;~|XWA&m1>thYE%h4J}f|BKHX#^*Vd@p(eOF;Kkzeb>L(w{w5VjpdJavHW4f zeEY}uhhw|N{vXEi5XP|(>wSSZygrX_%)Kf1mR$c|D8=>e%Y86+U2fEidB*hcaj%%K z-@z(2ev>8QXLDcuk2vbT>=nlU?@H8*ID947cc_ZvFzW47+Jfgsytg>&mtn;F=W*4% zUM-eU9&y+#_vn0j810T?%%@i#H_zj!AMe+i(S96@w|{Pxyu4{{#oR{!5y$)^4rBRa zKJmWc=Dhs9+?Zd~i}{87<{pz9pCh(oELRwD)QdPgFte@Lmt1K`(mzVOpEK+-PO3c zSEJmca`(xtlDlti_1uGVYv$I^JtB7vmwScpdfnx<%duDD+KIL_u*Vx=~TxtZ$9`S6inD4yYyK?W(eK>a(W4?z}bguG>^WP!%t2qCKMU3;`f@{zesDncFs_&JesR4FBaZbJ zao8!}uAOruj(QP?UGsKhawCrAh&YV?K^Xmmk~o$t;;?tVUe3&o`mvlbJ&gWBw2S^i z7;&_VxVXLLi1IUY`{l;(^+fpq^9={)Mt>vX=x>A($9y6VBaZitIE*;fQ^a9U*5>%! zh-3eWIE*;zMH~*b9I>9__khCTjP*Jqx0iaQ#1nEOj^&6rjQ)EZSJ8hDBaZn*97Y`T zi#UupK3BwH#4(?U!_)KfiMbI+yNJW+4@JA^4~5b1kNbq^_lFV3d?F4bj`b9A7{^)3 z`YTzkxKD}sg_ru=G2d3=Xx~3K;$8DNTq%yvw>>xF`1}!v5l6j?vMjS>Q?II2%j&>1;@qOd?9PxeQFwR?1FV0(G#I^D`JSC4u=SCd$A`Uk(mgnc( zh+{b-4kM0w5tr1D<%l?p?-$2%#P^HC`PNg+FMdBNj5y{KaTsyTC*m;Tm`}uE#I^D` zjN>SbnmAw+*cZ&I>xe>?mMjS?5l*i%qUeP{`_T}<+yXHn5?II2% zj&>1;aX%65!)RYodA$E#xe-UZh{L#_iTYvGubS7}FE`?-7jYQ(DN#R+`Ze-;2j@l{ z^&$=3HxV!7(?kp_s?(XioxV!Db3j6=w-P8Ha zweRy@7vYl=IZ4l*q-iHfRhIf3run7j&(CnpU%;rc=2Ka!toc-y`uwH&rRFcraLpfL zR9W+>EcLli(@RZX&P-R{sIsP0S?Y70rk9$&nwhS;QDsf1vQ$~?rLtT`r}^qr)_O@z zCpG>go$8mGu9=xG)TpwiS6Qm8`BauG=`_F8{B0So<%Ahk)_f{Ul{KHrQu~Fb*Y+v3 z9ZBUW)vi*nUur$2#r&nwHad(>qsth@7-WoUjAo2(jA4vvjAe{%jAM*zOl3@M^cvF` z(;Cwm(;G7wGa54)GaIuQvl_D*vm0|5m4jZta*)l9EsQOVt&FXWZH&r6^C<_}&e-1A z!PwE*$=KPb-1L4YbsifYX}IcJLnk#}IqQC2l_%3_KB@Vn@{}6Cf}*C^ecvi;dywml zDr@{PGhW9j&9CD}Kuy1&@hVGgcT)3dyVCNdmap$)G@Z&CudX?m&Y^?i+|KS-zjLu&ufculYIav7cGlbTOzzQuH!UhSc>?qe^F+WzG^qsppB z_up!`jw_m8`(r>2pOl(o7fvxwGhQ%WF}Kp~>}Bk0>}MQm9A+G89A#W$Twz>kTy5-c9AKPmoMPN$ z+-%%p+-kg=`QO)B(Z{wzSJ_zASlw9DSld|FSl`&t*x13Y;A07>>SBC^mjF7 zsd5ifmMZr)WvOyMQOO)_bh5KC+}Op~6?f8UzFkJ$PoVj9KY>(P^Q$cLa$KkthyQdO4Y zmH!<31y9V@F*Ayt)qf1lX{YjkhOBO9_kU&`WC#1t$+z0c{GTz;+Cl#BVlSiqhH%qP z=l@RnyB){>H;7n{SpH*mGDjT$u{XUVp8s!F!48-IT)&_rv;Q-GO9$5gGJk7Kk8K&B z0XsO7`2Pkp)Dgq~8Gk(W>AR#^OsDSv<~V}J)y$L#U=Y>zs| zHFWy?`_wpR3@gMsz-QYNeBM06cS&FEsyy|%wsn*583w!S^Glzh)_p$nKIe1kYrbRp zW_RYP%ss4ceE%@S-N*XmUSYlQ9O4&LPx0%fSNLVpJJxOcq4kUVrFGE#*}Clhi>Jdn z>}*K^X@-*d`&$#dHI(8J#v zT*+LqU3p#AT=`u*$<)K+w>?fzG*1jqbWcoAJWm2od{078GEWLma?cN*R35JB;TM?`?20)xwHux3XUFJ?R%a%zDH3 zkvZKRc;a(MtC+hJPjv2Ved0U0+U~AaZMz%ami6Gqz+TocGWnbDZu;6Qc>2d7e&O~g zPrp6JlW&jnE6b<&CED}+s__+mN%ktgGJM1OZr`!4lh-o$6YEd=sr9%0%!*ES``z!X z9OQS={l%*6{%ZYW|HV^(9rikMTu+Wk$T2B7)*#24?zVPscNo6}yvV*qp0~;KIeESy z&ymjQ&e6`9&N0r}&auvg&hgG=&WX;I&Yzqcook$1ook)jo$H)Co$HRVEi$c5rj5z8C7HG&(+*_Xkxb8# z>91sZh)koA=_xWzX_w=P-sO2(dIg@$U6pTEYVri^+SY9{>~7cRDb)@5#o|!D$!cLm zC&QOy_@mv{N=J@w`8#r6vir>UosG%tPcrLa_py4}eXYsvMea53#qPE49qwc9o$llA zGwvJiv+kSj5bH4e=pJ`@o*Z7qy2y7ySJ_9~+I9G)<@)^UZm9K$eRQuo%*teUvOe&g z&=>a6FuSKUfbHR%`w+iqe2QNizQQjBKeSep?@_Y7?snK)$a5>X-6yvPZqlb&u`pk^RNW#%~2=;r9Tp*)Q$a_AC31o!)WZ&fs`pXXmNUIUG41 zlbt)+?{BitzhHmg$bNp2ef)tZnd_k|jq5G@^dnbE*E?6RJHI=hyMViayOF!0yD{tQ zV0{y@zHV~K%KBDeeLJupIa$}ltgDB%&&Ik|WnDYk8F@-+CdZF#pF3FI4VL$u42{n%E#w4slEzc~ATDR))=VqJ}2?ykYFcGu*WscZ48+jaRR z?0WqAb$x#Ex`EY?_0HgEZaLj8teAF7E2g`ZmDFx+dEB%)+ff>B>kZ);P}<$u%HasN z>T(RI@9t)Wy1QHLxg>j(ySFut+<$WS<;my`?3V6^{OWfzyR|#iZsQ(g&viFeTx11H2d-uXOJtZE1v5TZEMrE zcU(zbztX;sU2fM?+W5UIgX^;^i|eZ^o9i|0eVO*IPJ7>V<#XMm&7Zi6x}MSQA6#W! zUtASk-&~blZ(Ma;bzL`Hw_G<}w_VR&FI_KOuUsMg!gpzYg}c0Ug7!Y=uFWr8*XLKN zL#?H>>pRyKUT7T9A^Kx%Kov5{bMux$FJ-k_t-yP zvwysy-Hy^uooJgU>}Q?rUe+w?na#4QvE7BUomFQ$>td&IePn$b4vDp^l zvOWL6_WX?Pz~czxN$u@;T6%lyh24Rt|99fa{+)ShJKF>A388j(s}<{Ug=5P=j`KG; zhTpQc+i&eDJO!V9+)>(cu@2E3Re7R$4W43Nn+-PQK&n*qq7y;(bbCY=wT&x zba(Z1^>Fp#wK$kBi1}hLUmWI3z&kW)h3zdl$IcwI<15;+8{2RK zmYs|?D@=P;ppC*==bp4hLY9-9*I$I!UXj<^h1b!GcFs)uWMQ5Zw0Bmf?ap*R&|cXX z--Aqo$RrN0ArEyHq|S1*e-rBML|adzoug8BThr0uq&}D0>2-Odd82z{dSiLhc++~* zdDD9fdqcb>y`{XRy=A@Syyd->y&b)w-sawx-f(X#Zx?S@Z+CAuZx3%zZ*OleZy#@8 z?_h5~?+|Z)?|tt8?*s2p?{M!h?+EWm?`ZER?-=i7??dkt?<4OL?;7t4?^^Fl?>g^l z?|Sbh?@RAy?mb|jHn!bkY`fRkW}mXn{>FLibu!pZ2gXonhbk%d^J0#k0p6+_oXlDE6wa!r zaway7GqLHc{S4NAChI?o^`Fhz_8iW(=W>QNk2AFSys8Dfs)elkBG!E|XVyzNvtG*C z*w37eE#nm}=N0|JYg)l;TFDvrD$cl9bC$M-v$VCm(sjJj^`3CgCQo}lhDCknq<)EuU2N<3%C}Z?Y(S6Z#M$a8R zPxRx_Pt%==UN&aMm}O&@qpKA2Sjk5|Sw%~#P^$yeD|#aGo=%~#!5 z!&lQ+%U9c1$5+=^&sX2qz}L{%$k*7{#Mji<%oplw?rZ65y(~`EK}b`fmH~_}=+` z_r3Rh^?mSt^L_OF;rrY7o#l67`Q2H5FP7hz`P zit|YS_n^GjtfCxBc|AJ#w+VAm-i*0%3%VG-6?s0ny#x6u?bwMq8NUnpTc^DTqw(#+ zUgRUPb06|`gEN=S-@=$aHzvelm>7#=QY??jQGc)IS_7NkMDhQ&=u!NU;m44#VjagZ zHS!xNRvJ8s>F^Y0z|)us&tMk(fZ34WDzS3lCuDsc8+egzSupQ;i$h8pm z7VN|Dt=JFwtp;ldZb$yMZ|~4}yn(cj<0kUmisKgM!`oN@?_eRki+pG6xQ{KU=K*$L z{)ed7`v|!vz59yo1&8F4n|YamhQbW%t6` zl>1;^%u0Byznu+_VRkHmKIGaaJ14SSI~Pv0Eju@|d^-=8!@S6P*uhu<^C9bF=f_G| z09h})AXdS`$oknu&`UcO#cGs`VGS&fwJ-!(PrC%No_0wL#Zp*}`ATDTEQ5TFWS7O} zj4y{Ru{^fI3Rs`%Dq=&dgpIH=vYXjeu{~D9j#wQl@;Yl^XUa9P7Sq?lBrK;kCdE1! z&iK069qVBatdBo3z5#Y-zJ|!PD|RDH$MD9e?W75A#-_Lho1wOoP&|&!F^u_Iptkpx zsQO!>>T8Xvrwyu}w#eUF>@ZY4?NIg1;d+GnG|^sm&W5PJ2Q|h6*cA159NN|4NcHdW z#%XjJqZosXQH{}z(Ty>T@iAU1?kB<))SDKwv%cw&JRIqfc6a7L{@JYWWjU%U^**dHp)*4yV0r9EA?# zxM4eSEaRi#c!mezPZ$*^v7JZ9DU6SS!r~a8TuNgprZ0mnu`Fsm%Ax8hk8Iy|Om>=7s_RlcKUrdkv zFcbF2tT+I3*p_P`a!%(Og!ynV7Q!J|42NP#9EN3aIP(7I8i7@DB-X@HSQkfQL)LQ) zHpa2o6vtsGj>i@_0bAijY=b{x7*4|WI2k*#o>MR!r(!pphCOjQ_Q4t0A7|nqoP|Si zHjcnKI2z~TIGl$QaXwDQ1vm{C;!IqGb8s=v$0fK3m*P_V8JFWST#3tZ4gP}baRqL| zmADmG;SOAlyKxQf!?k!2*WnRdkH>KXaxU!Lh-YyVUck+G8Mojy+=@4G8-}ojZO0P0 z1GV4n#8S8mb-de+I^ONUvbYy@9N&jJ9_+^ocmONnLDYVH2({lHMjfw@U{ySdI$j?` z9j}k0j@KuU^GW+8*1}U*8&6{$JcD)dEb91i4(sE2)baZQhT=tRj+apT@nvj@S5U{# ztJoT^q28CSquzgRppGv$QODI=*dA}Ajth5C$AP=3_vK%)Gv32+ypLV*0d~cQ*bN_H zcYKUJ@CoWT^%Q&IGt}|uIrhO9*cV@--uGi==H3bRkM4M%+DU6N9ZVCbq{|I22>! z42*-TF)rT6c&O*M#Ycj*6QG`Bn-ITYB1~M9_i;>tNiaVq#Slz}jW9WeVG10IKj1b@ zi6`(!e1vYiUdOUM=&5Vjsqj@j%TA4s`j+j*7?=hVV_M9B>98B7$7&5NI|B~JjQABZ zVcbTRof-8!uPm4!v!b5kl?}UKcD&t~^8|c?KKy_=@jK?iu1z>!z`mFV2Vq|PjKQeq zYURV2O*vn{gjfKpVL=SVLf8!p<6$g<(VBDqfWcS{D`IiX(aN$zFb|f%QCJdZVk!I? zOXE%~gZHs4KEZPMzO`kS$7pT%oP!y$BBo+HtAx$5GWNkLxEZVBX{?4nfv4Nk$f*puh%gy8^ehwHFC?#B-J7CYi?o}beR zA7W?xgyC4Q59cYk1H0lr?1tTWj!k#e^JaVCH|&Ys`tx}KzhZ9;9%R{lusZg|vDgpy zVt=#;bDn~+a3ChYL6{o{;~*S@V{j-w8p7uVe2K&H6OO>Ya3sbW%I5|A0Y{?;$6zNM zi(PRX_Qvs8c_g0~usu%1;yh2}C)D$6C!wB0G8qSsvFs_R=h#lgOE?Yr;9yV3eB*ge z43@!}sOO2y!rnL==i?k~$#X&GVmQvj%{U)dPqgd>sORi2#JM~tWD#z_#kduh;AEZ; zvJ}ft<~cEV440vvGrSxx;xE{1I-h%RFs{UuGc0=*Zo}2M6W8E#T#MCb@|-wqg6pw4 zZon|yh^KKAzQN7-3Adolb33-;3fzXbaXa3_9XMk)@54A7ci|e`jVtExK8!bTFW$p_ zm~Jlb!Ej<5`TekoRFsi03gCUcf?l5i8&&tbvzt0$#y+coi3+-sDozB*B=#$MFyKK2QKl zVL>d7bMZOvd-L!mF2vXP55C2mIoYn_bG$_xbK@$^gAUA#YcLqsVm@?Yesp00jDiJm z0~W%KSQs~95!{SLaSIm1tymmWUF8#8RKIWOn_A}6suu#td1?P2DZeS*a~Z5YpjiJunxAxx)_G_upQRN_SgVB zU_$_*1763DcoREeO6-hxF&yt<7yJ>sq8qzmD(sG4?15>pCuYE2mzpz2JDR)k-sB2GvPqYjQl;pnFWVqRvd-da4crW377*Xp%15GPMm?ca5m<~d6)+m zVqRQ=!MF_b;R?);tFZvC!-B})KAeS+zjZhZBY)d)7QtOu6!&5=Jb=aVFoxhUEP*Gn zB%Z-)cpj_cB_5Sm1Fv9ByoR-qzm+&@BYz`t)It6>;;4(iVm-W%_3iluXoyd- z5kAMp_!67oYix>du^C$I525J5=IF#07zJBm5Vpdo*czi_8;prF3F-=RKVO+tN6n2ed*avZ|wOg9ya_Te~$ zvAMi>I`-lGdiHIry`1OAU@gc!TmB%*XL)F6#5xJPhM}Z9ZmZeHUOj>$wo0 zvtElZ3g=0SF@9Cf?{GHLEyWgm-uM}H+*^h^?kz_h_kO{rd>&hYpQvvo7Q$6Hvmxhq z*c#X1qn@1Kp-uhk@Hp$g9&0iE229C%Zp6govk8;pW=xJ-kmvF^w&IVt4L!IWQ{xUy zgFBIHo*lc8Yn>gtF%#~=EVviBui3E=bKri=i3cz@9>lzO2=n1#EPzL_5FW)Mcnpi- zaSXu|SQ1ZSX{Y5ph2`)xR>U({h4E*x8s&3Xlk$12P5A;Y;XLgk{*0H9djRaqxB{=> zD!hu^<6vJy?s2fMBlkGiH;{WA?3>6v4)!hN9tZn2a*u<32f4?=zKh)BVE>BT<6z%I z?s2g1BlkGi50HBt?1#ub4)!DD9tZm|p28=18lU1Be1>Q7IiAB8$h{EuOXS`M`xSD} zgZ&!0*TH^++~Z)s#b5Cq-oy8JA3xv&{D=?n6F$bz_!Ph3bNmfo;P3bnzv3(WhOhAt ze1m@?_fXh>A@@+&eC0T>epV=NrT_}DlKl4=1x7#7F*K;Y@%tm@XmC!9=)# z@riK}%S(dGoK{d$TunI{ZeYITxS4VaZtGvZUsgwL5SGrqzs_?CLJ;wQ@4@Hgtu zj(;#b2mZrwA3FG5;+*KhTo~lCqU6SyT;HAt<5JFxiCnx7V^YfbFeTIHM>oR@U}`Lg zX|WJyz`~dri(oeDD~dTO7sEWvUmSxehhPCLfrYUo7GwHSSdwySEQ4jRJeI{utVcPl z&UEFmHuY7&x(u&~jVM>bCXBC)&9Mr$X8x)ehSjhGR>#hauYui|uO{|jcrEPB@Y>j) z;dO8z_1DE=jIW0y8D1YpV*?zE4RJg+!im@zCt(wuf=zK6Hp3YhinFje&Y|8GI3HW$ zB5Z|Au{AEoHnW} z<2i=+z>Ac7;$`fGSFtx9u${2fQ(Hyn+BvfMHFo%+V2gWvHThb|nCK{x@U;Y5sqKVdAKgmG{( z#*1P(r(i;yib-%9CdcWRlIdok8)srFoP}PTjcIWXrpLLM5$9oMoR3*?0cOXA=%e06 zm>U;kFvFK%e#%R+5dMrsaT$g%emRz;{0o-B6<9usgWY0FK0iI2sS(SYFRzoJjcyPQs%&1&`r0#vjL7yq*&{m*FRIKAysb zcp4Yu8C;5IaT%V&U+_Gx#0$6@FXCFfgzKsIGH#-L1-IZ;+{W;0$bE0l>$r#EH*g>8 za}y6S{1zT%_-#Cnckm?M#nbpJo@KgwcmeMte@AybK<=q?JjCnx2yfzJ5AMkJdh*ps0{DjkvTKJCu?I?bYOOL zqL1+|%#BenIEbHq#{7&=hy^hb7RJO_6q8_aOo}Bi6+Vn&*{PBCw!Nt97SiAqOpCOy zoeu9{dZeB03`jfL8Sy1%LS0`{6-!ZXHS7{(+0~6Tupi}`##+YO#yU8V@pX;$jP;ET za4_Q=8XFlK8=GKX#y2%KGlm+Q;~>VjFt#+dGPcG(jBjIXYYa2C!=D-7-q^v|(bx%> zGrqGi+}Op~6^Apvo3XpGhp{IPV|*`TZ(|>0UmVH!e#ZXB0mgwiit&StgN;LsLvaG* zhZ%?Cc*-M;BXJ_-QO40Yn(`RqSe!+9oN+u(raZwo5m!?F$vDY4**FE)Gk&UZnsK^u z25x5jOyex$Y~vi<$oRR&dB*w11-Ogx3yq6#H|52~CB~)3pYZ_Wml>DiLCU`vR~T0s zSK&U!uQskRt~IVho*`(jH*Ubols6hL<66pBj8~1Z5oj$C*S zbK?cfgO@NbUcq3zhWYRY=EqxD0PkQy{1pq~eJqR*u?RlKqWBbx;d3mGFEIpPV+nkV zCGkC$!jD)QKVupE4a?$JEQfz!dHf42;CHNu7Qg#f2_0A&U04N!uqsBwY8V5nV=SzJ zaj+)F!&;aCYhxm;gGsP1Cc}D|0_$T+Y=CZTh^ep*cOXp7?!|xSPI)?8SH@Nup?H$PFM*$V-*a?YS;y9 zU{|b#-LMXJ$9mWU8(>dtguSo{_Qqz|2b*JGY>EA_HTK80H~`z>Kx`m*O`38F%0^+=a_=5B`Gta0MQ~m3RnO;SpSo$8Zgvz_oY^*WnplkLPd$Ucilb z2{+*t+>F<73*Nx3cni1T9o&w;;tsrzJMkgz!pFE9pW+^Tj(hPX?!(u(AK&5ue2)k5 zBOb!fco=`fBls1M;vaYn|H9+=9Zw)Pv^Y+p15cp~Ph$|C!Dx6EW8gWAh37F2Uch*G z5fk7gOoW#)30}cucokFNHB5=u(Tz7S72ZTI-omta8`I+*%!qd}GyaNM@g8Q!`{=_5 zmK9!ZP?8%i$ZWfN!x9zQZc`9;@L8 ztbre~Rx~~@VO{);4e$##X8dp16o1E1{E98`8@9qfunqo+VfYue$G@>7e#g%E4|YKw zw_tTc8+)Jwd!ZBipbPt96dZs-I0&QS5R8VyFglLF7&r=J;uwsD<1jW(z&Q95#>L4P z52s>$oQ?@_CMLw$mOB}Pu1}w z*1#`V6Tf0D{1a>AcdUbUbjw*6U04sJVttH(4KOw~#CX^U6JlfJ+2774m>ip8N^FK6 z3`H+C$8^{Nd6u}dCGsqAXDiH(tuZII!93U&^I;ek#CBK&+hcL;fF-dbmcdR~9y?F2Dco`SsHC%)@ zaWUS(C3p{);zRrypWrflj?3{C{(^6D1%AMl_!(E>@3b4Zo$O36_epM`~kP48+Tx8+=*#%7iPfSm>KtAHr$Io+=sbwKL+Cg zEPw}*=hQn7VKF?6CGZHA#-msck6}eTj#cnIR>u!m3qNAr7?$f3HpI`^6u)2#{0-aS z@7Nx{VrTq@-I)Im>>0yK@F$LrVMY53Co$f~%@pZUSs^(wQ7X&Mg$Xe?HsuhI$Cwv0 zQVzyUm=F75ejI=WFcE);D~O4)5XQs87$1vZ3@nNVh>*EjGe1kP!Jaq|d*K4?jf=1kF2TO|Gxo#f z*dJHm09=IwaSaZ_bvPLL{?#6WUL0y1hG{4dH;%xxlt&szVLHmAjbku9<*~+b#_`4p z$oIPTMB`7Gk@6(tWaAX$RLsQqX~yZ8neq(dOyex$Y|O&=ImWrhdB*vemGKLV3yq76 zi!mGHml&5Ce>N_|?2KP-{KdG!xDs8ifp` z#t&G5@<-z*<7eX++`#_&8*akiaSMLMZTJm$;2*dP|HM7`7w$v*pMQ^g4&)X-+le}k zxlqTUD5&F55bC%V6*FKo)bTMo>i8G~b-as-I^M-X9e-n^j(c%X$KSZfCC_#|Gc0%M&J$540{ep=xFD60VFPIc{zhE-d{esC+_Y0;#9nXJ29p6);j))cZ*h)ca^rY=XtGDHg|O7=pTAtOPd4lGp-EVM{EHt*{L0ezCHs`^Cy( zTP%-ZSOME%MQo3iQ1^>f#*SD8J7HDqjMXq4t78|efnBjCcEehz`^9Qw53GZ_U#u?b zezAI}`^D;`?iXu-x?ijz`roIqKjp@#_v@88W)=Oryr=Orys z=MAm!Pi&34{-_Nu#N>LSsO!jjpspk9iMo!g7p}$LsO!l3pspk9i@J`iAL=@?{;2E72B5AZ8;H7&Y!K=? zvcah9$cErS9E!S*Y#8b~vf-%f$VQ;9BO8gjj%*a_I&Pact|Oa>x{mB8)OBQ&P}h-7MqNiX1s~v4)OBRjP}h-7M_orY19ctQOw@H`vryNO z%|=~EHV1Vb*<5^s^HA53&Bsr;0C~2ky%2d`ue}JlCO>2`=ENnK3zuSU{2BA$GR%w1 zF&KZre7FMh<4P=mtFR!h#zMFT3*%ZWg6ps-uE%1y0gK~C48cuU0ykqx+=8WWE0)G> zSO&LaS=@o;a3_|>U04BkBlk>%?7>R77c1jFtb&KIDjvpacm%8CQLKT-uqGbIT6hX; z<7upeXRt1w#d>%S>*INBfR~VaCPFS_BfNr*@hUdKYuFU8V>7&kp?Djc;~i{)_i+ik z-~(KW4{;4X!u2%SV_Z)82`o5xTyDmc&PV*_^9`R1gQ6cgsAs{M5y)BAp_3WtQdJfca-G@4^=R_UXbD@swxlzaUJgE2S zyr_JFQTgOU<&z(kPXSau1yT7FLgiB!l}`~=K1EUa6hq}x9F$z@+pVPr#vd33aET4qVlPP%BM0apDL()s-p6#hRUZpDxVstd}^ZdsfEg? z4l0+rs9frya;cBXr2#6JhNxT`p>k=A%B2Y^m!_y(nxS$DMdi{Ql}ig$E-g{Hv_j?5 z8kI{MR4#2%xrCu|X@|;50y)QR4xNhxeP?*G6_T7eR-|eXO-GOS~ov8NRjcV7usP^24 zYR~IN=ifYGWsCGP#YR40(c07q{$5W_wJdJ9{GpKevi)zPn zsCGP$YR3zxcD#t%&M%?b^D?SEub{T`tElb#8mfJ-qqg%KsO|hFs=aTa+WR(YJHLb4 z&hMhy|5sG|-$S+keN_8DKyBv_Q8_$9N%<<3i;V1(i=!R4&m_xkN|h5(AY>Y*Y?$ zP&vd!C}+C4q0-7}!tJtL~!Goji&GpgOQpxQkvs@=1p+C4j} z-E*MY-G^%ToTzrsg=+WQsCLhT+As2=+C3Q6?)gydo*&ij1yJo?2-WU|QSDv?)$T=6 z?Oq(UAB3RVzXYoNOQPDp6srBppxV6xs+}vM+PM;{ohzf-xeBVCtD@Su8mgUZpxU`6 zs-0`0+POBWo$H|5xh|@m>!I4YKB}D?pxU`1s+}95+PN{RotvQAxhblho1xk{6xGhn zQSICU)y^$Z?c560&aF}H+y>RoZBgwUhHB?_sCI6TYUd89cJ7F3=T4~OPiItnhojoN z3#z@lqT0I~>iE+g)$Tn|?cNjB?!8d$-W%2KeNfwdUsU_|Lv8o{QQQ3h)bVE^Dwjd1 zTn3|Z8G_1XC@Pm>s9c7lav6a-{)|NBGYXZ@XjDF9Q2C5S&o8KaR-p1(iOOdcDxcM;eAb}yS&PbN9V(yo zsC+h{^4W;WXA>%)&8U2~pz_&@%4ZuYpY5o8cA)aviOOdeDxck`eDD|Ypz=A2%I6p=pW~=}PN4ERiOS~`DxcG+e9oZqIg85Y94epl zsC+J<^0|o0=MpNP%cy*=pz^tj%I6v?pX;c6ZlLnHiOS~|Dxce^eD0v~xr@r@S5!Xt zQ2E?PmCs+OeEvq|^BtAX zKd5~4P%Y(Sqw;Z}@^PZ_aiQ{wg32cdl}}VuKG9J5L`UTl1C>uqR6em#`NT%$69<(~ zTvR^sQ2E41<&yxFPeN2aiBS0@M&*+Pl}}PsKFLt|BuC|w0+r7XsC-hQ^7#>!j~kVb z2bE7MR6eOu`FK(Jq(SAA7L`vrR6glZ`D8%llM$6qCR9F|QTb#+<&zbaPc~FO*-`oA zK;`2@<&zVYPcBqGxl#G#LFJPdl}|7#pM0o%@}u%8fXb&JDxX5AdWgPgztxcQv;PxO;kR$Q2EqGZhPhC_#^-%fLN9EH1l}|%dK8;ZMG)Cpq1eH%y zR6fm6`GlhKX^zUL1uCDGsC-(X@@b9ArwuBfwy1o4nOtH!7b#sC@dO^67`lr#~v60jPWiqVgGp z%4aYtpCPDxhNAKrhRSC+DxVRkd`6=38HLJcG%BAlsC>qv@)?K9XFMvO38;J~qVo9( zmCqzpK9f=TOhM%{6_w94R6f&D`OHA&GZU51EL1+TQTfb4~Xm1hT3o*hwnc0%RZ8I@-^D$g#cJiDUu?1sv-J1Wl}s62b3 z^6Z7mvo|WwKBzqVqVnvA%CkQz&jF}B2cq&EgvxU;D$gOPJcpw49EQqsI4aK(s60oa z@*IWAb2KW?F{nJpqVgPv%5yv_&k3kJC!+HF36&!{|?q4Hdg z%JUago-0s!u0-Xz3YF(-RGw>4d9Fp}xek@*dQ_epP;iW0la~#?_ZU^*6I6XqQT07T)%P4#-wRZI zFH!ZqLe=*gRo@#_eQ#0qy+hUa9#!85RDB;&^?gFs_Zd~+7gT+}q3Zh`Ro_=secw>^ z{ei0QPgH$>q3Zh^Ro{11egB~9OJ!O9-?o)5r)5dom;qUaRf>Vs8PU)3w5*8Xe16bp z2c388oLiq8tnGB1Z~NWjuyn4i&m;V5+`kS)&gc0BxPPbD{F+bmYd*>64S&5-Wz`{7 z51$qMa*pk*O<=a>7D{c=5? z-^y_;D;M2ax=nPe>1G=jYgkhD3n#+RY1 zLZ@*x>G<5J&w%>;>CaaE8m8$&=<3l0wy)guYrE36rvAWiw&953>eo0;qy9XR`UBIm zt^3Dm8ubT;v&~1$tA5R|Y1Get5z!x*UfcBl>gq6DoyGXoWTbq4wa^~ylq>JQYf{=hQSA6Sn11Ito>V0r2fEK~h~ zJW_v`Nc~+S^>>TZ-#t=)k4XJJBlY)+)ZaT&f1gPGdVkV7{CEE>$S`$WtNcH^R1D|( zMN4x1qBWXsEFIT;>N-t-4m|%e;`KQq^*bZ=^DNMaX?PxJL_g02jp*mOpb`B%8#JPy z=YvM{^Ni4lex4H=(a*C&Bl>w>Xhc8H42|g5dWJ?SYh5FkwZ4(dTIa}Rt#{mRwS*ATg^*Acm_*Als`*Auy{*A%&|*A=;}*A}^~`a97Dwlyt3j84N_(XFQY?>N;F zc^ zYD7QJO^xX1*{Kozk0QO6{*mfw&4uuRdU%#;M8942|N4(ds=rpGcIX&sSv+4gVqShb zAfo@1>958<$zrY2bUHTXG^$^nj=wUGQO8}Cb??tNB(QG;_KCp05ZDI-+kRl14{Ym!>9)uJciDmMTidYe z>`te3(`%G^jZ&{s>NRTp^%`YPqh5dDb;&@d*QkEgCsm(ReNy#l8r7$|v>mH1spUyc zCpDc^T~c*P)g@JzR6VLM&>yH*2I?+HKa=Wj5&AnpM1LLnE78@W)8~{WRCJM!YySPm zYn8cfUDwF#eggk^u2Z*enSPDew3_B;Iizv(W3T%Z{OhW5TvP8Kr|H+2verTUl56^{ zb*3z}zM7VM6?FeY#Jn2!itYeiV7%(qbW*QR%hoythH+nje_FlIY2LtauJ4an$H?sby)H)Owty<30+1zuIOqou)r!`Zdj6I_|CT&mX8yW!0}NG>z7q z`!4*iSHra~+;L?2Q)XIjV7;|`?yvB7fjrJr)@wLI7ucpE*Bcn7 z@fYZ{UfdhuulEXS+tM-v?Wtjb*Q0*D_RDlykHGdB*dDZg8n%>9^{S3*bXqs9Kli{y z%%^$O2HgMQub=x({PU<@ZRgyF;`cOi&xn6IjnjH-yyn$YkYwZmG_gE-(+><({N*zD22bQJ&!1B}|Sf=^|%T?VzIxS268W-pfELR4W ztNy@p^;x7Eoj&L2bBqk3t4*iRJ1Tdj)8`8v_gRGhdlOBo&msXgMe5&7e|b99(TyAP z;!#mHJ`9cL4TIn1(lgwxIFxsD-lg-WxJ@l91?M3;XHxxIekUW}_xj6PmWJzdk*r8p zf==JH$g;-3cREz)h$(bK)ugt8B)uTdjHdB8kGb6T3(=E%MA4M(jxj* zZ^G37)+g0QQuRn)x_?<(4=q#E%Rr}fR==hX43lc(z&P~>h6Sbz4AXFB5SUi|fnkAZ z1H%$~|NTx(;{*MwC$LRu`apl>Nd0>4f#F^052e%VjNBin?|%)`GL%O*I<D(u z8;{28GY*YImv^kstZ-cAEQyANi3V`H>&_oip)%-o)3=mownpsCVrg$zMA!505eW zt-<=x`mj8$nIEl>w$HS2XdD`cyH~8;Omj}qIYH+HjYDV3KJy_T@*yAcU8eCNAMzm| z^0nW*e5gN-Vq^88_*0%w#(e5?>c4tEITx-0t=lV<`H&C!P#v^?^uA}} z_K1FV^rJecAL~~~Q>U8PIg-DfW;_~?#v|{|5u^CpdEmbs{eAAp-}efN=UqyxK8mmH zPwS6-=XHHIxw~oi)99X0bk8T>N$wjo-gl80#lDNgD7Hp1img$MV*4a+zwDp5eYAh# z_R-lCqw&5o#aMk~)mIaZMc0n*b?ZTO(0Kg6U!Uv$xBc1Alrz$A5P9R!zGC}&tzP4L zHar~vi)ryP+PKcSnqv90&SSnb=BJG@mgcK=wepB*XC>|YbX~@Et@_h>sw02ajg{B( z(9W59+F9rK*|es!R@<88`P=)m7U!XzYc;z^+C%wtpB3LVtxkDs=y#}mzeWE^o44Pm zJZW{(Jbojzo?Z7{U~Or2de-k+&GKr}_DwtM=91sFdghW(M|IMeHZQH;*mNHC8{1rY z#O|}=wAlAf_0q13uHH4BM-6M^q2*g^eW>-@ME_-)`nxt(-g?W^<*Ua-%eO{;+O_O? z+C5U8?hF3TKiwIo`yA8FD^?%1)=%fbbZ1F_drOyB%Utb$=bXp)YjxWHeC^jWrds5A= zdEOQIp-%h6W6tN()R0GWswb~sPOaAAOzhgfrOs=SJJ*vt*R*fy>q}S58m#fJY32BQ yNBO;j;+F?s9lRU%J4wIaNc~n4`<>M9CGq_cqxkg^qqTi`;#Wr;9~^OPt@t-1cP%gg literal 0 HcmV?d00001 diff --git a/Collections.subproj/CFArray.c b/Collections.subproj/CFArray.c new file mode 100644 index 0000000..a2ef408 --- /dev/null +++ b/Collections.subproj/CFArray.c @@ -0,0 +1,1061 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFArray.c + Copyright 1998-2002, Apple, Inc. All rights reserved. + Responsibility: Christopher Kane +*/ + +#include +#include "CFStorage.h" +#include "CFUtilities.h" +#include "CFInternal.h" +#include + +const CFArrayCallBacks kCFTypeArrayCallBacks = {0, __CFTypeCollectionRetain, __CFTypeCollectionRelease, CFCopyDescription, CFEqual}; +static const CFArrayCallBacks __kCFNullArrayCallBacks = {0, NULL, NULL, NULL, NULL}; + +struct __CFArrayBucket { + const void *_item; +}; + +struct __CFArrayImmutable { + CFRuntimeBase _base; + CFIndex _count; /* number of objects */ +}; + +struct __CFArrayFixedMutable { + CFRuntimeBase _base; + CFIndex _count; /* number of objects */ + CFIndex _capacity; /* maximum number of objects */ +}; + +enum { + __CF_MAX_BUCKETS_PER_DEQUE = 65534 +}; + +CF_INLINE CFIndex __CFArrayDequeRoundUpCapacity(CFIndex capacity) { + if (capacity < 4) return 4; + return __CFMin((1 << (CFLog2(capacity) + 1)), __CF_MAX_BUCKETS_PER_DEQUE); +} + +struct __CFArrayDeque { + uint16_t _leftIdx; + uint16_t _capacity; + /* struct __CFArrayBucket buckets follow here */ +}; + +struct __CFArrayMutable { + CFRuntimeBase _base; + CFIndex _count; /* number of objects */ + void *_store; /* can be NULL when MutableDeque */ +}; + +/* Flag bits */ +enum { /* Bits 0-1 */ + __kCFArrayImmutable = 0, + __kCFArrayFixedMutable = 1, + __kCFArrayMutableDeque = 2, + __kCFArrayMutableStore = 3 +}; + +enum { /* Bits 2-3 */ + __kCFArrayHasNullCallBacks = 0, + __kCFArrayHasCFTypeCallBacks = 1, + __kCFArrayHasCustomCallBacks = 3 /* callbacks are at end of header */ +}; + +CF_INLINE CFIndex __CFArrayGetType(CFArrayRef array) { + return __CFBitfieldGetValue(((const CFRuntimeBase *)array)->_info, 1, 0); +} + +CF_INLINE CFIndex __CFArrayGetSizeOfType(CFIndex t) { + CFIndex size = 0; + switch (__CFBitfieldGetValue(t, 1, 0)) { + case __kCFArrayImmutable: + size += sizeof(struct __CFArrayImmutable); + break; + case __kCFArrayFixedMutable: + size += sizeof(struct __CFArrayFixedMutable); + break; + case __kCFArrayMutableDeque: + case __kCFArrayMutableStore: + size += sizeof(struct __CFArrayMutable); + break; + } + if (__CFBitfieldGetValue(t, 3, 2) == __kCFArrayHasCustomCallBacks) { + size += sizeof(CFArrayCallBacks); + } + return size; +} + +CF_INLINE CFIndex __CFArrayGetCount(CFArrayRef array) { + return ((struct __CFArrayImmutable *)array)->_count; +} + +CF_INLINE void __CFArraySetCount(CFArrayRef array, CFIndex v) { + ((struct __CFArrayImmutable *)array)->_count = v; +} + +/* Only applies to immutable, fixed-mutable, and mutable-deque-using arrays; + * Returns the bucket holding the left-most real value in the later case. */ +CF_INLINE struct __CFArrayBucket *__CFArrayGetBucketsPtr(CFArrayRef array) { + switch (__CFArrayGetType(array)) { + case __kCFArrayImmutable: + case __kCFArrayFixedMutable: + return (struct __CFArrayBucket *)((uint8_t *)array + __CFArrayGetSizeOfType(((CFRuntimeBase *)array)->_info)); + case __kCFArrayMutableDeque: { + struct __CFArrayDeque *deque = ((struct __CFArrayMutable *)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 __kCFArrayFixedMutable: + case __kCFArrayMutableDeque: + return __CFArrayGetBucketsPtr(array) + idx; + case __kCFArrayMutableStore: { + CFStorageRef store = ((struct __CFArrayMutable *)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)->_info, 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 __CFArrayImmutable)); + break; + case __kCFArrayFixedMutable: + result = (CFArrayCallBacks *)((uint8_t *)array + sizeof(struct __CFArrayFixedMutable)); + break; + case __kCFArrayMutableDeque: + case __kCFArrayMutableStore: + result = (CFArrayCallBacks *)((uint8_t *)array + sizeof(struct __CFArrayMutable)); + 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); +} + +static void __CFArrayReleaseValues(CFArrayRef array, CFRange range, bool releaseStorageIfPossible) { + const CFArrayCallBacks *cb = __CFArrayGetCallBacks(array); + CFAllocatorRef allocator; + CFIndex idx; + switch (__CFArrayGetType(array)) { + case __kCFArrayImmutable: + case __kCFArrayFixedMutable: + if (NULL != cb->release && 0 < range.length) { + 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); + } + } + break; + case __kCFArrayMutableDeque: { + struct __CFArrayDeque *deque = ((struct __CFArrayMutable *)array)->_store; + if (NULL != cb->release && 0 < range.length && NULL != deque) { + 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); + } + } + if (releaseStorageIfPossible && 0 == range.location && __CFArrayGetCount(array) == range.length) { + allocator = __CFGetAllocator(array); + if (NULL != deque) CFAllocatorDeallocate(allocator, deque); + ((struct __CFArrayMutable *)array)->_store = NULL; + } + break; + } + case __kCFArrayMutableStore: { + CFStorageRef store = ((struct __CFArrayMutable *)array)->_store; + if (NULL != cb->release && 0 < range.length) { + 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) { + CFRelease(store); + ((struct __CFArrayMutable *)array)->_store = NULL; + __CFBitfieldSetValue(((CFRuntimeBase *)array)->_info, 1, 0, __kCFArrayMutableDeque); + } + break; + } + } +} + +CF_INLINE void __CFArrayValidateRange(CFArrayRef array, CFRange range, const char *func) { +#if defined(DEBUG) + 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)); +#endif +} + +static bool __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 && cb1->equal && !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("{type = immutable, count = %u, values = (\n"), cf, allocator, cnt); + break; + case __kCFArrayFixedMutable: + CFStringAppendFormat(result, NULL, CFSTR("{type = fixed-mutable, count = %u, capacity = %u, values = (\n"), cf, allocator, cnt, ((struct __CFArrayFixedMutable *)array)->_capacity); + break; + case __kCFArrayMutableDeque: + CFStringAppendFormat(result, NULL, CFSTR("{type = mutable-small, count = %u, values = (\n"), cf, allocator, cnt); + break; + case __kCFArrayMutableStore: + CFStringAppendFormat(result, NULL, CFSTR("{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 __CFArrayDeallocate(CFTypeRef cf) { + CFArrayRef array = (CFArrayRef)cf; + __CFArrayReleaseValues(array, CFRangeMake(0, __CFArrayGetCount(array)), true); +} + +static CFTypeID __kCFArrayTypeID = _kCFRuntimeNotATypeID; + +static const CFRuntimeClass __CFArrayClass = { + 0, + "CFArray", + NULL, // init + NULL, // copy + __CFArrayDeallocate, + (void *)__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 __CFArrayImmutable *memory; + UInt32 size; + __CFBitfieldSetValue(flags, 31, 2, 0); + 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: + case __kCFArrayFixedMutable: + size += capacity * sizeof(struct __CFArrayBucket); + break; + case __kCFArrayMutableDeque: + case __kCFArrayMutableStore: + break; + } + memory = (struct __CFArrayImmutable *)_CFRuntimeCreateInstance(allocator, __kCFArrayTypeID, size, NULL); + if (NULL == memory) { + return NULL; + } + __CFBitfieldSetValue(memory->_base._info, 6, 0, flags); + __CFArraySetCount((CFArrayRef)memory, 0); + switch (__CFBitfieldGetValue(flags, 1, 0)) { + case __kCFArrayImmutable: + if (__CFOASafe) __CFSetLastAllocationEventName(memory, "CFArray (immutable)"); + break; + case __kCFArrayFixedMutable: + if (__CFOASafe) __CFSetLastAllocationEventName(memory, "CFArray (mutable-fixed)"); + ((struct __CFArrayFixedMutable *)memory)->_capacity = capacity; + break; + case __kCFArrayMutableDeque: + case __kCFArrayMutableStore: + if (__CFOASafe) __CFSetLastAllocationEventName(memory, "CFArray (mutable-variable)"); + ((struct __CFArrayMutable *)memory)->_store = NULL; + break; + } + if (__kCFArrayHasCustomCallBacks == __CFBitfieldGetValue(flags, 3, 2)) { + const CFArrayCallBacks *cb = __CFArrayGetCallBacks((CFArrayRef)memory); + *(CFArrayCallBacks *)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; + 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); + for (idx = 0; idx < numValues; idx++) { + if (NULL != cb->retain) { + buckets->_item = (void *)INVOKE_CALLBACK2(cb->retain, allocator, *values); + } else { + 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); + return (CFMutableArrayRef)__CFArrayInit(allocator, (0 == capacity) ? __kCFArrayMutableDeque : __kCFArrayFixedMutable, capacity, callBacks); +} + +CFArrayRef _CFArrayCreate_ex(CFAllocatorRef allocator, bool mutable, const void **values, CFIndex numValues) { + CFArrayRef result; + result = __CFArrayInit(allocator, mutable ? __kCFArrayMutableDeque : __kCFArrayImmutable, numValues, &kCFTypeArrayCallBacks); + if (!mutable) { + struct __CFArrayBucket *buckets = __CFArrayGetBucketsPtr(result); + memmove(buckets, values, numValues * sizeof(struct __CFArrayBucket)); + } else { + if (__CF_MAX_BUCKETS_PER_DEQUE <= numValues) { + CFStorageRef store = CFStorageCreate(allocator, sizeof(const void *)); + if (__CFOASafe) __CFSetLastAllocationEventName(store, "CFArray (store-storage)"); + ((struct __CFArrayMutable *)result)->_store = store; + CFStorageInsertValues(store, CFRangeMake(0, numValues)); + CFStorageReplaceValues(store, CFRangeMake(0, numValues), values); + __CFBitfieldSetValue(((CFRuntimeBase *)result)->_info, 1, 0, __kCFArrayMutableStore); + } 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 = CFAllocatorAllocate(allocator, size, 0); + if (__CFOASafe) __CFSetLastAllocationEventName(deque, "CFArray (store-deque)"); + deque->_leftIdx = (capacity - numValues) / 2; + deque->_capacity = capacity; + ((struct __CFArrayMutable *)result)->_store = deque; + raw_buckets = (struct __CFArrayBucket *)((uint8_t *)deque + sizeof(struct __CFArrayDeque)); + memmove(raw_buckets + deque->_leftIdx + 0, values, numValues * sizeof(struct __CFArrayBucket)); + __CFBitfieldSetValue(((CFRuntimeBase *)result)->_info, 1, 0, __kCFArrayMutableDeque); + } + } + __CFArraySetCount(result, numValues); + return result; +} + +CFArrayRef CFArrayCreateCopy(CFAllocatorRef allocator, CFArrayRef array) { + CFArrayRef result; + const CFArrayCallBacks *cb; + struct __CFArrayBucket *buckets; + CFIndex numValues = CFArrayGetCount(array); + CFIndex idx; + cb = CF_IS_OBJC(__kCFArrayTypeID, array) ? &kCFTypeArrayCallBacks : __CFArrayGetCallBacks(array); + result = __CFArrayInit(allocator, __kCFArrayImmutable, numValues, cb); + buckets = __CFArrayGetBucketsPtr(result); + for (idx = 0; idx < numValues; idx++) { + const void *value = CFArrayGetValueAtIndex(array, idx); + if (NULL != cb->retain) { + value = (void *)INVOKE_CALLBACK2(cb->retain, allocator, value); + } + 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; + CFAssert3(0 == capacity || numValues <= capacity, __kCFLogAssertion, "%s(): for fixed-mutable arrays, capacity (%d) must be greater than or equal to initial number of values (%d)", __PRETTY_FUNCTION__, capacity, numValues); + cb = CF_IS_OBJC(__kCFArrayTypeID, array) ? &kCFTypeArrayCallBacks : __CFArrayGetCallBacks(array); + flags = (0 == capacity) ? __kCFArrayMutableDeque : __kCFArrayFixedMutable; + 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); +#if defined(DEBUG) + __CFGenericValidateType(array, __kCFArrayTypeID); + __CFArrayValidateRange(array, range, __PRETTY_FUNCTION__); +#endif + 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) { + const CFArrayCallBacks *cb; + CFIndex idx; + CF_OBJC_FUNCDISPATCH2(__kCFArrayTypeID, char, array, "containsObject:inRange:", value, range); +#if defined(DEBUG) + __CFGenericValidateType(array, __kCFArrayTypeID); + __CFArrayValidateRange(array, range, __PRETTY_FUNCTION__); +#endif + 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 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:inRange:", values, range); +#if defined(DEBUG) + __CFGenericValidateType(array, __kCFArrayTypeID); + __CFArrayValidateRange(array, range, __PRETTY_FUNCTION__); + CFAssert1(NULL != values, __kCFLogAssertion, "%s(): pointer to values may not be NULL", __PRETTY_FUNCTION__); +#endif + if (0 < range.length) { + switch (__CFArrayGetType(array)) { + case __kCFArrayImmutable: + case __kCFArrayFixedMutable: + case __kCFArrayMutableDeque: + memmove(values, __CFArrayGetBucketsPtr(array) + range.location, range.length * sizeof(struct __CFArrayBucket)); + break; + case __kCFArrayMutableStore: { + CFStorageRef store = ((struct __CFArrayMutable *)array)->_store; + CFStorageGetValues(store, range, values); + break; + } + } + } +} + +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); +#if defined(DEBUG) + __CFGenericValidateType(array, __kCFArrayTypeID); + __CFArrayValidateRange(array, range, __PRETTY_FUNCTION__); + CFAssert1(NULL != applier, __kCFLogAssertion, "%s(): pointer to applier function may not be NULL", __PRETTY_FUNCTION__); +#endif + 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); +#if defined(DEBUG) + __CFGenericValidateType(array, __kCFArrayTypeID); + __CFArrayValidateRange(array, range, __PRETTY_FUNCTION__); +#endif + 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); +#if defined(DEBUG) + __CFGenericValidateType(array, __kCFArrayTypeID); + __CFArrayValidateRange(array, range, __PRETTY_FUNCTION__); +#endif + 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); + if (NULL != cb->retain) { + value = (void *)INVOKE_CALLBACK2(cb->retain, allocator, value); + } + old_value = __CFArrayGetBucketAtIndex(array, idx)->_item; + __CFArrayGetBucketAtIndex(array, idx)->_item = value; + if (NULL != cb->release) { + INVOKE_CALLBACK2(cb->release, allocator, old_value); + } + } +} + +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; + 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__); + tmp = __CFArrayGetBucketAtIndex(array, idx1)->_item; + __CFArrayGetBucketAtIndex(array, idx1)->_item = __CFArrayGetBucketAtIndex(array, idx2)->_item; + __CFArrayGetBucketAtIndex(array, idx2)->_item = tmp; +} + +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); +} + +static void __CFArrayConvertDequeToStore(CFMutableArrayRef array) { + struct __CFArrayDeque *deque = ((struct __CFArrayMutable *)array)->_store; + struct __CFArrayBucket *raw_buckets = (struct __CFArrayBucket *)((uint8_t *)deque + sizeof(struct __CFArrayDeque)); + CFStorageRef store; + CFIndex count = CFArrayGetCount(array); + store = CFStorageCreate(__CFGetAllocator(array), sizeof(const void *)); + if (__CFOASafe) __CFSetLastAllocationEventName(store, "CFArray (store-storage)"); + ((struct __CFArrayMutable *)array)->_store = store; + CFStorageInsertValues(store, CFRangeMake(0, count)); + CFStorageReplaceValues(store, CFRangeMake(0, count), raw_buckets + deque->_leftIdx); + CFAllocatorDeallocate(__CFGetAllocator(array), deque); + __CFBitfieldSetValue(((CFRuntimeBase *)array)->_info, 1, 0, __kCFArrayMutableStore); +} + +static void __CFArrayConvertStoreToDeque(CFMutableArrayRef array) { + CFStorageRef store = ((struct __CFArrayMutable *)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); + deque = CFAllocatorAllocate(__CFGetAllocator(array), size, 0); + if (__CFOASafe) __CFSetLastAllocationEventName(deque, "CFArray (store-deque)"); + deque->_leftIdx = (capacity - count) / 2; + deque->_capacity = capacity; + ((struct __CFArrayMutable *)array)->_store = deque; + raw_buckets = (struct __CFArrayBucket *)((uint8_t *)deque + sizeof(struct __CFArrayDeque)); + CFStorageGetValues(store, CFRangeMake(0, count), raw_buckets + deque->_leftIdx); + CFRelease(store); + __CFBitfieldSetValue(((CFRuntimeBase *)array)->_info, 1, 0, __kCFArrayMutableDeque); +} + +// 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 __CFArrayMutable *)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; + + if (deque->_capacity < futureCnt) { + // must be inserting, reallocate and re-center everything + CFIndex capacity = __CFArrayDequeRoundUpCapacity(futureCnt); + CFIndex size = sizeof(struct __CFArrayDeque) + capacity * sizeof(struct __CFArrayBucket); + CFAllocatorRef allocator = __CFGetAllocator(array); + struct __CFArrayDeque *newDeque = CFAllocatorAllocate(allocator, size, 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; + if (0 < A) memmove(newBuckets + newL, buckets + oldL, A * sizeof(struct __CFArrayBucket)); + if (0 < C) memmove(newBuckets + newC0, buckets + oldC0, C * sizeof(struct __CFArrayBucket)); + CFAllocatorDeallocate(allocator, deque); + ((struct __CFArrayMutable *)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) memmove(buckets + newC0, buckets + oldC0, C * 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) memmove(buckets + newL, buckets + oldL, A * 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_leftIdx = newL; + if (newL < oldL) { + if (0 < A) memmove(buckets + newL, buckets + oldL, A * sizeof(struct __CFArrayBucket)); + if (0 < C) memmove(buckets + newC0, buckets + oldC0, C * sizeof(struct __CFArrayBucket)); + } else { + if (0 < C) memmove(buckets + newC0, buckets + oldC0, C * sizeof(struct __CFArrayBucket)); + if (0 < A) memmove(buckets + newL, buckets + oldL, A * sizeof(struct __CFArrayBucket)); + } + } +} + +// 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; +#if defined(DEBUG) + __CFGenericValidateType(array, __kCFArrayTypeID); + CFAssert1(__CFArrayGetType(array) != __kCFArrayImmutable && __CFArrayGetType(array) != __kCFArrayFixedMutable, __kCFLogAssertion, "%s(): array is immutable or fixed-mutable", __PRETTY_FUNCTION__); + CFAssert3(__CFArrayGetCount(array) <= cap, __kCFLogAssertion, "%s(): desired capacity (%d) is less than count (%d)", __PRETTY_FUNCTION__, cap, __CFArrayGetCount(array)); +#endif + // 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) == __kCFArrayMutableDeque) { + struct __CFArrayDeque *deque = ((struct __CFArrayMutable *)array)->_store; + CFIndex capacity = __CFArrayDequeRoundUpCapacity(cap); + CFIndex size = sizeof(struct __CFArrayDeque) + capacity * sizeof(struct __CFArrayBucket); + if (NULL == deque) { + deque = CFAllocatorAllocate(__CFGetAllocator(array), size, 0); + if (__CFOASafe) __CFSetLastAllocationEventName(deque, "CFArray (store-deque)"); + deque->_leftIdx = capacity / 2; + } else { + deque = CFAllocatorReallocate(__CFGetAllocator(array), deque, size, 0); + if (__CFOASafe) __CFSetLastAllocationEventName(deque, "CFArray (store-deque)"); + } + deque->_capacity = capacity; + ((struct __CFArrayMutable *)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); +#if defined(DEBUG) + __CFGenericValidateType(array, __kCFArrayTypeID); + __CFArrayValidateRange(array, range, __PRETTY_FUNCTION__); +#endif + 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((__kCFArrayFixedMutable != __CFArrayGetType(array)) || (futureCnt <= ((struct __CFArrayFixedMutable *)array)->_capacity), __kCFLogAssertion, "%s(): fixed-capacity array is full (or will overflow)", __PRETTY_FUNCTION__); + 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) { + newv = (newCount <= 256) ? buffer : CFAllocatorAllocate(allocator, newCount * sizeof(void *), 0); + 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; + } + /* 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 (__kCFArrayFixedMutable == __CFArrayGetType(array)) { + struct __CFArrayBucket *buckets = __CFArrayGetBucketsPtr(array); +// CF: we should treat a fixed mutable array like a deque too + if (0 < range.length) { + __CFArrayReleaseValues(array, range, false); + } + if (newCount != range.length && range.location + range.length < cnt) { + /* This neatly moves region C in the proper direction */ + memmove(buckets + range.location + newCount, buckets + range.location + range.length, (cnt - range.location - range.length) * sizeof(struct __CFArrayBucket)); + } + if (0 < newCount) { + memmove(buckets + range.location, newv, newCount * sizeof(void *)); + } + __CFArraySetCount(array, futureCnt); + if (newv != buffer && newv != newValues) CFAllocatorDeallocate(allocator, newv); + return; + } + if (0 < range.length) { + __CFArrayReleaseValues(array, range, false); + } + // region B elements are now "dead" + if (__kCFArrayMutableStore == __CFArrayGetType(array)) { + CFStorageRef store = ((struct __CFArrayMutable *)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 == ((struct __CFArrayMutable *)array)->_store) { + if (__CF_MAX_BUCKETS_PER_DEQUE <= futureCnt) { + CFStorageRef store = CFStorageCreate(allocator, sizeof(const void *)); + if (__CFOASafe) __CFSetLastAllocationEventName(store, "CFArray (store-storage)"); + ((struct __CFArrayMutable *)array)->_store = store; + CFStorageInsertValues(store, CFRangeMake(0, newCount)); + __CFBitfieldSetValue(((CFRuntimeBase *)array)->_info, 1, 0, __kCFArrayMutableStore); + } else if (0 <= futureCnt) { + struct __CFArrayDeque *deque; + CFIndex capacity = __CFArrayDequeRoundUpCapacity(futureCnt); + CFIndex size = sizeof(struct __CFArrayDeque) + capacity * sizeof(struct __CFArrayBucket); + deque = CFAllocatorAllocate(allocator, size, 0); + if (__CFOASafe) __CFSetLastAllocationEventName(deque, "CFArray (store-deque)"); + deque->_leftIdx = (capacity - newCount) / 2; + deque->_capacity = capacity; + ((struct __CFArrayMutable *)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 = ((struct __CFArrayMutable *)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 (__kCFArrayMutableStore == __CFArrayGetType(array)) { + CFStorageRef store = ((struct __CFArrayMutable *)array)->_store; + CFStorageReplaceValues(store, CFRangeMake(range.location, newCount), newv); + } else { // Deque + struct __CFArrayDeque *deque = ((struct __CFArrayMutable *)array)->_store; + struct __CFArrayBucket *raw_buckets = (struct __CFArrayBucket *)((uint8_t *)deque + sizeof(struct __CFArrayDeque)); + if (newCount == 1) + *((const void **)raw_buckets + deque->_leftIdx + range.location) = newv[0]; + else + 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); +#if defined(DEBUG) + __CFGenericValidateType(array, __kCFArrayTypeID); + __CFArrayValidateRange(array, range, __PRETTY_FUNCTION__); +#endif + 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__); + if (1 < range.length) { + struct _acompareContext ctx; + ctx.func = comparator; + ctx.context = context; + switch (__CFArrayGetType(array)) { + case __kCFArrayFixedMutable: + case __kCFArrayMutableDeque: + CFQSortArray(__CFArrayGetBucketsPtr(array) + range.location, range.length, sizeof(void *), (CFComparatorFunction)__CFArrayCompareValues, &ctx); + break; + case __kCFArrayMutableStore: { + CFStorageRef store = ((struct __CFArrayMutable *)array)->_store; + CFAllocatorRef allocator = __CFGetAllocator(array); + const void **values, *buffer[256]; + values = (range.length <= 256) ? buffer : CFAllocatorAllocate(allocator, range.length * sizeof(void *), 0); + 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); + break; + } + } + } +} + +CFIndex CFArrayBSearchValues(CFArrayRef array, CFRange range, const void *value, CFComparatorFunction comparator, void *context) { + bool isObjC = CF_IS_OBJC(__kCFArrayTypeID, array); + CFIndex idx = 0; + FAULT_CALLBACK((void **)&(comparator)); + if (!isObjC) { +#if defined(DEBUG) + __CFGenericValidateType(array, __kCFArrayTypeID); + __CFArrayValidateRange(array, range, __PRETTY_FUNCTION__); +#endif + } + CFAssert1(NULL != comparator, __kCFLogAssertion, "%s(): pointer to comparator function may not be NULL", __PRETTY_FUNCTION__); + if (range.length <= 0) return range.location; + if (isObjC || __kCFArrayMutableStore == __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 = CFLog2(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; +#if defined(DEBUG) + __CFGenericValidateType(array, __kCFArrayTypeID); + __CFGenericValidateType(otherArray, __kCFArrayTypeID); + CFAssert1(__CFArrayGetType(array) != __kCFArrayImmutable, __kCFLogAssertion, "%s(): array is immutable", __PRETTY_FUNCTION__); + __CFArrayValidateRange(otherArray, otherRange, __PRETTY_FUNCTION__); +#endif + for (idx = otherRange.location; idx < otherRange.location + otherRange.length; idx++) { + CFArrayAppendValue(array, CFArrayGetValueAtIndex(otherArray, idx)); + } +} diff --git a/Collections.subproj/CFArray.h b/Collections.subproj/CFArray.h new file mode 100644 index 0000000..9dcf528 --- /dev/null +++ b/Collections.subproj/CFArray.h @@ -0,0 +1,712 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFArray.h + Copyright (c) 1998-2003, 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 have two subflavors, fixed-capacity, for which there + is a maximum number set at creation time of values which can be put + into the array, and variable capacity, which can have an unlimited + number of values (or rather, limited only by constraints external + to CFArray, like the amount of available memory). Fixed-capacity + arrays can be somewhat higher performing, if you can put a definite + upper limit on the number of values that might be put into the + array. + + 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 + +#if defined(__cplusplus) +extern "C" { +#endif + +/*! + @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 The maximum number of values that can be contained + by the CFArray. The array starts empty, and can grow to this + number of values (and it can have less). If this parameter + is 0, the array's maximum capacity is unlimited (or rather, + 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 arrays 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 The maximum number of values that can be contained + by the CFArray. The array starts empty, and can grow to this + number of values (and it can have less). If this parameter + is 0, the array's maximum capacity is unlimited (or rather, + 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. If the array is a fixed-capacity array and it + is full before this operation, 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. If the array is a fixed-capacity array and it + is full before this operation, 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. If the array is a fixed-capacity array and it + is full before this operation and the index is the same as + N, 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. If the array is a fixed-capacity + array and adding range.length values from the otherArray + exceeds the capacity of the array, 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); + +#if defined(__cplusplus) +} +#endif + +#endif /* ! __COREFOUNDATION_CFARRAY__ */ + diff --git a/Collections.subproj/CFBag.c b/Collections.subproj/CFBag.c new file mode 100644 index 0000000..26ed5c0 --- /dev/null +++ b/Collections.subproj/CFBag.c @@ -0,0 +1,764 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFBag.c + Copyright 1998-2002, Apple, Inc. All rights reserved. + Responsibility: Christopher Kane +*/ + +#include +#include "CFInternal.h" + +const CFBagCallBacks kCFTypeBagCallBacks = {0, __CFTypeCollectionRetain, __CFTypeCollectionRelease, CFCopyDescription, CFEqual, CFHash}; +const CFBagCallBacks kCFCopyStringBagCallBacks = {0, (void *)CFStringCreateCopy, __CFTypeCollectionRelease, CFCopyDescription, CFEqual, CFHash}; +static const CFBagCallBacks __kCFNullBagCallBacks = {0, NULL, NULL, NULL, NULL, NULL}; + + +static const uint32_t __CFBagCapacities[42] = { + 4, 8, 17, 29, 47, 76, 123, 199, 322, 521, 843, 1364, 2207, 3571, 5778, 9349, + 15127, 24476, 39603, 64079, 103682, 167761, 271443, 439204, 710647, 1149851, 1860498, + 3010349, 4870847, 7881196, 12752043, 20633239, 33385282, 54018521, 87403803, 141422324, + 228826127, 370248451, 599074578, 969323029, 1568397607, 2537720636U +}; + +static const uint32_t __CFBagBuckets[42] = { // primes + 5, 11, 23, 41, 67, 113, 199, 317, 521, 839, 1361, 2207, 3571, 5779, 9349, 15121, + 24473, 39607, 64081, 103681, 167759, 271429, 439199, 710641, 1149857, 1860503, 3010349, + 4870843, 7881193, 12752029, 20633237, 33385273, 54018521, 87403763, 141422317, 228826121, + 370248451, 599074561, 969323023, 1568397599, 2537720629U, 4106118251U +}; + +CF_INLINE CFIndex __CFBagRoundUpCapacity(CFIndex capacity) { + CFIndex idx; + for (idx = 0; idx < 42 && __CFBagCapacities[idx] < (uint32_t)capacity; idx++); + if (42 <= idx) HALT; + return __CFBagCapacities[idx]; +} + +CF_INLINE CFIndex __CFBagNumBucketsForCapacity(CFIndex capacity) { + CFIndex idx; + for (idx = 0; idx < 42 && __CFBagCapacities[idx] < (uint32_t)capacity; idx++); + if (42 <= idx) HALT; + return __CFBagBuckets[idx]; +} + +enum { /* Bits 1-0 */ + __kCFBagImmutable = 0, /* unchangable and fixed capacity */ + __kCFBagMutable = 1, /* changeable and variable capacity */ + __kCFBagFixedMutable = 3 /* changeable and fixed capacity */ +}; + +enum { /* Bits 3-2 */ + __kCFBagHasNullCallBacks = 0, + __kCFBagHasCFTypeCallBacks = 1, + __kCFBagHasCustomCallBacks = 3 /* callbacks are at end of header */ +}; + +struct __CFBagBucket { + const void *_key; + CFIndex _count; +}; + +struct __CFBag { + CFRuntimeBase _base; + CFIndex _count; /* number of values */ + CFIndex _capacity; /* maximum number of values */ + CFIndex _bucketsUsed; /* number of slots used */ + CFIndex _bucketsNum; /* number of slots */ + const void *_emptyMarker; + const void *_deletedMarker; + void *_context; /* private */ + struct __CFBagBucket *_buckets; /* can be NULL if not allocated yet */ +}; + +CF_INLINE bool __CFBagBucketIsEmpty(CFBagRef bag, const struct __CFBagBucket *bucket) { + return (bag->_emptyMarker == bucket->_key); +} + +CF_INLINE bool __CFBagBucketIsDeleted(CFBagRef bag, const struct __CFBagBucket *bucket) { + return (bag->_deletedMarker == bucket->_key); +} + +CF_INLINE bool __CFBagBucketIsOccupied(CFBagRef bag, const struct __CFBagBucket *bucket) { + return (bag->_emptyMarker != bucket->_key && bag->_deletedMarker != bucket->_key); +} + +/* Bits 1-0 of the base reserved bits are used for mutability variety */ +/* Bits 3-2 of the base reserved bits are used for callback indicator bits */ + +CF_INLINE CFIndex __CFBagGetType(CFBagRef bag) { + return __CFBitfieldGetValue(((const CFRuntimeBase *)bag)->_info, 1, 0); +} + +CF_INLINE CFIndex __CFBagGetSizeOfType(CFIndex t) { + CFIndex size = sizeof(struct __CFBag); + if (__CFBitfieldGetValue(t, 3, 2) == __kCFBagHasCustomCallBacks) { + size += sizeof(CFBagCallBacks); + } + return size; +} + +CF_INLINE const CFBagCallBacks *__CFBagGetCallBacks(CFBagRef bag) { + CFBagCallBacks *result = NULL; + switch (__CFBitfieldGetValue(((const CFRuntimeBase *)bag)->_info, 3, 2)) { + case __kCFBagHasNullCallBacks: + return &__kCFNullBagCallBacks; + case __kCFBagHasCFTypeCallBacks: + return &kCFTypeBagCallBacks; + case __kCFBagHasCustomCallBacks: + break; + } + result = (CFBagCallBacks *)((uint8_t *)bag + sizeof(struct __CFBag)); + return result; +} + +CF_INLINE bool __CFBagCallBacksMatchNull(const CFBagCallBacks *c) { + return (NULL == c || + (c->retain == __kCFNullBagCallBacks.retain && + c->release == __kCFNullBagCallBacks.release && + c->copyDescription == __kCFNullBagCallBacks.copyDescription && + c->equal == __kCFNullBagCallBacks.equal && + c->hash == __kCFNullBagCallBacks.hash)); +} + +CF_INLINE bool __CFBagCallBacksMatchCFType(const CFBagCallBacks *c) { + return (&kCFTypeBagCallBacks == c || + (c->retain == kCFTypeBagCallBacks.retain && + c->release == kCFTypeBagCallBacks.release && + c->copyDescription == kCFTypeBagCallBacks.copyDescription && + c->equal == kCFTypeBagCallBacks.equal && + c->hash == kCFTypeBagCallBacks.hash)); +} + + +static void __CFBagFindBuckets1(CFBagRef bag, const void *key, struct __CFBagBucket **match) { + const CFBagCallBacks *cb = __CFBagGetCallBacks(bag); + struct __CFBagBucket *buckets = bag->_buckets; + CFHashCode keyHash = cb->hash ? (CFHashCode)INVOKE_CALLBACK2(((CFHashCode (*)(const void *, void *))cb->hash), key, bag->_context) : (CFHashCode)key; + UInt32 start = keyHash % bag->_bucketsNum; + UInt32 probe = start; + UInt32 probeskip = 1; + *match = NULL; + for (;;) { + struct __CFBagBucket *currentBucket = buckets + probe; + if (__CFBagBucketIsEmpty(bag, currentBucket)) { + return; + } else if (__CFBagBucketIsDeleted(bag, currentBucket)) { + /* do nothing */ + } else if (currentBucket->_key == key || (cb->equal && INVOKE_CALLBACK3((Boolean (*)(void *, void *, void *))cb->equal, currentBucket->_key, key, bag->_context))) { + *match = currentBucket; + return; + } + probe = (probe + probeskip) % bag->_bucketsNum; + if (start == probe) return; + } +} + +static void __CFBagFindBuckets2(CFBagRef bag, const void *key, struct __CFBagBucket **match, struct __CFBagBucket **nomatch) { + const CFBagCallBacks *cb = __CFBagGetCallBacks(bag); + struct __CFBagBucket *buckets = bag->_buckets; + CFHashCode keyHash = cb->hash ? (CFHashCode)INVOKE_CALLBACK2(((CFHashCode (*)(const void *, void *))cb->hash), key, bag->_context) : (CFHashCode)key; + UInt32 start = keyHash % bag->_bucketsNum; + UInt32 probe = start; + UInt32 probeskip = 1; + *match = NULL; + *nomatch = NULL; + for (;;) { + struct __CFBagBucket *currentBucket = buckets + probe; + if (__CFBagBucketIsEmpty(bag, currentBucket)) { + if (!*nomatch) *nomatch = currentBucket; + return; + } else if (__CFBagBucketIsDeleted(bag, currentBucket)) { + if (!*nomatch) *nomatch = currentBucket; + } else if (!*match && (currentBucket->_key == key || (cb->equal && INVOKE_CALLBACK3((Boolean (*)(void *, void *, void *))cb->equal, currentBucket->_key, key, bag->_context)))) { + *match = currentBucket; + if (*nomatch) return; + } + probe = (probe + probeskip) % bag->_bucketsNum; + if (start == probe) return; + } +} + +static void __CFBagFindNewEmptyMarker(CFBagRef bag) { + struct __CFBagBucket *buckets; + const void *newEmpty; + bool hit; + CFIndex idx, nbuckets; + buckets = bag->_buckets; + nbuckets = bag->_bucketsNum; + newEmpty = bag->_emptyMarker; + do { + (intptr_t)newEmpty -= 2; + hit = false; + for (idx = 0; idx < nbuckets; idx++) { + if (newEmpty == buckets[idx]._key) { + hit = true; + break; + } + } + } while (hit); + for (idx = 0; idx < nbuckets; idx++) { + if (bag->_emptyMarker == buckets[idx]._key) { + buckets[idx]._key = newEmpty; + } + } + ((struct __CFBag *)bag)->_emptyMarker = newEmpty; +} + +static void __CFBagFindNewDeletedMarker(CFBagRef bag) { + struct __CFBagBucket *buckets; + const void *newDeleted; + bool hit; + CFIndex idx, nbuckets; + buckets = bag->_buckets; + nbuckets = bag->_bucketsNum; + newDeleted = bag->_deletedMarker; + do { + (intptr_t)newDeleted += 2; + hit = false; + for (idx = 0; idx < nbuckets; idx++) { + if (newDeleted == buckets[idx]._key) { + hit = true; + break; + } + } + } while (hit); + for (idx = 0; idx < nbuckets; idx++) { + if (bag->_deletedMarker == buckets[idx]._key) { + buckets[idx]._key = newDeleted; + } + } + ((struct __CFBag *)bag)->_deletedMarker = newDeleted; +} + +static bool __CFBagEqual(CFTypeRef cf1, CFTypeRef cf2) { + CFBagRef bag1 = (CFBagRef)cf1; + CFBagRef bag2 = (CFBagRef)cf2; + const CFBagCallBacks *cb1, *cb2; + const struct __CFBagBucket *buckets; + CFIndex idx, nbuckets; + if (bag1 == bag2) return true; + if (bag1->_count != bag2->_count) return false; + cb1 = __CFBagGetCallBacks(bag1); + cb2 = __CFBagGetCallBacks(bag2); + if (cb1->equal != cb2->equal) return false; + if (0 == bag1->_count) return true; /* after function comparison! */ + buckets = bag1->_buckets; + nbuckets = bag1->_bucketsNum; + for (idx = 0; idx < nbuckets; idx++) { + if (__CFBagBucketIsOccupied(bag1, &buckets[idx])) { + if (buckets[idx]._count != CFBagGetCountOfValue(bag2, buckets[idx]._key)) { + return false; + } + } + } + return true; +} + +static CFHashCode __CFBagHash(CFTypeRef cf) { + CFBagRef bag = (CFBagRef)cf; + return bag->_count; +} + +static CFStringRef __CFBagCopyDescription(CFTypeRef cf) { + CFBagRef bag = (CFBagRef)cf; + const CFBagCallBacks *cb; + const struct __CFBagBucket *buckets; + CFIndex idx, nbuckets; + CFMutableStringRef result; + cb = __CFBagGetCallBacks(bag); + buckets = bag->_buckets; + nbuckets = bag->_bucketsNum; + result = CFStringCreateMutable(kCFAllocatorSystemDefault, 0); + CFStringAppendFormat(result, NULL, CFSTR("{count = %u, capacity = %u, values = (\n"), bag, CFGetAllocator(bag), bag->_count, bag->_capacity); + for (idx = 0; idx < nbuckets; idx++) { + if (__CFBagBucketIsOccupied(bag, &buckets[idx])) { + CFStringRef desc = NULL; + if (NULL != cb->copyDescription) { + desc = (CFStringRef)INVOKE_CALLBACK2(((CFStringRef (*)(const void *, void *))cb->copyDescription), buckets[idx]._key, bag->_context); + } + if (NULL != desc) { + CFStringAppendFormat(result, NULL, CFSTR("\t%u : %@ (%d)\n"), idx, desc, buckets[idx]._count); + CFRelease(desc); + } else { + CFStringAppendFormat(result, NULL, CFSTR("\t%u : <%p> (%d)\n"), idx, buckets[idx]._key, buckets[idx]._count); + } + } + } + CFStringAppend(result, CFSTR(")}")); + return result; +} + +static void __CFBagDeallocate(CFTypeRef cf) { + CFMutableBagRef bag = (CFMutableBagRef)cf; + CFAllocatorRef allocator = CFGetAllocator(bag); + if (__CFBagGetType(bag) == __kCFBagImmutable) { + __CFBitfieldSetValue(((CFRuntimeBase *)bag)->_info, 1, 0, __kCFBagFixedMutable); + } + CFBagRemoveAllValues(bag); + if (__CFBagGetType(bag) == __kCFBagMutable && bag->_buckets) { + CFAllocatorDeallocate(allocator, bag->_buckets); + } +} + +static CFTypeID __kCFBagTypeID = _kCFRuntimeNotATypeID; + +static const CFRuntimeClass __CFBagClass = { + 0, + "CFBag", + NULL, // init + NULL, // copy + __CFBagDeallocate, + (void *)__CFBagEqual, + __CFBagHash, + NULL, // + __CFBagCopyDescription +}; + +__private_extern__ void __CFBagInitialize(void) { + __kCFBagTypeID = _CFRuntimeRegisterClass(&__CFBagClass); +} + +CFTypeID CFBagGetTypeID(void) { + return __kCFBagTypeID; +} + +static CFBagRef __CFBagInit(CFAllocatorRef allocator, UInt32 flags, CFIndex capacity, const CFBagCallBacks *callBacks) { + struct __CFBag *memory; + UInt32 size; + CFIndex idx; + __CFBitfieldSetValue(flags, 31, 2, 0); + if (__CFBagCallBacksMatchNull(callBacks)) { + __CFBitfieldSetValue(flags, 3, 2, __kCFBagHasNullCallBacks); + } else if (__CFBagCallBacksMatchCFType(callBacks)) { + __CFBitfieldSetValue(flags, 3, 2, __kCFBagHasCFTypeCallBacks); + } else { + __CFBitfieldSetValue(flags, 3, 2, __kCFBagHasCustomCallBacks); + } + size = __CFBagGetSizeOfType(flags) - sizeof(CFRuntimeBase); + switch (__CFBitfieldGetValue(flags, 1, 0)) { + case __kCFBagImmutable: + case __kCFBagFixedMutable: + size += __CFBagNumBucketsForCapacity(capacity) * sizeof(struct __CFBagBucket); + break; + case __kCFBagMutable: + break; + } + memory = (struct __CFBag *)_CFRuntimeCreateInstance(allocator, __kCFBagTypeID, size, NULL); + if (NULL == memory) { + return NULL; + } + __CFBitfieldSetValue(memory->_base._info, 6, 0, flags); + memory->_count = 0; + memory->_bucketsUsed = 0; + memory->_emptyMarker = (const void *)0xa1b1c1d3; + memory->_deletedMarker = (const void *)0xa1b1c1d5; + memory->_context = NULL; + switch (__CFBitfieldGetValue(flags, 1, 0)) { + case __kCFBagImmutable: + if (__CFOASafe) __CFSetLastAllocationEventName(memory, "CFBag (immutable)"); + memory->_capacity = capacity; /* Don't round up capacity */ + memory->_bucketsNum = __CFBagNumBucketsForCapacity(memory->_capacity); + memory->_buckets = (struct __CFBagBucket *)((uint8_t *)memory + __CFBagGetSizeOfType(flags)); + for (idx = memory->_bucketsNum; idx--;) { + memory->_buckets[idx]._key = memory->_emptyMarker; + } + break; + case __kCFBagFixedMutable: + if (__CFOASafe) __CFSetLastAllocationEventName(memory, "CFBag (mutable-fixed)"); + memory->_capacity = capacity; /* Don't round up capacity */ + memory->_bucketsNum = __CFBagNumBucketsForCapacity(memory->_capacity); + memory->_buckets = (struct __CFBagBucket *)((uint8_t *)memory + __CFBagGetSizeOfType(flags)); + for (idx = memory->_bucketsNum; idx--;) { + memory->_buckets[idx]._key = memory->_emptyMarker; + } + break; + case __kCFBagMutable: + if (__CFOASafe) __CFSetLastAllocationEventName(memory, "CFBag (mutable-variable)"); + memory->_capacity = __CFBagRoundUpCapacity(1); + memory->_bucketsNum = 0; + memory->_buckets = NULL; + break; + } + if (__kCFBagHasCustomCallBacks == __CFBitfieldGetValue(flags, 3, 2)) { + const CFBagCallBacks *cb = __CFBagGetCallBacks((CFBagRef)memory); + *(CFBagCallBacks *)cb = *callBacks; + 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)); + } + return (CFBagRef)memory; +} + +CFBagRef CFBagCreate(CFAllocatorRef allocator, const void **values, CFIndex numValues, const CFBagCallBacks *callBacks) { + CFBagRef result; + UInt32 flags; + CFIndex idx; + CFAssert2(0 <= numValues, __kCFLogAssertion, "%s(): numValues (%d) cannot be less than zero", __PRETTY_FUNCTION__, numValues); + result = __CFBagInit(allocator, __kCFBagImmutable, numValues, callBacks); + flags = __CFBitfieldGetValue(((const CFRuntimeBase *)result)->_info, 1, 0); + if (flags == __kCFBagImmutable) { + __CFBitfieldSetValue(((CFRuntimeBase *)result)->_info, 1, 0, __kCFBagFixedMutable); + } + for (idx = 0; idx < numValues; idx++) { + CFBagAddValue((CFMutableBagRef)result, values[idx]); + } + __CFBitfieldSetValue(((CFRuntimeBase *)result)->_info, 1, 0, flags); + return result; +} + +CFMutableBagRef CFBagCreateMutable(CFAllocatorRef allocator, CFIndex capacity, const CFBagCallBacks *callBacks) { + CFAssert2(0 <= capacity, __kCFLogAssertion, "%s(): capacity (%d) cannot be less than zero", __PRETTY_FUNCTION__, capacity); + return (CFMutableBagRef)__CFBagInit(allocator, (0 == capacity) ? __kCFBagMutable : __kCFBagFixedMutable, capacity, callBacks); +} + +CFBagRef CFBagCreateCopy(CFAllocatorRef allocator, CFBagRef bag) { + CFBagRef result; + const CFBagCallBacks *cb; + CFIndex numValues = CFBagGetCount(bag); + const void **list, *buffer[256]; + list = (numValues <= 256) ? buffer : CFAllocatorAllocate(allocator, numValues * sizeof(void *), 0); + if (list != buffer && __CFOASafe) __CFSetLastAllocationEventName(list, "CFBag (temp)"); + CFBagGetValues(bag, list); + cb = CF_IS_OBJC(__kCFBagTypeID, bag) ? &kCFTypeBagCallBacks : __CFBagGetCallBacks(bag); + result = CFBagCreate(allocator, list, numValues, cb); + if (list != buffer) CFAllocatorDeallocate(allocator, list); + return result; +} + +CFMutableBagRef CFBagCreateMutableCopy(CFAllocatorRef allocator, CFIndex capacity, CFBagRef bag) { + CFMutableBagRef result; + const CFBagCallBacks *cb; + CFIndex idx, numValues = CFBagGetCount(bag); + const void **list, *buffer[256]; + CFAssert3(0 == capacity || numValues <= capacity, __kCFLogAssertion, "%s(): for fixed-mutable bags, capacity (%d) must be greater than or equal to initial number of values (%d)", __PRETTY_FUNCTION__, capacity, numValues); + list = (numValues <= 256) ? buffer : CFAllocatorAllocate(allocator, numValues * sizeof(void *), 0); + if (list != buffer && __CFOASafe) __CFSetLastAllocationEventName(list, "CFBag (temp)"); + CFBagGetValues(bag, list); + cb = CF_IS_OBJC(__kCFBagTypeID, bag) ? &kCFTypeBagCallBacks : __CFBagGetCallBacks(bag); + result = CFBagCreateMutable(allocator, capacity, cb); + if (0 == capacity) _CFBagSetCapacity(result, numValues); + for (idx = 0; idx < numValues; idx++) { + CFBagAddValue(result, list[idx]); + } + if (list != buffer) CFAllocatorDeallocate(allocator, list); + return result; +} + +void _CFBagSetContext(CFBagRef bag, void *context) { + ((struct __CFBag *)bag)->_context = context; +} + +CFIndex CFBagGetCount(CFBagRef bag) { + __CFGenericValidateType(bag, __kCFBagTypeID); + return bag->_count; +} + +CFIndex CFBagGetCountOfValue(CFBagRef bag, const void *value) { + struct __CFBagBucket *match; + __CFGenericValidateType(bag, __kCFBagTypeID); + if (0 == bag->_count) return 0; + __CFBagFindBuckets1(bag, value, &match); + return (match ? match->_count : 0); +} + +Boolean CFBagContainsValue(CFBagRef bag, const void *value) { + struct __CFBagBucket *match; + __CFGenericValidateType(bag, __kCFBagTypeID); + if (0 == bag->_count) return false; + __CFBagFindBuckets1(bag, value, &match); + return (match ? true : false); +} + +const void *CFBagGetValue(CFBagRef bag, const void *value) { + struct __CFBagBucket *match; + __CFGenericValidateType(bag, __kCFBagTypeID); + if (0 == bag->_count) return NULL; + __CFBagFindBuckets1(bag, value, &match); + return (match ? match->_key : NULL); +} + +Boolean CFBagGetValueIfPresent(CFBagRef bag, const void *candidate, const void **value) { + struct __CFBagBucket *match; + __CFGenericValidateType(bag, __kCFBagTypeID); + if (0 == bag->_count) return false; + __CFBagFindBuckets1(bag, candidate, &match); + return (match ? ((value ? *value = match->_key : NULL), true) : false); +} + +void CFBagGetValues(CFBagRef bag, const void **values) { + struct __CFBagBucket *buckets; + CFIndex idx, cnt, nbuckets; + __CFGenericValidateType(bag, __kCFBagTypeID); + buckets = bag->_buckets; + nbuckets = bag->_bucketsNum; + for (idx = 0; idx < nbuckets; idx++) { + if (__CFBagBucketIsOccupied(bag, &buckets[idx])) { + for (cnt = buckets[idx]._count; cnt--;) { + if (values) *values++ = buckets[idx]._key; + } + } + } +} + +void CFBagApplyFunction(CFBagRef bag, CFBagApplierFunction applier, void *context) { + struct __CFBagBucket *buckets; + CFIndex idx, cnt, nbuckets; + FAULT_CALLBACK((void **)&(applier)); + __CFGenericValidateType(bag, __kCFBagTypeID); + buckets = bag->_buckets; + nbuckets = bag->_bucketsNum; + for (idx = 0; idx < nbuckets; idx++) { + if (__CFBagBucketIsOccupied(bag, &buckets[idx])) { + for (cnt = buckets[idx]._count; cnt--;) { + INVOKE_CALLBACK2(applier, buckets[idx]._key, context); + } + } + } +} + +static void __CFBagGrow(CFMutableBagRef bag, CFIndex numNewValues) { + struct __CFBagBucket *oldbuckets = bag->_buckets; + CFIndex idx, oldnbuckets = bag->_bucketsNum; + CFIndex oldCount = bag->_count; + bag->_capacity = __CFBagRoundUpCapacity(oldCount + numNewValues); + bag->_bucketsNum = __CFBagNumBucketsForCapacity(bag->_capacity); + bag->_buckets = CFAllocatorAllocate(CFGetAllocator(bag), bag->_bucketsNum * sizeof(struct __CFBagBucket), 0); + if (__CFOASafe) __CFSetLastAllocationEventName(bag->_buckets, "CFBag (store)"); + if (NULL == bag->_buckets) HALT; + for (idx = bag->_bucketsNum; idx--;) { + bag->_buckets[idx]._key = bag->_emptyMarker; + } + if (NULL == oldbuckets) return; + for (idx = 0; idx < oldnbuckets; idx++) { + if (__CFBagBucketIsOccupied(bag, &oldbuckets[idx])) { + struct __CFBagBucket *match, *nomatch; + __CFBagFindBuckets2(bag, oldbuckets[idx]._key, &match, &nomatch); + CFAssert3(!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__, oldbuckets[idx]._key, match->_key); + nomatch->_key = oldbuckets[idx]._key; + nomatch->_count = oldbuckets[idx]._count; + } + } + CFAssert1(bag->_count == oldCount, __kCFLogAssertion, "%s(): bag count differs after rehashing; error", __PRETTY_FUNCTION__); + CFAllocatorDeallocate(CFGetAllocator(bag), oldbuckets); +} + +// This function is for Foundation's benefit; no one else should use it. +void _CFBagSetCapacity(CFMutableBagRef bag, CFIndex cap) { + if (CF_IS_OBJC(__kCFBagTypeID, bag)) return; +#if defined(DEBUG) + __CFGenericValidateType(bag, __kCFBagTypeID); + CFAssert1(__CFBagGetType(bag) != __kCFBagImmutable && __CFBagGetType(bag) != __kCFBagFixedMutable, __kCFLogAssertion, "%s(): bag is immutable or fixed-mutable", __PRETTY_FUNCTION__); + CFAssert3(bag->_count <= cap, __kCFLogAssertion, "%s(): desired capacity (%d) is less than count (%d)", __PRETTY_FUNCTION__, cap, bag->_count); +#endif + __CFBagGrow(bag, cap - bag->_count); +} + +void CFBagAddValue(CFMutableBagRef bag, const void *value) { + struct __CFBagBucket *match, *nomatch; + const CFBagCallBacks *cb; + const void *newValue; + __CFGenericValidateType(bag, __kCFBagTypeID); + switch (__CFBagGetType(bag)) { + case __kCFBagMutable: + if (bag->_bucketsUsed == bag->_capacity || NULL == bag->_buckets) { + __CFBagGrow(bag, 1); + } + break; + case __kCFBagFixedMutable: + CFAssert3(bag->_count < bag->_capacity, __kCFLogAssertion, "%s(): capacity exceeded on fixed-capacity bag %p (capacity = %d)", __PRETTY_FUNCTION__, bag, bag->_capacity); + break; + default: + CFAssert2(__CFBagGetType(bag) != __kCFBagImmutable, __kCFLogAssertion, "%s(): immutable bag %p passed to mutating operation", __PRETTY_FUNCTION__, bag); + break; + } + __CFBagFindBuckets2(bag, value, &match, &nomatch); + if (match) { + match->_count++; bag->_count++; + } else { + cb = __CFBagGetCallBacks(bag); + if (cb->retain) { + newValue = (void *)INVOKE_CALLBACK3(((const void *(*)(CFAllocatorRef, const void *, void *))cb->retain), CFGetAllocator(bag), value, bag->_context); + } else { + newValue = value; + } + if (bag->_emptyMarker == newValue) { + __CFBagFindNewEmptyMarker(bag); + } + if (bag->_deletedMarker == newValue) { + __CFBagFindNewDeletedMarker(bag); + } + nomatch->_key = newValue; + nomatch->_count = 1; + bag->_bucketsUsed++; + bag->_count++; + } +} + +void CFBagReplaceValue(CFMutableBagRef bag, const void *value) { + struct __CFBagBucket *match; + const CFBagCallBacks *cb; + const void *newValue; + __CFGenericValidateType(bag, __kCFBagTypeID); + switch (__CFBagGetType(bag)) { + case __kCFBagMutable: + case __kCFBagFixedMutable: + break; + default: + CFAssert2(__CFBagGetType(bag) != __kCFBagImmutable, __kCFLogAssertion, "%s(): immutable bag %p passed to mutating operation", __PRETTY_FUNCTION__, bag); + break; + } + if (0 == bag->_count) return; + __CFBagFindBuckets1(bag, value, &match); + if (!match) return; + cb = __CFBagGetCallBacks(bag); + if (cb->retain) { + newValue = (void *)INVOKE_CALLBACK3(((const void *(*)(CFAllocatorRef, const void *, void *))cb->retain), CFGetAllocator(bag), value, bag->_context); + } else { + newValue = value; + } + if (cb->release) { + INVOKE_CALLBACK3(((void (*)(CFAllocatorRef, const void *, void *))cb->release), CFGetAllocator(bag), match->_key, bag->_context); + match->_key = bag->_deletedMarker; + } + if (bag->_emptyMarker == newValue) { + __CFBagFindNewEmptyMarker(bag); + } + if (bag->_deletedMarker == newValue) { + __CFBagFindNewDeletedMarker(bag); + } + match->_key = newValue; +} + +void CFBagSetValue(CFMutableBagRef bag, const void *value) { + struct __CFBagBucket *match, *nomatch; + const CFBagCallBacks *cb; + const void *newValue; + __CFGenericValidateType(bag, __kCFBagTypeID); + switch (__CFBagGetType(bag)) { + case __kCFBagMutable: + if (bag->_bucketsUsed == bag->_capacity || NULL == bag->_buckets) { + __CFBagGrow(bag, 1); + } + break; + case __kCFBagFixedMutable: + break; + default: + CFAssert2(__CFBagGetType(bag) != __kCFBagImmutable, __kCFLogAssertion, "%s(): immutable bag %p passed to mutating operation", __PRETTY_FUNCTION__, bag); + break; + } + __CFBagFindBuckets2(bag, value, &match, &nomatch); + cb = __CFBagGetCallBacks(bag); + if (cb->retain) { + newValue = (void *)INVOKE_CALLBACK3(((const void *(*)(CFAllocatorRef, const void *, void *))cb->retain), CFGetAllocator(bag), value, bag->_context); + } else { + newValue = value; + } + if (match) { + if (cb->release) { + INVOKE_CALLBACK3(((void (*)(CFAllocatorRef, const void *, void *))cb->release), CFGetAllocator(bag), match->_key, bag->_context); + match->_key = bag->_deletedMarker; + } + if (bag->_emptyMarker == newValue) { + __CFBagFindNewEmptyMarker(bag); + } + if (bag->_deletedMarker == newValue) { + __CFBagFindNewDeletedMarker(bag); + } + match->_key = newValue; + } else { + CFAssert3(__kCFBagFixedMutable != __CFBagGetType(bag) || bag->_count < bag->_capacity, __kCFLogAssertion, "%s(): capacity exceeded on fixed-capacity bag %p (capacity = %d)", __PRETTY_FUNCTION__, bag, bag->_capacity); + if (bag->_emptyMarker == newValue) { + __CFBagFindNewEmptyMarker(bag); + } + if (bag->_deletedMarker == newValue) { + __CFBagFindNewDeletedMarker(bag); + } + nomatch->_key = newValue; + nomatch->_count = 1; + bag->_bucketsUsed++; + bag->_count++; + } +} + +void CFBagRemoveValue(CFMutableBagRef bag, const void *value) { + struct __CFBagBucket *match; + const CFBagCallBacks *cb; + __CFGenericValidateType(bag, __kCFBagTypeID); + switch (__CFBagGetType(bag)) { + case __kCFBagMutable: + case __kCFBagFixedMutable: + break; + default: + CFAssert2(__CFBagGetType(bag) != __kCFBagImmutable, __kCFLogAssertion, "%s(): immutable bag %p passed to mutating operation", __PRETTY_FUNCTION__, bag); + break; + } + if (0 == bag->_count) return; + __CFBagFindBuckets1(bag, value, &match); + if (!match) return; + match->_count--; bag->_count--; + if (0 == match->_count) { + cb = __CFBagGetCallBacks(bag); + if (cb->release) { + INVOKE_CALLBACK3(((void (*)(CFAllocatorRef, const void *, void *))cb->release), CFGetAllocator(bag), match->_key, bag->_context); + } + match->_key = bag->_deletedMarker; + bag->_bucketsUsed--; + } +} + +void CFBagRemoveAllValues(CFMutableBagRef bag) { + struct __CFBagBucket *buckets; + const CFBagCallBacks *cb; + CFAllocatorRef allocator; + CFIndex idx, nbuckets; + __CFGenericValidateType(bag, __kCFBagTypeID); + switch (__CFBagGetType(bag)) { + case __kCFBagMutable: + case __kCFBagFixedMutable: + break; + default: + CFAssert2(__CFBagGetType(bag) != __kCFBagImmutable, __kCFLogAssertion, "%s(): immutable bag %p passed to mutating operation", __PRETTY_FUNCTION__, bag); + break; + } + if (0 == bag->_count) return; + buckets = bag->_buckets; + nbuckets = bag->_bucketsNum; + cb = __CFBagGetCallBacks(bag); + allocator = CFGetAllocator(bag); + for (idx = 0; idx < nbuckets; idx++) { + if (__CFBagBucketIsOccupied(bag, &buckets[idx])) { + if (cb->release) { + INVOKE_CALLBACK3(((void (*)(CFAllocatorRef, const void *, void *))cb->release), allocator, buckets[idx]._key, bag->_context); + } + buckets[idx]._key = bag->_emptyMarker; + buckets[idx]._count = 0; + } + } + bag->_bucketsUsed = 0; + bag->_count = 0; +} + diff --git a/Collections.subproj/CFBag.h b/Collections.subproj/CFBag.h new file mode 100644 index 0000000..a95b9c9 --- /dev/null +++ b/Collections.subproj/CFBag.h @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFBag.h + Copyright (c) 1998-2003, Apple, Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFBAG__) +#define __COREFOUNDATION_CFBAG__ 1 + +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +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); + +#if defined(__cplusplus) +} +#endif + +#endif /* ! __COREFOUNDATION_CFBAG__ */ + diff --git a/Collections.subproj/CFBinaryHeap.c b/Collections.subproj/CFBinaryHeap.c new file mode 100644 index 0000000..cf0fcaa --- /dev/null +++ b/Collections.subproj/CFBinaryHeap.c @@ -0,0 +1,444 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFBinaryHeap.c + Copyright 1998-2002, Apple, Inc. All rights reserved. + Responsibility: Christopher Kane +*/ + +#include +#include "CFUtilities.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 << (CFLog2(capacity) + 1)); +} + +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 { + kCFBinaryHeapImmutable = 0x0, /* unchangable and fixed capacity; default */ + kCFBinaryHeapMutable = 0x1, /* changeable and variable capacity */ + kCFBinaryHeapFixedMutable = 0x3 /* changeable and fixed capacity */ +}; + +CF_INLINE UInt32 __CFBinaryHeapMutableVariety(const void *cf) { + return __CFBitfieldGetValue(((const CFRuntimeBase *)cf)->_info, 3, 2); +} + +CF_INLINE void __CFBinaryHeapSetMutableVariety(void *cf, UInt32 v) { + __CFBitfieldSetValue(((CFRuntimeBase *)cf)->_info, 3, 2, v); +} + +CF_INLINE UInt32 __CFBinaryHeapMutableVarietyFromFlags(UInt32 flags) { + return __CFBitfieldGetValue(flags, 1, 0); +} + +static bool __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) ? buffer : CFAllocatorAllocate(kCFAllocatorSystemDefault, 2 * cnt * sizeof(void *), 0); + 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 && compare && !compare(val1, val2, heap1->_context.info)) return false; + } + if (list1 != buffer) CFAllocatorDeallocate(CFGetAllocator(heap1), list1); + return true; +} + +static UInt32 __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("{count = %u, capacity = %u, objects = (\n"), cf, CFGetAllocator(heap), cnt, __CFBinaryHeapCapacity(heap)); + list = (cnt <= 128) ? buffer : CFAllocatorAllocate(kCFAllocatorSystemDefault, cnt * sizeof(void *), 0); + 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 : <0x%x>\n"), idx, (UInt32)item); + } + } + CFStringAppend(result, CFSTR(")}")); + if (list != buffer) CFAllocatorDeallocate(CFGetAllocator(heap), list); + return result; +} + +static void __CFBinaryHeapDeallocate(CFTypeRef cf) { + CFBinaryHeapRef heap = (CFBinaryHeapRef)cf; + CFAllocatorRef allocator = CFGetAllocator(heap); +// CF: should make the heap mutable here first, a la CFArrayDeallocate + CFBinaryHeapRemoveAllValues(heap); +// CF: does not release the context info + if (__CFBinaryHeapMutableVariety(heap) == kCFBinaryHeapMutable) { + CFAllocatorDeallocate(allocator, heap->_buckets); + } +} + +static CFTypeID __kCFBinaryHeapTypeID = _kCFRuntimeNotATypeID; + +static const CFRuntimeClass __CFBinaryHeapClass = { + 0, + "CFBinaryHeap", + NULL, // init + NULL, // copy + __CFBinaryHeapDeallocate, + (void *)__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) { +// CF: does not copy the compareContext argument into the object + CFBinaryHeapRef memory; + CFIndex idx; + CFIndex size; + + CFAssert2(0 <= capacity, __kCFLogAssertion, "%s(): capacity (%d) cannot be less than zero", __PRETTY_FUNCTION__, capacity); + CFAssert3(kCFBinaryHeapFixedMutable != __CFBinaryHeapMutableVarietyFromFlags(flags) || numValues <= capacity, __kCFLogAssertion, "%s(): for fixed-mutable type, capacity (%d) must be greater than or equal to number of initial elements (%d)", __PRETTY_FUNCTION__, capacity, numValues); + CFAssert2(0 <= numValues, __kCFLogAssertion, "%s(): numValues (%d) cannot be less than zero", __PRETTY_FUNCTION__, numValues); + size = sizeof(struct __CFBinaryHeap) - sizeof(CFRuntimeBase); + if (__CFBinaryHeapMutableVarietyFromFlags(flags) != kCFBinaryHeapMutable) + size += sizeof(struct __CFBinaryHeapBucket) * __CFBinaryHeapNumBucketsForCapacity(capacity); + memory = (CFBinaryHeapRef)_CFRuntimeCreateInstance(allocator, __kCFBinaryHeapTypeID, size, NULL); + if (NULL == memory) { + return NULL; + } + switch (__CFBinaryHeapMutableVarietyFromFlags(flags)) { + case kCFBinaryHeapMutable: + __CFBinaryHeapSetCapacity(memory, __CFBinaryHeapRoundUpCapacity(1)); + __CFBinaryHeapSetNumBuckets(memory, __CFBinaryHeapNumBucketsForCapacity(__CFBinaryHeapRoundUpCapacity(1))); + memory->_buckets = CFAllocatorAllocate(allocator, __CFBinaryHeapNumBuckets(memory) * sizeof(struct __CFBinaryHeapBucket), 0); + if (__CFOASafe) __CFSetLastAllocationEventName(memory->_buckets, "CFBinaryHeap (store)"); + if (NULL == memory->_buckets) { + CFRelease(memory); + return NULL; + } + break; + case kCFBinaryHeapFixedMutable: + case kCFBinaryHeapImmutable: + /* Don't round up capacity */ + __CFBinaryHeapSetCapacity(memory, capacity); + __CFBinaryHeapSetNumBuckets(memory, __CFBinaryHeapNumBucketsForCapacity(capacity)); + memory->_buckets = (struct __CFBinaryHeapBucket *)((int8_t *)memory + sizeof(struct __CFBinaryHeap)); + break; + } + __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; + } + if (__CFBinaryHeapMutableVarietyFromFlags(flags) != kCFBinaryHeapMutable) { + __CFBinaryHeapSetMutableVariety(memory, kCFBinaryHeapFixedMutable); + } else { + __CFBinaryHeapSetMutableVariety(memory, kCFBinaryHeapMutable); + } + for (idx = 0; idx < numValues; idx++) { + CFBinaryHeapAddValue(memory, values[idx]); + } + __CFBinaryHeapSetMutableVariety(memory, __CFBinaryHeapMutableVarietyFromFlags(flags)); + return memory; +} + +CFBinaryHeapRef CFBinaryHeapCreate(CFAllocatorRef allocator, CFIndex capacity, const CFBinaryHeapCallBacks *callBacks, const CFBinaryHeapCompareContext *compareContext) { + return __CFBinaryHeapInit(allocator, (0 == capacity) ? kCFBinaryHeapMutable : kCFBinaryHeapFixedMutable, capacity, NULL, 0, callBacks, compareContext); +} + +CFBinaryHeapRef CFBinaryHeapCreateCopy(CFAllocatorRef allocator, CFIndex capacity, CFBinaryHeapRef heap) { + __CFGenericValidateType(heap, __kCFBinaryHeapTypeID); + return __CFBinaryHeapInit(allocator, (0 == capacity) ? kCFBinaryHeapMutable : kCFBinaryHeapFixedMutable, 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) *value = heap->_buckets[0]._item; + 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; + 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); + __CFBinaryHeapSetCapacity(heap, capacity); + __CFBinaryHeapSetNumBuckets(heap, __CFBinaryHeapNumBucketsForCapacity(capacity)); + heap->_buckets = CFAllocatorReallocate(CFGetAllocator(heap), heap->_buckets, __CFBinaryHeapNumBuckets(heap) * sizeof(struct __CFBinaryHeapBucket), 0); + if (__CFOASafe) __CFSetLastAllocationEventName(heap->_buckets, "CFBinaryHeap (store)"); + if (NULL == heap->_buckets) HALT; +} + +void CFBinaryHeapAddValue(CFBinaryHeapRef heap, const void *value) { + CFIndex idx, pidx; + CFIndex cnt; + __CFGenericValidateType(heap, __kCFBinaryHeapTypeID); + CFAssert1(__CFBinaryHeapMutableVariety(heap) == kCFBinaryHeapMutable || __CFBinaryHeapMutableVariety(heap) == kCFBinaryHeapFixedMutable, __kCFLogAssertion, "%s(): binary heap is immutable", __PRETTY_FUNCTION__); + switch (__CFBinaryHeapMutableVariety(heap)) { + case kCFBinaryHeapMutable: + if (__CFBinaryHeapNumBucketsUsed(heap) == __CFBinaryHeapCapacity(heap)) + __CFBinaryHeapGrow(heap, 1); + break; + case kCFBinaryHeapFixedMutable: + CFAssert1(__CFBinaryHeapCount(heap) < __CFBinaryHeapCapacity(heap), __kCFLogAssertion, "%s(): fixed-capacity binary heap is full", __PRETTY_FUNCTION__); + 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; + heap->_buckets[idx]._item = item; + idx = pidx; + pidx = (idx - 1) >> 1; + } + if (heap->_callbacks.retain) { + heap->_buckets[idx]._item = (void *)heap->_callbacks.retain(CFGetAllocator(heap), (void *)value); + } else { + heap->_buckets[idx]._item = (void *)value; + } +} + +void CFBinaryHeapRemoveMinimumValue(CFBinaryHeapRef heap) { + void *val; + CFIndex idx, cidx; + CFIndex cnt; + __CFGenericValidateType(heap, __kCFBinaryHeapTypeID); + CFAssert1(__CFBinaryHeapMutableVariety(heap) == kCFBinaryHeapMutable || __CFBinaryHeapMutableVariety(heap) == kCFBinaryHeapFixedMutable, __kCFLogAssertion, "%s(): binary heap is immutable", __PRETTY_FUNCTION__); + cnt = __CFBinaryHeapCount(heap); + if (0 == cnt) return; + idx = 0; + __CFBinaryHeapSetNumBucketsUsed(heap, cnt - 1); + __CFBinaryHeapSetCount(heap, cnt - 1); + if (heap->_callbacks.release) + heap->_callbacks.release(CFGetAllocator(heap), 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; + heap->_buckets[idx]._item = item; + idx = cidx; + cidx = (idx << 1) + 1; + } + heap->_buckets[idx]._item = val; +} + +void CFBinaryHeapRemoveAllValues(CFBinaryHeapRef heap) { + CFIndex idx; + CFIndex cnt; + __CFGenericValidateType(heap, __kCFBinaryHeapTypeID); + CFAssert1(__CFBinaryHeapMutableVariety(heap) == kCFBinaryHeapMutable || __CFBinaryHeapMutableVariety(heap) == kCFBinaryHeapFixedMutable, __kCFLogAssertion, "%s(): binary heap is immutable", __PRETTY_FUNCTION__); + 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/Collections.subproj/CFBinaryHeap.h b/Collections.subproj/CFBinaryHeap.h new file mode 100644 index 0000000..2db26af --- /dev/null +++ b/Collections.subproj/CFBinaryHeap.h @@ -0,0 +1,316 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFBinaryHeap.h + Copyright (c) 1998-2003, 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 + +#if defined(__cplusplus) +extern "C" { +#endif + +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 or fixed-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 The maximum number of values that can be contained + by the CFBinaryHeap. The binary heap starts empty, and can grow to this + number of values (and it can have less). If this parameter + is 0, the binary heap's maximum capacity is unlimited (or rather, + 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 or fixed-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 The maximum number of values that can be contained + by the CFBinaryHeap. The binary heap starts empty, and can grow to this + number of values (and it can have less). If this parameter + is 0, the binary heap's maximum capacity is unlimited (or rather, + only limited by address space and available memory + constraints). If this parameter is negative, or less than the number of + values in the given binary heap, 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 or fixed-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. + If the binary heap is a fixed-capacity binary heap and it + is full before this operation, 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); + +#if defined(__cplusplus) +} +#endif + +#endif /* ! __COREFOUNDATION_CFBINARYHEAP__ */ + diff --git a/Collections.subproj/CFBitVector.c b/Collections.subproj/CFBitVector.c new file mode 100644 index 0000000..7fe20d3 --- /dev/null +++ b/Collections.subproj/CFBitVector.c @@ -0,0 +1,548 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFBitVector.c + Copyright 1998-2002, Apple, Inc. All rights reserved. + Responsibility: Christopher Kane +*/ + +#include +#include "CFInternal.h" +#include + +/* 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 (__CF_BITS_PER_BUCKET < 64) ? (capacity + 63) / 64 : (capacity + __CF_BITS_PER_BUCKET - 1) / __CF_BITS_PER_BUCKET; +} + +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)->_info, 3, 2); +} + +CF_INLINE void __CFBitVectorSetMutableVariety(void *cf, UInt32 v) { + __CFBitfieldSetValue(((CFRuntimeBase *)cf)->_info, 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 bool __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("{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) { + CFAllocatorDeallocate(allocator, bv->_buckets); + } +} + +static CFTypeID __kCFBitVectorTypeID = _kCFRuntimeNotATypeID; + +static const CFRuntimeClass __CFBitVectorClass = { + 0, + "CFBitVector", + NULL, // init + NULL, // copy + __CFBitVectorDeallocate, + (void *)__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))); + memory->_buckets = CFAllocatorAllocate(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); + } + __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 = ctx; + __CFBitVectorBucket val; + CFIndex nBits; + val = bucketValue & bucketValueMask; + nBits = __CFMin(__CF_BITS_PER_BUCKET, 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; + } + nBits -= context->initBits; + 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); + __CFBitVectorSetCapacity(bv, capacity); + __CFBitVectorSetNumBuckets(bv, __CFBitVectorNumBucketsForCapacity(capacity)); + bv->_buckets = CFAllocatorReallocate(CFGetAllocator(bv), 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/Collections.subproj/CFBitVector.h b/Collections.subproj/CFBitVector.h new file mode 100644 index 0000000..4cde214 --- /dev/null +++ b/Collections.subproj/CFBitVector.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFBitVector.h + Copyright (c) 1998-2003, Apple, Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFBITVECTOR__) +#define __COREFOUNDATION_CFBITVECTOR__ 1 + +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +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); + +#if defined(__cplusplus) +} +#endif + +#endif /* ! __COREFOUNDATION_CFBITVECTOR__ */ + diff --git a/Collections.subproj/CFData.c b/Collections.subproj/CFData.c new file mode 100644 index 0000000..3191091 --- /dev/null +++ b/Collections.subproj/CFData.c @@ -0,0 +1,396 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFData.c + Copyright 1998-2002, Apple, Inc. All rights reserved. + Responsibility: Christopher Kane +*/ + +#include +#include "CFUtilities.h" +#include "CFInternal.h" +#include + +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)->_info, 3, 2); +} + +CF_INLINE void __CFSetMutableVariety(void *cf, UInt32 v) { + __CFBitfieldSetValue(((CFRuntimeBase *)cf)->_info, 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 << (CFLog2(capacity) + 1)); +} + +CF_INLINE CFIndex __CFDataNumBytesForCapacity(CFIndex capacity) { + return capacity; +} + +#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 bool __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), 16)); +} + +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("{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: + CFAllocatorDeallocate(allocator, data->_bytes); + break; + case kCFFixedMutable: + break; + case kCFImmutable: + if (NULL != data->_bytesDeallocator) { + CFAllocatorDeallocate(data->_bytesDeallocator, data->_bytes); + CFRelease(data->_bytesDeallocator); + } + break; + } +} + +static CFTypeID __kCFDataTypeID = _kCFRuntimeNotATypeID; + +static const CFRuntimeClass __CFDataClass = { + 0, + "CFData", + NULL, // init + NULL, // copy + __CFDataDeallocate, + (void *)__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))); + // assume that allocators give 16-byte aligned memory back -- it is their responsibility + memory->_bytes = CFAllocatorAllocate(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) { + memory->_bytes = (uint8_t *)bytes; + memory->_bytesDeallocator = 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(); + if (!_CFExecutableLinkedOnOrAfter(CFSystemVersionJaguar)) { + bytesDeallocator = NULL; + } + 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); + __CFDataSetCapacity(data, capacity); + __CFDataSetNumBytes(data, __CFDataNumBytesForCapacity(capacity)); + data->_bytes = CFAllocatorReallocate(CFGetAllocator(data), data->_bytes, __CFDataNumBytes(data) * sizeof(uint8_t), 0); + if (__CFOASafe) __CFSetLastAllocationEventName(data->_bytes, "CFData (store)"); + if (NULL == data->_bytes) HALT; +} + +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) { + CFIndex len; + 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); + len = __CFDataLength(data); + switch (__CFMutableVariety(data)) { + case kCFMutable: + if (range.length < newLength && __CFDataNumBytes(data) < len - range.length + newLength) { + __CFDataGrow(data, newLength - range.length); + } + break; + case kCFFixedMutable: + CFAssert1(len - range.length + newLength <= __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, (len - range.length + newLength)); + __CFDataSetLength(data, (len - range.length + newLength)); +} + +#undef __CFDataValidateRange +#undef __CFGenericValidateMutabilityFlags + diff --git a/Collections.subproj/CFData.h b/Collections.subproj/CFData.h new file mode 100644 index 0000000..3b46f0d --- /dev/null +++ b/Collections.subproj/CFData.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFData.h + Copyright (c) 1998-2003, Apple, Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFDATA__) +#define __COREFOUNDATION_CFDATA__ 1 + +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +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); + +#if defined(__cplusplus) +} +#endif + +#endif /* ! __COREFOUNDATION_CFDATA__ */ + diff --git a/Collections.subproj/CFDictionary.c b/Collections.subproj/CFDictionary.c new file mode 100644 index 0000000..7f11d22 --- /dev/null +++ b/Collections.subproj/CFDictionary.c @@ -0,0 +1,1153 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFDictionary.c + Copyright 1998-2002, Apple, Inc. All rights reserved. + Responsibility: Christopher Kane +*/ + +#include +#include "CFInternal.h" + +const CFDictionaryKeyCallBacks kCFTypeDictionaryKeyCallBacks = {0, __CFTypeCollectionRetain, __CFTypeCollectionRelease, CFCopyDescription, CFEqual, CFHash}; +const CFDictionaryKeyCallBacks kCFCopyStringDictionaryKeyCallBacks = {0, (void *)CFStringCreateCopy, __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}; + +static const uint32_t __CFDictionaryCapacities[42] = { + 4, 8, 17, 29, 47, 76, 123, 199, 322, 521, 843, 1364, 2207, 3571, 5778, 9349, + 15127, 24476, 39603, 64079, 103682, 167761, 271443, 439204, 710647, 1149851, 1860498, + 3010349, 4870847, 7881196, 12752043, 20633239, 33385282, 54018521, 87403803, 141422324, + 228826127, 370248451, 599074578, 969323029, 1568397607, 2537720636U +}; + +static const uint32_t __CFDictionaryBuckets[42] = { // primes + 5, 11, 23, 41, 67, 113, 199, 317, 521, 839, 1361, 2207, 3571, 5779, 9349, 15121, + 24473, 39607, 64081, 103681, 167759, 271429, 439199, 710641, 1149857, 1860503, 3010349, + 4870843, 7881193, 12752029, 20633237, 33385273, 54018521, 87403763, 141422317, 228826121, + 370248451, 599074561, 969323023, 1568397599, 2537720629U, 4106118251U +}; + +CF_INLINE CFIndex __CFDictionaryRoundUpCapacity(CFIndex capacity) { + CFIndex idx; + for (idx = 0; idx < 42 && __CFDictionaryCapacities[idx] < (uint32_t)capacity; idx++); + if (42 <= idx) HALT; + return __CFDictionaryCapacities[idx]; +} + +CF_INLINE CFIndex __CFDictionaryNumBucketsForCapacity(CFIndex capacity) { + CFIndex idx; + for (idx = 0; idx < 42 && __CFDictionaryCapacities[idx] < (uint32_t)capacity; idx++); + if (42 <= idx) HALT; + return __CFDictionaryBuckets[idx]; +} + +enum { /* Bits 1-0 */ + __kCFDictionaryImmutable = 0, /* unchangable and fixed capacity */ + __kCFDictionaryMutable = 1, /* changeable and variable capacity */ + __kCFDictionaryFixedMutable = 3 /* changeable and fixed capacity */ +}; + +enum { /* Bits 5-4 (value), 3-2 (key) */ + __kCFDictionaryHasNullCallBacks = 0, + __kCFDictionaryHasCFTypeCallBacks = 1, + __kCFDictionaryHasCustomCallBacks = 3 /* callbacks are at end of header */ +}; + +struct __CFDictionary { + CFRuntimeBase _base; + CFIndex _count; /* number of values */ + CFIndex _capacity; /* maximum number of values */ + CFIndex _bucketsNum; /* number of slots */ + uintptr_t _marker; + void *_context; /* private */ + CFIndex _deletes; + const void **_keys; /* can be NULL if not allocated yet */ + const void **_values; /* can be NULL if not allocated yet */ +}; + +/* Bits 1-0 of the base reserved bits are used for mutability variety */ +/* Bits 3-2 of the base reserved bits are used for key callback indicator bits */ +/* Bits 5-4 of the base reserved bits are used for value callback indicator bits */ +/* Bit 6 is special KVO actions bit */ + +CF_INLINE CFIndex __CFDictionaryGetType(CFDictionaryRef dict) { + return __CFBitfieldGetValue(((const CFRuntimeBase *)dict)->_info, 1, 0); +} + +CF_INLINE CFIndex __CFDictionaryGetSizeOfType(CFIndex t) { + CFIndex size = sizeof(struct __CFDictionary); + if (__CFBitfieldGetValue(t, 3, 2) == __kCFDictionaryHasCustomCallBacks) { + size += sizeof(CFDictionaryKeyCallBacks); + } + if (__CFBitfieldGetValue(t, 5, 4) == __kCFDictionaryHasCustomCallBacks) { + size += sizeof(CFDictionaryValueCallBacks); + } + return size; +} + +CF_INLINE const CFDictionaryKeyCallBacks *__CFDictionaryGetKeyCallBacks(CFDictionaryRef dict) { + CFDictionaryKeyCallBacks *result = NULL; + switch (__CFBitfieldGetValue(((const CFRuntimeBase *)dict)->_info, 3, 2)) { + case __kCFDictionaryHasNullCallBacks: + return &__kCFNullDictionaryKeyCallBacks; + case __kCFDictionaryHasCFTypeCallBacks: + return &kCFTypeDictionaryKeyCallBacks; + case __kCFDictionaryHasCustomCallBacks: + break; + } + result = (CFDictionaryKeyCallBacks *)((uint8_t *)dict + sizeof(struct __CFDictionary)); + return result; +} + +CF_INLINE bool __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 bool __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(CFDictionaryRef dict) { + CFDictionaryValueCallBacks *result = NULL; + switch (__CFBitfieldGetValue(((const CFRuntimeBase *)dict)->_info, 5, 4)) { + case __kCFDictionaryHasNullCallBacks: + return &__kCFNullDictionaryValueCallBacks; + case __kCFDictionaryHasCFTypeCallBacks: + return &kCFTypeDictionaryValueCallBacks; + case __kCFDictionaryHasCustomCallBacks: + break; + } + if (__CFBitfieldGetValue(((const CFRuntimeBase *)dict)->_info, 3, 2) == __kCFDictionaryHasCustomCallBacks) { + result = (CFDictionaryValueCallBacks *)((uint8_t *)dict + sizeof(struct __CFDictionary) + sizeof(CFDictionaryKeyCallBacks)); + } else { + result = (CFDictionaryValueCallBacks *)((uint8_t *)dict + sizeof(struct __CFDictionary)); + } + return result; +} + +CF_INLINE bool __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 bool __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(CFDictionaryRef dict) { + return __CFBitfieldGetValue(((const CFRuntimeBase *)dict)->_info, 6, 6); +} + +void _CFDictionarySetKVOBit(CFDictionaryRef dict, CFIndex bit) { + __CFBitfieldSetValue(((CFRuntimeBase *)dict)->_info, 6, 6, (bit & 0x1)); +} + +#if !defined(__MACH__) + +#define CF_OBJC_KVO_WILLCHANGE(obj, sel, a1) +#define CF_OBJC_KVO_DIDCHANGE(obj, sel, a1) + +#else + +static SEL __CF_KVO_WillChangeSelector = 0; +static SEL __CF_KVO_DidChangeSelector = 0; + +#define CF_OBJC_KVO_WILLCHANGE(obj, key) \ + if (__CFBitfieldGetValue(((const CFRuntimeBase *)dict)->_info, 6, 6)) \ + {void (*func)(const void *, SEL, ...) = (void *)__CFSendObjCMsg; \ + if (!__CF_KVO_WillChangeSelector) __CF_KVO_WillChangeSelector = __CFGetObjCSelector("willChangeValueForKey:"); \ + func((const void *)(obj), __CF_KVO_WillChangeSelector, (key));} + +#define CF_OBJC_KVO_DIDCHANGE(obj, key) \ + if (__CFBitfieldGetValue(((const CFRuntimeBase *)dict)->_info, 6, 6)) \ + {void (*func)(const void *, SEL, ...) = (void *)__CFSendObjCMsg; \ + if (!__CF_KVO_DidChangeSelector) __CF_KVO_DidChangeSelector = __CFGetObjCSelector("didChangeValueForKey:"); \ + func((const void *)(obj), __CF_KVO_DidChangeSelector, (key));} + +#endif + + +static CFIndex __CFDictionaryFindBuckets1a(CFDictionaryRef dict, const void *key) { + CFHashCode keyHash = (CFHashCode)key; + const void **keys = dict->_keys; + uintptr_t marker = dict->_marker; + CFIndex probe = keyHash % dict->_bucketsNum; + CFIndex probeskip = 1; // See RemoveValue() for notes before changing this value + CFIndex start = probe; + for (;;) { + uintptr_t currKey = (uintptr_t)keys[probe]; + if (marker == currKey) { /* empty */ + return kCFNotFound; + } else if (~marker == currKey) { /* deleted */ + /* do nothing */ + } else if (currKey == (uintptr_t)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 (dict->_bucketsNum <= probe) { + probe -= dict->_bucketsNum; + } + if (start == probe) { + return kCFNotFound; + } + } +} + +static CFIndex __CFDictionaryFindBuckets1b(CFDictionaryRef dict, const void *key) { + const CFDictionaryKeyCallBacks *cb = __CFDictionaryGetKeyCallBacks(dict); + CFHashCode keyHash = cb->hash ? (CFHashCode)INVOKE_CALLBACK2(((CFHashCode (*)(const void *, void *))cb->hash), key, dict->_context) : (CFHashCode)key; + const void **keys = dict->_keys; + uintptr_t marker = dict->_marker; + CFIndex probe = keyHash % dict->_bucketsNum; + CFIndex probeskip = 1; // See RemoveValue() for notes before changing this value + CFIndex start = probe; + for (;;) { + uintptr_t currKey = (uintptr_t)keys[probe]; + if (marker == currKey) { /* empty */ + return kCFNotFound; + } else if (~marker == currKey) { /* deleted */ + /* do nothing */ + } else if (currKey == (uintptr_t)key || (cb->equal && INVOKE_CALLBACK3((Boolean (*)(void *, void *, void*))cb->equal, currKey, key, dict->_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 (dict->_bucketsNum <= probe) { + probe -= dict->_bucketsNum; + } + if (start == probe) { + return kCFNotFound; + } + } +} + +static void __CFDictionaryFindBuckets2(CFDictionaryRef dict, const void *key, CFIndex *match, CFIndex *nomatch) { + const CFDictionaryKeyCallBacks *cb = __CFDictionaryGetKeyCallBacks(dict); + CFHashCode keyHash = cb->hash ? (CFHashCode)INVOKE_CALLBACK2(((CFHashCode (*)(const void *, void *))cb->hash), key, dict->_context) : (CFHashCode)key; + const void **keys = dict->_keys; + uintptr_t marker = dict->_marker; + CFIndex probe = keyHash % dict->_bucketsNum; + CFIndex probeskip = 1; // See RemoveValue() for notes before changing this value + CFIndex start = probe; + *match = kCFNotFound; + *nomatch = kCFNotFound; + for (;;) { + uintptr_t currKey = (uintptr_t)keys[probe]; + if (marker == currKey) { /* empty */ + if (kCFNotFound == *nomatch) *nomatch = probe; + return; + } else if (~marker == currKey) { /* deleted */ + if (kCFNotFound == *nomatch) *nomatch = probe; + if (kCFNotFound != *match) { + return; + } + } else if (kCFNotFound == *match && (currKey == (uintptr_t)key || (cb->equal && INVOKE_CALLBACK3((Boolean (*)(void *, void *, void*))cb->equal, currKey, key, dict->_context)))) { + *match = probe; + if (kCFNotFound != *nomatch) { + return; + } + } + probe = probe + probeskip; + // This alternative to probe % buckets assumes that + // probeskip is always positive and less than the + // number of buckets. + if (dict->_bucketsNum <= probe) { + probe -= dict->_bucketsNum; + } + if (start == probe) { + return; + } + } +} + +static void __CFDictionaryFindNewMarker(CFDictionaryRef dict) { + const void **keys = dict->_keys; + uintptr_t newMarker; + CFIndex idx, nbuckets; + bool hit; + + nbuckets = dict->_bucketsNum; + newMarker = dict->_marker; + do { + newMarker--; + hit = false; + for (idx = 0; idx < nbuckets; idx++) { + if (newMarker == (uintptr_t)keys[idx] || ~newMarker == (uintptr_t)keys[idx]) { + hit = true; + break; + } + } + } while (hit); + for (idx = 0; idx < nbuckets; idx++) { + if (dict->_marker == (uintptr_t)keys[idx]) { + keys[idx] = (const void *)newMarker; + } else if (~dict->_marker == (uintptr_t)keys[idx]) { + keys[idx] = (const void *)~newMarker; + } + } + ((struct __CFDictionary *)dict)->_marker = newMarker; +} + +static bool __CFDictionaryEqual(CFTypeRef cf1, CFTypeRef cf2) { + CFDictionaryRef dict1 = (CFDictionaryRef)cf1; + CFDictionaryRef dict2 = (CFDictionaryRef)cf2; + const CFDictionaryKeyCallBacks *cb1, *cb2; + const CFDictionaryValueCallBacks *vcb1, *vcb2; + const void **keys; + CFIndex idx, nbuckets; + if (dict1 == dict2) return true; + if (dict1->_count != dict2->_count) return false; + cb1 = __CFDictionaryGetKeyCallBacks(dict1); + cb2 = __CFDictionaryGetKeyCallBacks(dict2); + if (cb1->equal != cb2->equal) return false; + vcb1 = __CFDictionaryGetValueCallBacks(dict1); + vcb2 = __CFDictionaryGetValueCallBacks(dict2); + if (vcb1->equal != vcb2->equal) return false; + if (0 == dict1->_count) return true; /* after function comparison! */ + keys = dict1->_keys; + nbuckets = dict1->_bucketsNum; + for (idx = 0; idx < nbuckets; idx++) { + if (dict1->_marker != (uintptr_t)keys[idx] && ~dict1->_marker != (uintptr_t)keys[idx]) { + const void *value; + if (!CFDictionaryGetValueIfPresent(dict2, keys[idx], &value)) return false; + if (dict1->_values[idx] != value && vcb1->equal && !INVOKE_CALLBACK3((Boolean (*)(void *, void *, void*))vcb1->equal, dict1->_values[idx], value, dict1->_context)) { + return false; + } + } + } + return true; +} + +static CFHashCode __CFDictionaryHash(CFTypeRef cf) { + CFDictionaryRef dict = (CFDictionaryRef)cf; + return dict->_count; +} + +static CFStringRef __CFDictionaryCopyDescription(CFTypeRef cf) { + CFDictionaryRef dict = (CFDictionaryRef)cf; + CFAllocatorRef allocator; + const CFDictionaryKeyCallBacks *cb; + const CFDictionaryValueCallBacks *vcb; + const void **keys; + CFIndex idx, nbuckets; + CFMutableStringRef result; + cb = __CFDictionaryGetKeyCallBacks(dict); + vcb = __CFDictionaryGetValueCallBacks(dict); + keys = dict->_keys; + nbuckets = dict->_bucketsNum; + allocator = CFGetAllocator(dict); + result = CFStringCreateMutable(allocator, 0); + switch (__CFDictionaryGetType(dict)) { + case __kCFDictionaryImmutable: + CFStringAppendFormat(result, NULL, CFSTR("{type = immutable, count = %u, capacity = %u, pairs = (\n"), cf, allocator, dict->_count, dict->_capacity); + break; + case __kCFDictionaryFixedMutable: + CFStringAppendFormat(result, NULL, CFSTR("{type = fixed-mutable, count = %u, capacity = %u, pairs = (\n"), cf, allocator, dict->_count, dict->_capacity); + break; + case __kCFDictionaryMutable: + CFStringAppendFormat(result, NULL, CFSTR("{type = mutable, count = %u, capacity = %u, pairs = (\n"), cf, allocator, dict->_count, dict->_capacity); + break; + } + for (idx = 0; idx < nbuckets; idx++) { + if (dict->_marker != (uintptr_t)keys[idx] && ~dict->_marker != (uintptr_t)keys[idx]) { + CFStringRef kDesc = NULL, vDesc = NULL; + if (NULL != cb->copyDescription) { + kDesc = (CFStringRef)INVOKE_CALLBACK2(((CFStringRef (*)(const void *, void *))cb->copyDescription), keys[idx], dict->_context); + } + if (NULL != vcb->copyDescription) { + vDesc = (CFStringRef)INVOKE_CALLBACK2(((CFStringRef (*)(const void *, void *))vcb->copyDescription), dict->_values[idx], dict->_context); + } + 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, dict->_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], dict->_values[idx]); + } + } + } + CFStringAppend(result, CFSTR(")}")); + return result; +} + +static void __CFDictionaryDeallocate(CFTypeRef cf) { + CFMutableDictionaryRef dict = (CFMutableDictionaryRef)cf; + CFAllocatorRef allocator = __CFGetAllocator(dict); + if (__CFDictionaryGetType(dict) == __kCFDictionaryImmutable) { + __CFBitfieldSetValue(((CFRuntimeBase *)dict)->_info, 1, 0, __kCFDictionaryFixedMutable); + } + CFDictionaryRemoveAllValues(dict); + if (__CFDictionaryGetType(dict) == __kCFDictionaryMutable && dict->_keys) { + CFAllocatorDeallocate(allocator, dict->_keys); + } +} + +static CFTypeID __kCFDictionaryTypeID = _kCFRuntimeNotATypeID; + +static const CFRuntimeClass __CFDictionaryClass = { + 0, + "CFDictionary", + NULL, // init + NULL, // copy + __CFDictionaryDeallocate, + (void *)__CFDictionaryEqual, + __CFDictionaryHash, + NULL, // + __CFDictionaryCopyDescription +}; + +__private_extern__ void __CFDictionaryInitialize(void) { + __kCFDictionaryTypeID = _CFRuntimeRegisterClass(&__CFDictionaryClass); +} + +CFTypeID CFDictionaryGetTypeID(void) { + return __kCFDictionaryTypeID; +} + +static CFDictionaryRef __CFDictionaryInit(CFAllocatorRef allocator, uint32_t flags, CFIndex capacity, const CFDictionaryKeyCallBacks *callBacks, const CFDictionaryValueCallBacks *valueCallBacks) { + struct __CFDictionary *memory; + uint32_t size; + CFIndex idx; + __CFBitfieldSetValue(flags, 31, 2, 0); + if (__CFDictionaryKeyCallBacksMatchNull(callBacks)) { + __CFBitfieldSetValue(flags, 3, 2, __kCFDictionaryHasNullCallBacks); + } else if (__CFDictionaryKeyCallBacksMatchCFType(callBacks)) { + __CFBitfieldSetValue(flags, 3, 2, __kCFDictionaryHasCFTypeCallBacks); + } else { + __CFBitfieldSetValue(flags, 3, 2, __kCFDictionaryHasCustomCallBacks); + } + if (__CFDictionaryValueCallBacksMatchNull(valueCallBacks)) { + __CFBitfieldSetValue(flags, 5, 4, __kCFDictionaryHasNullCallBacks); + } else if (__CFDictionaryValueCallBacksMatchCFType(valueCallBacks)) { + __CFBitfieldSetValue(flags, 5, 4, __kCFDictionaryHasCFTypeCallBacks); + } else { + __CFBitfieldSetValue(flags, 5, 4, __kCFDictionaryHasCustomCallBacks); + } + size = __CFDictionaryGetSizeOfType(flags) - sizeof(CFRuntimeBase); + switch (__CFBitfieldGetValue(flags, 1, 0)) { + case __kCFDictionaryImmutable: + case __kCFDictionaryFixedMutable: + size += 2 * __CFDictionaryNumBucketsForCapacity(capacity) * sizeof(const void *); + break; + case __kCFDictionaryMutable: + break; + } + memory = (struct __CFDictionary *)_CFRuntimeCreateInstance(allocator, __kCFDictionaryTypeID, size, NULL); + if (NULL == memory) { + return NULL; + } + __CFBitfieldSetValue(memory->_base._info, 6, 0, flags); + memory->_count = 0; + memory->_marker = (uintptr_t)0xa1b1c1d3; + memory->_context = NULL; + memory->_deletes = 0; + switch (__CFBitfieldGetValue(flags, 1, 0)) { + case __kCFDictionaryImmutable: + if (__CFOASafe) __CFSetLastAllocationEventName(memory, "CFDictionary (immutable)"); + memory->_capacity = capacity; /* Don't round up capacity */ + memory->_bucketsNum = __CFDictionaryNumBucketsForCapacity(memory->_capacity); + memory->_keys = (const void **)((uint8_t *)memory + __CFDictionaryGetSizeOfType(flags)); + memory->_values = (const void **)(memory->_keys + memory->_bucketsNum); + for (idx = memory->_bucketsNum; idx--;) { + memory->_keys[idx] = (const void *)memory->_marker; + } + break; + case __kCFDictionaryFixedMutable: + if (__CFOASafe) __CFSetLastAllocationEventName(memory, "CFDictionary (mutable-fixed)"); + memory->_capacity = capacity; /* Don't round up capacity */ + memory->_bucketsNum = __CFDictionaryNumBucketsForCapacity(memory->_capacity); + memory->_keys = (const void **)((uint8_t *)memory + __CFDictionaryGetSizeOfType(flags)); + memory->_values = (const void **)(memory->_keys + memory->_bucketsNum); + for (idx = memory->_bucketsNum; idx--;) { + memory->_keys[idx] = (const void *)memory->_marker; + } + break; + case __kCFDictionaryMutable: + if (__CFOASafe) __CFSetLastAllocationEventName(memory, "CFDictionary (mutable-variable)"); + memory->_capacity = __CFDictionaryRoundUpCapacity(1); + memory->_bucketsNum = 0; + memory->_keys = NULL; + memory->_values = NULL; + break; + } + if (__kCFDictionaryHasCustomCallBacks == __CFBitfieldGetValue(flags, 3, 2)) { + const CFDictionaryKeyCallBacks *cb = __CFDictionaryGetKeyCallBacks((CFDictionaryRef)memory); + *(CFDictionaryKeyCallBacks *)cb = *callBacks; + 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 (__kCFDictionaryHasCustomCallBacks == __CFBitfieldGetValue(flags, 5, 4)) { + const CFDictionaryValueCallBacks *vcb = __CFDictionaryGetValueCallBacks((CFDictionaryRef)memory); + *(CFDictionaryValueCallBacks *)vcb = *valueCallBacks; + FAULT_CALLBACK((void **)&(vcb->retain)); + FAULT_CALLBACK((void **)&(vcb->release)); + FAULT_CALLBACK((void **)&(vcb->copyDescription)); + FAULT_CALLBACK((void **)&(vcb->equal)); + } + return (CFDictionaryRef)memory; +} + +CFDictionaryRef CFDictionaryCreate(CFAllocatorRef allocator, const void **keys, const void **values, CFIndex numValues, const CFDictionaryKeyCallBacks *keyCallBacks, const CFDictionaryValueCallBacks *valueCallBacks) { + CFDictionaryRef result; + uint32_t flags; + CFIndex idx; + CFAssert2(0 <= numValues, __kCFLogAssertion, "%s(): numValues (%d) cannot be less than zero", __PRETTY_FUNCTION__, numValues); + result = __CFDictionaryInit(allocator, __kCFDictionaryImmutable, numValues, keyCallBacks, valueCallBacks); + flags = __CFBitfieldGetValue(((const CFRuntimeBase *)result)->_info, 1, 0); + if (flags == __kCFDictionaryImmutable) { + __CFBitfieldSetValue(((CFRuntimeBase *)result)->_info, 1, 0, __kCFDictionaryFixedMutable); + } + for (idx = 0; idx < numValues; idx++) { + CFDictionaryAddValue((CFMutableDictionaryRef)result, keys[idx], values[idx]); + } + __CFBitfieldSetValue(((CFRuntimeBase *)result)->_info, 1, 0, flags); + return result; +} + +CFMutableDictionaryRef CFDictionaryCreateMutable(CFAllocatorRef allocator, CFIndex capacity, const CFDictionaryKeyCallBacks *keyCallBacks, const CFDictionaryValueCallBacks *valueCallBacks) { + CFAssert2(0 <= capacity, __kCFLogAssertion, "%s(): capacity (%d) cannot be less than zero", __PRETTY_FUNCTION__, capacity); + return (CFMutableDictionaryRef)__CFDictionaryInit(allocator, (0 == capacity) ? __kCFDictionaryMutable : __kCFDictionaryFixedMutable, capacity, keyCallBacks, valueCallBacks); +} + +static void __CFDictionaryGrow(CFMutableDictionaryRef dict, CFIndex numNewValues); + +CFDictionaryRef _CFDictionaryCreate_ex(CFAllocatorRef allocator, bool mutable, const void **keys, const void **values, CFIndex numValues) { + CFDictionaryRef result; + uint32_t flags; + CFIndex idx; + result = __CFDictionaryInit(allocator, mutable ? __kCFDictionaryMutable : __kCFDictionaryImmutable, numValues, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + flags = __CFBitfieldGetValue(((const CFRuntimeBase *)result)->_info, 1, 0); + if (!mutable) { + __CFBitfieldSetValue(((CFRuntimeBase *)result)->_info, 1, 0, __kCFDictionaryFixedMutable); + } + if (mutable) { + if (result->_count == result->_capacity || NULL == result->_keys) { + __CFDictionaryGrow((CFMutableDictionaryRef)result, numValues); + } + } + for (idx = 0; idx < numValues; idx++) { + CFIndex match, nomatch; + const void *newKey; + __CFDictionaryFindBuckets2(result, keys[idx], &match, &nomatch); + if (kCFNotFound != match) { + CFRelease(result->_values[match]); + result->_values[match] = values[idx]; + } else { + newKey = keys[idx]; + if (result->_marker == (uintptr_t)newKey || ~result->_marker == (uintptr_t)newKey) { + __CFDictionaryFindNewMarker(result); + } + result->_keys[nomatch] = newKey; + result->_values[nomatch] = values[idx]; + ((CFMutableDictionaryRef)result)->_count++; + } + } + __CFBitfieldSetValue(((CFRuntimeBase *)result)->_info, 1, 0, flags); + return result; +} + +CFDictionaryRef CFDictionaryCreateCopy(CFAllocatorRef allocator, CFDictionaryRef dict) { + CFDictionaryRef result; + const CFDictionaryKeyCallBacks *cb; + const CFDictionaryValueCallBacks *vcb; + CFIndex numValues = CFDictionaryGetCount(dict); + const void **list, *buffer[256]; + const void **vlist, *vbuffer[256]; + list = (numValues <= 256) ? buffer : CFAllocatorAllocate(allocator, numValues * sizeof(void *), 0); + if (list != buffer && __CFOASafe) __CFSetLastAllocationEventName(list, "CFDictionary (temp)"); + vlist = (numValues <= 256) ? vbuffer : CFAllocatorAllocate(allocator, numValues * sizeof(void *), 0); + if (vlist != vbuffer && __CFOASafe) __CFSetLastAllocationEventName(vlist, "CFDictionary (temp)"); + CFDictionaryGetKeysAndValues(dict, list, vlist); + cb = CF_IS_OBJC(__kCFDictionaryTypeID, dict) ? &kCFTypeDictionaryKeyCallBacks : __CFDictionaryGetKeyCallBacks(dict); + vcb = CF_IS_OBJC(__kCFDictionaryTypeID, dict) ? &kCFTypeDictionaryValueCallBacks : __CFDictionaryGetValueCallBacks(dict); + result = CFDictionaryCreate(allocator, list, vlist, numValues, cb, vcb); + if (list != buffer) CFAllocatorDeallocate(allocator, list); + if (vlist != vbuffer) CFAllocatorDeallocate(allocator, vlist); + return result; +} + +CFMutableDictionaryRef CFDictionaryCreateMutableCopy(CFAllocatorRef allocator, CFIndex capacity, CFDictionaryRef dict) { + CFMutableDictionaryRef result; + const CFDictionaryKeyCallBacks *cb; + const CFDictionaryValueCallBacks *vcb; + CFIndex idx, numValues = CFDictionaryGetCount(dict); + const void **list, *buffer[256]; + const void **vlist, *vbuffer[256]; + CFAssert3(0 == capacity || numValues <= capacity, __kCFLogAssertion, "%s(): for fixed-mutable dicts, capacity (%d) must be greater than or equal to initial number of values (%d)", __PRETTY_FUNCTION__, capacity, numValues); + list = (numValues <= 256) ? buffer : CFAllocatorAllocate(allocator, numValues * sizeof(void *), 0); + if (list != buffer && __CFOASafe) __CFSetLastAllocationEventName(list, "CFDictionary (temp)"); + vlist = (numValues <= 256) ? vbuffer : CFAllocatorAllocate(allocator, numValues * sizeof(void *), 0); + if (vlist != vbuffer && __CFOASafe) __CFSetLastAllocationEventName(vlist, "CFDictionary (temp)"); + CFDictionaryGetKeysAndValues(dict, list, vlist); + cb = CF_IS_OBJC(__kCFDictionaryTypeID, dict) ? &kCFTypeDictionaryKeyCallBacks : __CFDictionaryGetKeyCallBacks(dict); + vcb = CF_IS_OBJC(__kCFDictionaryTypeID, dict) ? &kCFTypeDictionaryValueCallBacks : __CFDictionaryGetValueCallBacks(dict); + result = CFDictionaryCreateMutable(allocator, capacity, cb, vcb); + if (0 == capacity) _CFDictionarySetCapacity(result, numValues); + for (idx = 0; idx < numValues; idx++) { + CFDictionaryAddValue(result, list[idx], vlist[idx]); + } + if (list != buffer) CFAllocatorDeallocate(allocator, list); + if (vlist != vbuffer) CFAllocatorDeallocate(allocator, vlist); + return result; +} + +// Used by NSMapTables and KVO +void _CFDictionarySetContext(CFDictionaryRef dict, void *context) { + ((struct __CFDictionary *)dict)->_context = context; +} + +void *_CFDictionaryGetContext(CFDictionaryRef dict) { + return ((struct __CFDictionary *)dict)->_context; +} + +CFIndex CFDictionaryGetCount(CFDictionaryRef dict) { + CF_OBJC_FUNCDISPATCH0(__kCFDictionaryTypeID, CFIndex, dict, "count"); + __CFGenericValidateType(dict, __kCFDictionaryTypeID); + return dict->_count; +} + +CFIndex CFDictionaryGetCountOfKey(CFDictionaryRef dict, const void *key) { + CFIndex match; + CF_OBJC_FUNCDISPATCH1(__kCFDictionaryTypeID, CFIndex, dict, "countForKey:", key); + __CFGenericValidateType(dict, __kCFDictionaryTypeID); + if (0 == dict->_count) return 0; + if (__kCFDictionaryHasNullCallBacks == __CFBitfieldGetValue(((const CFRuntimeBase *)dict)->_info, 3, 2)) { + match = __CFDictionaryFindBuckets1a(dict, key); + } else { + match = __CFDictionaryFindBuckets1b(dict, key); + } + return (kCFNotFound != match ? 1 : 0); +} + +CFIndex CFDictionaryGetCountOfValue(CFDictionaryRef dict, const void *value) { + const void **keys; + const CFDictionaryValueCallBacks *vcb; + CFIndex idx, cnt = 0, nbuckets; + CF_OBJC_FUNCDISPATCH1(__kCFDictionaryTypeID, CFIndex, dict, "countForObject:", value); + __CFGenericValidateType(dict, __kCFDictionaryTypeID); + if (0 == dict->_count) return 0; + keys = dict->_keys; + nbuckets = dict->_bucketsNum; + vcb = __CFDictionaryGetValueCallBacks(dict); + for (idx = 0; idx < nbuckets; idx++) { + if (dict->_marker != (uintptr_t)keys[idx] && ~dict->_marker != (uintptr_t)keys[idx]) { + if ((dict->_values[idx] == value) || (vcb->equal && INVOKE_CALLBACK3((Boolean (*)(void *, void *, void*))vcb->equal, dict->_values[idx], value, dict->_context))) { + cnt++; + } + } + } + return cnt; +} + +Boolean CFDictionaryContainsKey(CFDictionaryRef dict, const void *key) { + CFIndex match; + CF_OBJC_FUNCDISPATCH1(__kCFDictionaryTypeID, char, dict, "containsKey:", key); + __CFGenericValidateType(dict, __kCFDictionaryTypeID); + if (0 == dict->_count) return false; + if (__kCFDictionaryHasNullCallBacks == __CFBitfieldGetValue(((const CFRuntimeBase *)dict)->_info, 3, 2)) { + match = __CFDictionaryFindBuckets1a(dict, key); + } else { + match = __CFDictionaryFindBuckets1b(dict, key); + } + return (kCFNotFound != match ? true : false); +} + +Boolean CFDictionaryContainsValue(CFDictionaryRef dict, const void *value) { + const void **keys; + const CFDictionaryValueCallBacks *vcb; + CFIndex idx, nbuckets; + CF_OBJC_FUNCDISPATCH1(__kCFDictionaryTypeID, char, dict, "containsObject:", value); + __CFGenericValidateType(dict, __kCFDictionaryTypeID); + if (0 == dict->_count) return false; + keys = dict->_keys; + nbuckets = dict->_bucketsNum; + vcb = __CFDictionaryGetValueCallBacks(dict); + for (idx = 0; idx < nbuckets; idx++) { + if (dict->_marker != (uintptr_t)keys[idx] && ~dict->_marker != (uintptr_t)keys[idx]) { + if ((dict->_values[idx] == value) || (vcb->equal && INVOKE_CALLBACK3((Boolean (*)(void *, void *, void*))vcb->equal, dict->_values[idx], value, dict->_context))) { + return true; + } + } + } + return false; +} + +const void *CFDictionaryGetValue(CFDictionaryRef dict, const void *key) { + CFIndex match; + CF_OBJC_FUNCDISPATCH1(__kCFDictionaryTypeID, const void *, dict, "objectForKey:", key); + __CFGenericValidateType(dict, __kCFDictionaryTypeID); + if (0 == dict->_count) return NULL; + if (__kCFDictionaryHasNullCallBacks == __CFBitfieldGetValue(((const CFRuntimeBase *)dict)->_info, 3, 2)) { + match = __CFDictionaryFindBuckets1a(dict, key); + } else { + match = __CFDictionaryFindBuckets1b(dict, key); + } + return (kCFNotFound != match ? dict->_values[match] : NULL); +} + +Boolean CFDictionaryGetValueIfPresent(CFDictionaryRef dict, const void *key, const void **value) { + CFIndex match; + CF_OBJC_FUNCDISPATCH2(__kCFDictionaryTypeID, char, dict, "_getValue:forKey:", (void * *)value, key); + __CFGenericValidateType(dict, __kCFDictionaryTypeID); + if (0 == dict->_count) return false; + if (__kCFDictionaryHasNullCallBacks == __CFBitfieldGetValue(((const CFRuntimeBase *)dict)->_info, 3, 2)) { + match = __CFDictionaryFindBuckets1a(dict, key); + } else { + match = __CFDictionaryFindBuckets1b(dict, key); + } + return (kCFNotFound != match ? ((value ? *value = dict->_values[match] : NULL), true) : false); +} + +bool CFDictionaryGetKeyIfPresent(CFDictionaryRef dict, const void *key, const void **actualkey) { + CFIndex match; +//#warning CF: not toll-free bridged + __CFGenericValidateType(dict, __kCFDictionaryTypeID); + if (0 == dict->_count) return false; + if (__kCFDictionaryHasNullCallBacks == __CFBitfieldGetValue(((const CFRuntimeBase *)dict)->_info, 3, 2)) { + match = __CFDictionaryFindBuckets1a(dict, key); + } else { + match = __CFDictionaryFindBuckets1b(dict, key); + } + return (kCFNotFound != match ? ((actualkey ? *actualkey = dict->_keys[match] : NULL), true) : false); +} + +void CFDictionaryGetKeysAndValues(CFDictionaryRef dict, const void **keys, const void **values) { + CFIndex idx, cnt, nbuckets; + CF_OBJC_FUNCDISPATCH2(__kCFDictionaryTypeID, void, dict, "getObjects:andKeys:", (void * *)values, (void * *)keys); + __CFGenericValidateType(dict, __kCFDictionaryTypeID); + nbuckets = dict->_bucketsNum; + for (idx = 0; idx < nbuckets; idx++) { + if (dict->_marker != (uintptr_t)dict->_keys[idx] && ~dict->_marker != (uintptr_t)dict->_keys[idx]) { + for (cnt = 1; cnt--;) { + if (keys) *keys++ = dict->_keys[idx]; + if (values) *values++ = dict->_values[idx]; + } + } + } +} + +void CFDictionaryApplyFunction(CFDictionaryRef dict, CFDictionaryApplierFunction applier, void *context) { + const void **keys; + CFIndex idx, cnt, nbuckets; + FAULT_CALLBACK((void **)&(applier)); + CF_OBJC_FUNCDISPATCH2(__kCFDictionaryTypeID, void, dict, "_apply:context:", applier, context); + __CFGenericValidateType(dict, __kCFDictionaryTypeID); + keys = dict->_keys; + nbuckets = dict->_bucketsNum; + for (idx = 0; idx < nbuckets; idx++) { + if (dict->_marker != (uintptr_t)keys[idx] && ~dict->_marker != (uintptr_t)keys[idx]) { + for (cnt = 1; cnt--;) { + INVOKE_CALLBACK3(applier, keys[idx], dict->_values[idx], context); + } + } + } +} + +static void __CFDictionaryGrow(CFMutableDictionaryRef dict, CFIndex numNewValues) { + const void **oldkeys = dict->_keys; + const void **oldvalues = dict->_values; + CFIndex idx, oldnbuckets = dict->_bucketsNum; + CFIndex oldCount = dict->_count; + dict->_capacity = __CFDictionaryRoundUpCapacity(oldCount + numNewValues); + dict->_bucketsNum = __CFDictionaryNumBucketsForCapacity(dict->_capacity); + dict->_deletes = 0; + dict->_keys = CFAllocatorAllocate(__CFGetAllocator(dict), 2 * dict->_bucketsNum * sizeof(const void *), 0); + dict->_values = (const void **)(dict->_keys + dict->_bucketsNum); + if (NULL == dict->_keys) HALT; + if (__CFOASafe) __CFSetLastAllocationEventName(dict->_keys, "CFDictionary (store)"); + for (idx = dict->_bucketsNum; idx--;) { + dict->_keys[idx] = (const void *)dict->_marker; + } + if (NULL == oldkeys) return; + for (idx = 0; idx < oldnbuckets; idx++) { + if (dict->_marker != (uintptr_t)oldkeys[idx] && ~dict->_marker != (uintptr_t)oldkeys[idx]) { + CFIndex match, nomatch; + __CFDictionaryFindBuckets2(dict, 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], dict->_keys[match]); + dict->_keys[nomatch] = oldkeys[idx]; + dict->_values[nomatch] = oldvalues[idx]; + } + } + CFAssert1(dict->_count == oldCount, __kCFLogAssertion, "%s(): dict count differs after rehashing; error", __PRETTY_FUNCTION__); + CFAllocatorDeallocate(__CFGetAllocator(dict), oldkeys); +} + +// This function is for Foundation's benefit; no one else should use it. +void _CFDictionarySetCapacity(CFMutableDictionaryRef dict, CFIndex cap) { + if (CF_IS_OBJC(__kCFDictionaryTypeID, dict)) return; +#if defined(DEBUG) + __CFGenericValidateType(dict, __kCFDictionaryTypeID); + CFAssert1(__CFDictionaryGetType(dict) != __kCFDictionaryImmutable && __CFDictionaryGetType(dict) != __kCFDictionaryFixedMutable, __kCFLogAssertion, "%s(): dict is immutable or fixed-mutable", __PRETTY_FUNCTION__); + CFAssert3(dict->_count <= cap, __kCFLogAssertion, "%s(): desired capacity (%d) is less than count (%d)", __PRETTY_FUNCTION__, cap, dict->_count); +#endif + __CFDictionaryGrow(dict, cap - dict->_count); +} + +// This function is for Foundation's benefit; no one else should use it. +bool _CFDictionaryIsMutable(CFDictionaryRef dict) { + return (__CFDictionaryGetType(dict) != __kCFDictionaryImmutable); +} + +void CFDictionaryAddValue(CFMutableDictionaryRef dict, const void *key, const void *value) { + CFIndex match, nomatch; + const CFDictionaryKeyCallBacks *cb; + const CFDictionaryValueCallBacks *vcb; + const void *newKey, *newValue; + CF_OBJC_FUNCDISPATCH2(__kCFDictionaryTypeID, void, dict, "_addObject:forKey:", value, key); + __CFGenericValidateType(dict, __kCFDictionaryTypeID); + switch (__CFDictionaryGetType(dict)) { + case __kCFDictionaryMutable: + if (dict->_count == dict->_capacity || NULL == dict->_keys) { + __CFDictionaryGrow(dict, 1); + } + break; + case __kCFDictionaryFixedMutable: + CFAssert3(dict->_count < dict->_capacity, __kCFLogAssertion, "%s(): capacity exceeded on fixed-capacity dict %p (capacity = %d)", __PRETTY_FUNCTION__, dict, dict->_capacity); + break; + default: + CFAssert2(__CFDictionaryGetType(dict) != __kCFDictionaryImmutable, __kCFLogAssertion, "%s(): immutable dict %p passed to mutating operation", __PRETTY_FUNCTION__, dict); + break; + } + __CFDictionaryFindBuckets2(dict, key, &match, &nomatch); + if (kCFNotFound != match) { + } else { + cb = __CFDictionaryGetKeyCallBacks(dict); + vcb = __CFDictionaryGetValueCallBacks(dict); + if (cb->retain) { + newKey = (void *)INVOKE_CALLBACK3(((const void *(*)(CFAllocatorRef, const void *, void *))cb->retain), __CFGetAllocator(dict), key, dict->_context); + } else { + newKey = key; + } + if (vcb->retain) { + newValue = (void *)INVOKE_CALLBACK3(((const void *(*)(CFAllocatorRef, const void *, void *))vcb->retain), __CFGetAllocator(dict), value, dict->_context); + } else { + newValue = value; + } + if (dict->_marker == (uintptr_t)newKey || ~dict->_marker == (uintptr_t)newKey) { + __CFDictionaryFindNewMarker(dict); + } + CF_OBJC_KVO_WILLCHANGE(dict, key); + dict->_keys[nomatch] = newKey; + dict->_values[nomatch] = newValue; + dict->_count++; + CF_OBJC_KVO_DIDCHANGE(dict, key); + } +} + +void CFDictionaryReplaceValue(CFMutableDictionaryRef dict, const void *key, const void *value) { + CFIndex match; + const CFDictionaryValueCallBacks *vcb; + const void *newValue; + CF_OBJC_FUNCDISPATCH2(__kCFDictionaryTypeID, void, dict, "_replaceObject:forKey:", value, key); + __CFGenericValidateType(dict, __kCFDictionaryTypeID); + switch (__CFDictionaryGetType(dict)) { + case __kCFDictionaryMutable: + case __kCFDictionaryFixedMutable: + break; + default: + CFAssert2(__CFDictionaryGetType(dict) != __kCFDictionaryImmutable, __kCFLogAssertion, "%s(): immutable dict %p passed to mutating operation", __PRETTY_FUNCTION__, dict); + break; + } + if (0 == dict->_count) return; + if (__kCFDictionaryHasNullCallBacks == __CFBitfieldGetValue(((const CFRuntimeBase *)dict)->_info, 3, 2)) { + match = __CFDictionaryFindBuckets1a(dict, key); + } else { + match = __CFDictionaryFindBuckets1b(dict, key); + } + if (kCFNotFound == match) return; + vcb = __CFDictionaryGetValueCallBacks(dict); + if (vcb->retain) { + newValue = (void *)INVOKE_CALLBACK3(((const void *(*)(CFAllocatorRef, const void *, void *))vcb->retain), __CFGetAllocator(dict), value, dict->_context); + } else { + newValue = value; + } + CF_OBJC_KVO_WILLCHANGE(dict, key); + if (vcb->release) { + INVOKE_CALLBACK3(((void (*)(CFAllocatorRef, const void *, void *))vcb->release), __CFGetAllocator(dict), dict->_values[match], dict->_context); + } + dict->_values[match] = newValue; + CF_OBJC_KVO_DIDCHANGE(dict, key); +} + +void CFDictionarySetValue(CFMutableDictionaryRef dict, const void *key, const void *value) { + CFIndex match, nomatch; + const CFDictionaryKeyCallBacks *cb; + const CFDictionaryValueCallBacks *vcb; + const void *newKey, *newValue; + CF_OBJC_FUNCDISPATCH2(__kCFDictionaryTypeID, void, dict, "setObject:forKey:", value, key); + __CFGenericValidateType(dict, __kCFDictionaryTypeID); + switch (__CFDictionaryGetType(dict)) { + case __kCFDictionaryMutable: + if (dict->_count == dict->_capacity || NULL == dict->_keys) { + __CFDictionaryGrow(dict, 1); + } + break; + case __kCFDictionaryFixedMutable: + break; + default: + CFAssert2(__CFDictionaryGetType(dict) != __kCFDictionaryImmutable, __kCFLogAssertion, "%s(): immutable dict %p passed to mutating operation", __PRETTY_FUNCTION__, dict); + break; + } + __CFDictionaryFindBuckets2(dict, key, &match, &nomatch); + vcb = __CFDictionaryGetValueCallBacks(dict); + if (vcb->retain) { + newValue = (void *)INVOKE_CALLBACK3(((const void *(*)(CFAllocatorRef, const void *, void *))vcb->retain), __CFGetAllocator(dict), value, dict->_context); + } else { + newValue = value; + } + if (kCFNotFound != match) { + CF_OBJC_KVO_WILLCHANGE(dict, key); + if (vcb->release) { + INVOKE_CALLBACK3(((void (*)(CFAllocatorRef, const void *, void *))vcb->release), __CFGetAllocator(dict), dict->_values[match], dict->_context); + } + dict->_values[match] = newValue; + CF_OBJC_KVO_DIDCHANGE(dict, key); + } else { + CFAssert3(__kCFDictionaryFixedMutable != __CFDictionaryGetType(dict) || dict->_count < dict->_capacity, __kCFLogAssertion, "%s(): capacity exceeded on fixed-capacity dict %p (capacity = %d)", __PRETTY_FUNCTION__, dict, dict->_capacity); + cb = __CFDictionaryGetKeyCallBacks(dict); + if (cb->retain) { + newKey = (void *)INVOKE_CALLBACK3(((const void *(*)(CFAllocatorRef, const void *, void *))cb->retain), __CFGetAllocator(dict), key, dict->_context); + } else { + newKey = key; + } + if (dict->_marker == (uintptr_t)newKey || ~dict->_marker == (uintptr_t)newKey) { + __CFDictionaryFindNewMarker(dict); + } + CF_OBJC_KVO_WILLCHANGE(dict, key); + dict->_keys[nomatch] = newKey; + dict->_values[nomatch] = newValue; + dict->_count++; + CF_OBJC_KVO_DIDCHANGE(dict, key); + } +} + +void CFDictionaryRemoveValue(CFMutableDictionaryRef dict, const void *key) { + CFIndex match; + const CFDictionaryKeyCallBacks *cb; + const CFDictionaryValueCallBacks *vcb; + CF_OBJC_FUNCDISPATCH1(__kCFDictionaryTypeID, void, dict, "removeObjectForKey:", key); + __CFGenericValidateType(dict, __kCFDictionaryTypeID); + switch (__CFDictionaryGetType(dict)) { + case __kCFDictionaryMutable: + case __kCFDictionaryFixedMutable: + break; + default: + CFAssert2(__CFDictionaryGetType(dict) != __kCFDictionaryImmutable, __kCFLogAssertion, "%s(): immutable dict %p passed to mutating operation", __PRETTY_FUNCTION__, dict); + break; + } + if (0 == dict->_count) return; + if (__kCFDictionaryHasNullCallBacks == __CFBitfieldGetValue(((const CFRuntimeBase *)dict)->_info, 3, 2)) { + match = __CFDictionaryFindBuckets1a(dict, key); + } else { + match = __CFDictionaryFindBuckets1b(dict, key); + } + if (kCFNotFound == match) return; + if (1) { + cb = __CFDictionaryGetKeyCallBacks(dict); + vcb = __CFDictionaryGetValueCallBacks(dict); + const void *oldkey = dict->_keys[match]; + CF_OBJC_KVO_WILLCHANGE(dict, oldkey); + if (vcb->release) { + INVOKE_CALLBACK3(((void (*)(CFAllocatorRef, const void *, void *))vcb->release), __CFGetAllocator(dict), dict->_values[match], dict->_context); + } + dict->_keys[match] = (const void *)~dict->_marker; + dict->_count--; + CF_OBJC_KVO_DIDCHANGE(dict, oldkey); + if (cb->release) { + INVOKE_CALLBACK3(((void (*)(CFAllocatorRef, const void *, void *))cb->release), __CFGetAllocator(dict), oldkey, dict->_context); + } + dict->_deletes++; + // _CFDictionaryDecrementValue() below has this same code. + if ((__kCFDictionaryMutable == __CFDictionaryGetType(dict)) && (dict->_bucketsNum < 4 * dict->_deletes || (512 < dict->_capacity && 3.236067 * dict->_count < dict->_capacity))) { + // 3.236067 == 2 * golden_mean; this comes about because we're trying to resize down + // when the count is less than 2 capacities smaller, but not right away when count + // is just less than 2 capacities smaller, because an add would then force growth; + // well, the ratio between one capacity and the previous is close to the golden + // mean currently, so (cap / m / m) would be two smaller; but so we're not close, + // we take the average of that and the prior cap (cap / m / m / m). Well, after one + // does the algebra, and uses the convenient fact that m^(x+2) = m^(x+1) + m^x if m + // is the golden mean, this reduces to cap / 2m for the threshold. In general, the + // possible threshold constant is 1 / (2 * m^k), k = 0, 1, 2, ... under this scheme. + // Rehash; currently only for mutable-variable dictionaries + __CFDictionaryGrow(dict, 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. + // _CFDictionaryDecrementValue() below has this same code. + if (match < dict->_bucketsNum - 1 && dict->_keys[match + 1] == (const void *)dict->_marker) { + while (0 <= match && dict->_keys[match] == (const void *)~dict->_marker) { + dict->_keys[match] = (const void *)dict->_marker; + dict->_deletes--; + match--; + } + } + } + } +} + +void CFDictionaryRemoveAllValues(CFMutableDictionaryRef dict) { + const void **keys; + const CFDictionaryKeyCallBacks *cb; + const CFDictionaryValueCallBacks *vcb; + CFAllocatorRef allocator; + CFIndex idx, nbuckets; + CF_OBJC_FUNCDISPATCH0(__kCFDictionaryTypeID, void, dict, "removeAllObjects"); + __CFGenericValidateType(dict, __kCFDictionaryTypeID); + switch (__CFDictionaryGetType(dict)) { + case __kCFDictionaryMutable: + case __kCFDictionaryFixedMutable: + break; + default: + CFAssert2(__CFDictionaryGetType(dict) != __kCFDictionaryImmutable, __kCFLogAssertion, "%s(): immutable dict %p passed to mutating operation", __PRETTY_FUNCTION__, dict); + break; + } + if (0 == dict->_count) return; + keys = dict->_keys; + nbuckets = dict->_bucketsNum; + cb = __CFDictionaryGetKeyCallBacks(dict); + vcb = __CFDictionaryGetValueCallBacks(dict); + allocator = __CFGetAllocator(dict); + for (idx = 0; idx < nbuckets; idx++) { + if (dict->_marker != (uintptr_t)keys[idx] && ~dict->_marker != (uintptr_t)keys[idx]) { + const void *oldkey = keys[idx]; + CF_OBJC_KVO_WILLCHANGE(dict, oldkey); + if (vcb->release) { + INVOKE_CALLBACK3(((void (*)(CFAllocatorRef, const void *, void *))vcb->release), allocator, dict->_values[idx], dict->_context); + } + keys[idx] = (const void *)~dict->_marker; + dict->_count--; + CF_OBJC_KVO_DIDCHANGE(dict, oldkey); + if (cb->release) { + INVOKE_CALLBACK3(((void (*)(CFAllocatorRef, const void *, void *))cb->release), allocator, oldkey, dict->_context); + } + } + } + for (idx = 0; idx < nbuckets; idx++) { + keys[idx] = (const void *)dict->_marker; + } + dict->_count = 0; + dict->_deletes = 0; + if ((__kCFDictionaryMutable == __CFDictionaryGetType(dict)) && (512 < dict->_capacity)) { + __CFDictionaryGrow(dict, 256); + } +} + +void _CFDictionaryIncrementValue(CFMutableDictionaryRef dict, const void *key) { + CFIndex match, nomatch; + + __CFGenericValidateType(dict, __kCFDictionaryTypeID); + CFAssert1(__CFDictionaryGetType(dict) == __kCFDictionaryMutable, __kCFLogAssertion, "%s(): invalid dict type passed to increment operation", __PRETTY_FUNCTION__); + CFAssert1(__kCFDictionaryHasNullCallBacks == __CFBitfieldGetValue(((const CFRuntimeBase *)dict)->_info, 3, 2), __kCFLogAssertion, "%s(): invalid dict passed to increment operation", __PRETTY_FUNCTION__); + CFAssert1(__kCFDictionaryHasNullCallBacks == __CFBitfieldGetValue(((const CFRuntimeBase *)dict)->_info, 5, 4), __kCFLogAssertion, "%s(): invalid dict passed to increment operation", __PRETTY_FUNCTION__); + + match = kCFNotFound; + if (NULL != dict->_keys) { + __CFDictionaryFindBuckets2(dict, key, &match, &nomatch); + } + if (kCFNotFound != match) { + if (dict->_values[match] != (void *)UINT_MAX) { + dict->_values[match] = (void *)((uintptr_t)dict->_values[match] + 1); + } + } else { + if (dict->_marker == (uintptr_t)key || ~dict->_marker == (uintptr_t)key) { + __CFDictionaryFindNewMarker(dict); + } + if (dict->_count == dict->_capacity || NULL == dict->_keys) { + __CFDictionaryGrow(dict, 1); + __CFDictionaryFindBuckets2(dict, key, &match, &nomatch); + } + dict->_keys[nomatch] = key; + dict->_values[nomatch] = (void *)1; + dict->_count++; + } +} + +int _CFDictionaryDecrementValue(CFMutableDictionaryRef dict, const void *key) { + CFIndex match; + + __CFGenericValidateType(dict, __kCFDictionaryTypeID); + CFAssert1(__CFDictionaryGetType(dict) == __kCFDictionaryMutable, __kCFLogAssertion, "%s(): invalid dict type passed to increment operation", __PRETTY_FUNCTION__); + CFAssert1(__kCFDictionaryHasNullCallBacks == __CFBitfieldGetValue(((const CFRuntimeBase *)dict)->_info, 3, 2), __kCFLogAssertion, "%s(): invalid dict passed to increment operation", __PRETTY_FUNCTION__); + CFAssert1(__kCFDictionaryHasNullCallBacks == __CFBitfieldGetValue(((const CFRuntimeBase *)dict)->_info, 5, 4), __kCFLogAssertion, "%s(): invalid dict passed to increment operation", __PRETTY_FUNCTION__); + + if (0 == dict->_count) return -1; + match = __CFDictionaryFindBuckets1a(dict, key); + if (kCFNotFound == match) return -1; + if (1 == (uintptr_t)dict->_values[match]) { + dict->_count--; + dict->_values[match] = 0; + dict->_keys[match] = (const void *)~dict->_marker; + dict->_deletes++; + if ((__kCFDictionaryMutable == __CFDictionaryGetType(dict)) && (dict->_bucketsNum < 4 * dict->_deletes || (512 < dict->_capacity && 3.236067 * dict->_count < dict->_capacity))) { + __CFDictionaryGrow(dict, 0); + } else { + if (match < dict->_bucketsNum - 1 && dict->_keys[match + 1] == (const void *)dict->_marker) { + while (0 <= match && dict->_keys[match] == (const void *)~dict->_marker) { + dict->_keys[match] = (const void *)dict->_marker; + dict->_deletes--; + match--; + } + } + } + return 0; + } else if (dict->_values[match] != (void *)UINT_MAX) { + dict->_values[match] = (void *)((uintptr_t)dict->_values[match] - 1); + } + return 1; +} + diff --git a/Collections.subproj/CFDictionary.h b/Collections.subproj/CFDictionary.h new file mode 100644 index 0000000..16a4727 --- /dev/null +++ b/Collections.subproj/CFDictionary.h @@ -0,0 +1,704 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFDictionary.h + Copyright (c) 1998-2003, 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 have two subflavors, + fixed-capacity, for which there is a maximum number set at creation + time of values which can be put into the dictionary, and variable + capacity, which can have an unlimited number of values (or rather, + limited only by constraints external to CFDictionary, like the + amount of available memory). Fixed-capacity dictionaries can be + somewhat higher performing, if you can put a definate upper limit + on the number of values that might be put into the dictionary. + + 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 + +#if defined(__cplusplus) +extern "C" { +#endif + +/*! + @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 The maximum number of values that can be contained by + the CFDictionary. The dictionary starts empty, and can grow + to this number of values (and it can have less). If this + parameter is 0, the dictionary's maximum capacity is unlimited + (or rather, 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 The maximum number of values that can be contained + by the CFDictionary. The dictionary starts empty, and can grow + to this number of values (and it can have less). If this + parameter is 0, the dictionary's maximum capacity is unlimited + (or rather, 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. If the dictionary is a fixed-capacity dictionary and + it is full before this operation, 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. If the dictionary is a fixed-capacity dictionary and + it is full before this operation, and the key does not exist in + the dictionary, 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); + +#if defined(__cplusplus) +} +#endif + +#endif /* ! __COREFOUNDATION_CFDICTIONARY__ */ + diff --git a/Collections.subproj/CFSet.c b/Collections.subproj/CFSet.c new file mode 100644 index 0000000..5984c52 --- /dev/null +++ b/Collections.subproj/CFSet.c @@ -0,0 +1,817 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFSet.c + Copyright 1998-2002, Apple, Inc. All rights reserved. + Responsibility: Christopher Kane +*/ + +#include +#include "CFInternal.h" + +const CFSetCallBacks kCFTypeSetCallBacks = {0, __CFTypeCollectionRetain, __CFTypeCollectionRelease, CFCopyDescription, CFEqual, CFHash}; +const CFSetCallBacks kCFCopyStringSetCallBacks = {0, (void *)CFStringCreateCopy, __CFTypeCollectionRelease, CFCopyDescription, CFEqual, CFHash}; +static const CFSetCallBacks __kCFNullSetCallBacks = {0, NULL, NULL, NULL, NULL, NULL}; + + +static const uint32_t __CFSetCapacities[42] = { + 4, 8, 17, 29, 47, 76, 123, 199, 322, 521, 843, 1364, 2207, 3571, 5778, 9349, + 15127, 24476, 39603, 64079, 103682, 167761, 271443, 439204, 710647, 1149851, 1860498, + 3010349, 4870847, 7881196, 12752043, 20633239, 33385282, 54018521, 87403803, 141422324, + 228826127, 370248451, 599074578, 969323029, 1568397607, 2537720636U +}; + +static const uint32_t __CFSetBuckets[42] = { // primes + 5, 11, 23, 41, 67, 113, 199, 317, 521, 839, 1361, 2207, 3571, 5779, 9349, 15121, + 24473, 39607, 64081, 103681, 167759, 271429, 439199, 710641, 1149857, 1860503, 3010349, + 4870843, 7881193, 12752029, 20633237, 33385273, 54018521, 87403763, 141422317, 228826121, + 370248451, 599074561, 969323023, 1568397599, 2537720629U, 4106118251U +}; + +CF_INLINE CFIndex __CFSetRoundUpCapacity(CFIndex capacity) { + CFIndex idx; + for (idx = 0; idx < 42 && __CFSetCapacities[idx] < (uint32_t)capacity; idx++); + if (42 <= idx) HALT; + return __CFSetCapacities[idx]; +} + +CF_INLINE CFIndex __CFSetNumBucketsForCapacity(CFIndex capacity) { + CFIndex idx; + for (idx = 0; idx < 42 && __CFSetCapacities[idx] < (uint32_t)capacity; idx++); + if (42 <= idx) HALT; + return __CFSetBuckets[idx]; +} + +enum { /* Bits 1-0 */ + __kCFSetImmutable = 0, /* unchangable and fixed capacity */ + __kCFSetMutable = 1, /* changeable and variable capacity */ + __kCFSetFixedMutable = 3 /* changeable and fixed capacity */ +}; + +enum { /* Bits 3-2 */ + __kCFSetHasNullCallBacks = 0, + __kCFSetHasCFTypeCallBacks = 1, + __kCFSetHasCustomCallBacks = 3 /* callbacks are at end of header */ +}; + +struct __CFSetBucket { + const void *_key; +}; + +struct __CFSet { + CFRuntimeBase _base; + CFIndex _count; /* number of values */ + CFIndex _capacity; /* maximum number of values */ + CFIndex _bucketsUsed; /* number of slots used */ + CFIndex _bucketsNum; /* number of slots */ + const void *_emptyMarker; + const void *_deletedMarker; + void *_context; /* private */ + struct __CFSetBucket *_buckets; /* can be NULL if not allocated yet */ +}; + +CF_INLINE bool __CFSetBucketIsEmpty(CFSetRef set, const struct __CFSetBucket *bucket) { + return (set->_emptyMarker == bucket->_key); +} + +CF_INLINE bool __CFSetBucketIsDeleted(CFSetRef set, const struct __CFSetBucket *bucket) { + return (set->_deletedMarker == bucket->_key); +} + +CF_INLINE bool __CFSetBucketIsOccupied(CFSetRef set, const struct __CFSetBucket *bucket) { + return (set->_emptyMarker != bucket->_key && set->_deletedMarker != bucket->_key); +} + +/* Bits 1-0 of the base reserved bits are used for mutability variety */ +/* Bits 3-2 of the base reserved bits are used for callback indicator bits */ + +CF_INLINE CFIndex __CFSetGetType(CFSetRef set) { + return __CFBitfieldGetValue(((const CFRuntimeBase *)set)->_info, 1, 0); +} + +CF_INLINE CFIndex __CFSetGetSizeOfType(CFIndex t) { + CFIndex size = sizeof(struct __CFSet); + if (__CFBitfieldGetValue(t, 3, 2) == __kCFSetHasCustomCallBacks) { + size += sizeof(CFSetCallBacks); + } + return size; +} + +CF_INLINE const CFSetCallBacks *__CFSetGetCallBacks(CFSetRef set) { + CFSetCallBacks *result = NULL; + switch (__CFBitfieldGetValue(((const CFRuntimeBase *)set)->_info, 3, 2)) { + case __kCFSetHasNullCallBacks: + return &__kCFNullSetCallBacks; + case __kCFSetHasCFTypeCallBacks: + return &kCFTypeSetCallBacks; + case __kCFSetHasCustomCallBacks: + break; + } + result = (CFSetCallBacks *)((uint8_t *)set + sizeof(struct __CFSet)); + return result; +} + +CF_INLINE bool __CFSetCallBacksMatchNull(const CFSetCallBacks *c) { + return (NULL == c || + (c->retain == __kCFNullSetCallBacks.retain && + c->release == __kCFNullSetCallBacks.release && + c->copyDescription == __kCFNullSetCallBacks.copyDescription && + c->equal == __kCFNullSetCallBacks.equal && + c->hash == __kCFNullSetCallBacks.hash)); +} + +CF_INLINE bool __CFSetCallBacksMatchCFType(const CFSetCallBacks *c) { + return (&kCFTypeSetCallBacks == c || + (c->retain == kCFTypeSetCallBacks.retain && + c->release == kCFTypeSetCallBacks.release && + c->copyDescription == kCFTypeSetCallBacks.copyDescription && + c->equal == kCFTypeSetCallBacks.equal && + c->hash == kCFTypeSetCallBacks.hash)); +} + + +static void __CFSetFindBuckets1(CFSetRef set, const void *key, struct __CFSetBucket **match) { + const CFSetCallBacks *cb = __CFSetGetCallBacks(set); + struct __CFSetBucket *buckets = set->_buckets; + CFHashCode keyHash = cb->hash ? (CFHashCode)INVOKE_CALLBACK2(((CFHashCode (*)(const void *, void *))cb->hash), key, set->_context) : (CFHashCode)key; + UInt32 start = keyHash % set->_bucketsNum; + UInt32 probe = start; + UInt32 probeskip = 1; + *match = NULL; + for (;;) { + struct __CFSetBucket *currentBucket = buckets + probe; + if (__CFSetBucketIsEmpty(set, currentBucket)) { + return; + } else if (__CFSetBucketIsDeleted(set, currentBucket)) { + /* do nothing */ + } else if (currentBucket->_key == key || (cb->equal && INVOKE_CALLBACK3((Boolean (*)(void *, void *, void*))cb->equal, currentBucket->_key, key, set->_context))) { + *match = currentBucket; + return; + } + probe = (probe + probeskip) % set->_bucketsNum; + if (start == probe) return; + } +} + +static void __CFSetFindBuckets2(CFSetRef set, const void *key, struct __CFSetBucket **match, struct __CFSetBucket **nomatch) { + const CFSetCallBacks *cb = __CFSetGetCallBacks(set); + struct __CFSetBucket *buckets = set->_buckets; + CFHashCode keyHash = cb->hash ? (CFHashCode)INVOKE_CALLBACK2(((CFHashCode (*)(const void *, void *))cb->hash), key, set->_context) : (CFHashCode)key; + UInt32 start = keyHash % set->_bucketsNum; + UInt32 probe = start; + UInt32 probeskip = 1; + *match = NULL; + *nomatch = NULL; + for (;;) { + struct __CFSetBucket *currentBucket = buckets + probe; + if (__CFSetBucketIsEmpty(set, currentBucket)) { + if (!*nomatch) *nomatch = currentBucket; + return; + } else if (__CFSetBucketIsDeleted(set, currentBucket)) { + if (!*nomatch) *nomatch = currentBucket; + } else if (!*match && (currentBucket->_key == key || (cb->equal && INVOKE_CALLBACK3((Boolean (*)(void *, void *, void*))cb->equal, currentBucket->_key, key, set->_context)))) { + *match = currentBucket; + if (*nomatch) return; + } + probe = (probe + probeskip) % set->_bucketsNum; + if (start == probe) return; + } +} + +static void __CFSetFindNewEmptyMarker(CFSetRef set) { + struct __CFSetBucket *buckets; + const void *newEmpty; + bool hit; + CFIndex idx, nbuckets; + buckets = set->_buckets; + nbuckets = set->_bucketsNum; + newEmpty = set->_emptyMarker; + do { + (intptr_t)newEmpty -= 2; + hit = false; + for (idx = 0; idx < nbuckets; idx++) { + if (newEmpty == buckets[idx]._key) { + hit = true; + break; + } + } + } while (hit); + for (idx = 0; idx < nbuckets; idx++) { + if (set->_emptyMarker == buckets[idx]._key) { + buckets[idx]._key = newEmpty; + } + } + ((struct __CFSet *)set)->_emptyMarker = newEmpty; +} + +static void __CFSetFindNewDeletedMarker(CFSetRef set) { + struct __CFSetBucket *buckets; + const void *newDeleted; + bool hit; + CFIndex idx, nbuckets; + buckets = set->_buckets; + nbuckets = set->_bucketsNum; + newDeleted = set->_deletedMarker; + do { + (intptr_t)newDeleted += 2; + hit = false; + for (idx = 0; idx < nbuckets; idx++) { + if (newDeleted == buckets[idx]._key) { + hit = true; + break; + } + } + } while (hit); + for (idx = 0; idx < nbuckets; idx++) { + if (set->_deletedMarker == buckets[idx]._key) { + buckets[idx]._key = newDeleted; + } + } + ((struct __CFSet *)set)->_deletedMarker = newDeleted; +} + +static bool __CFSetEqual(CFTypeRef cf1, CFTypeRef cf2) { + CFSetRef set1 = (CFSetRef)cf1; + CFSetRef set2 = (CFSetRef)cf2; + const CFSetCallBacks *cb1, *cb2; + const struct __CFSetBucket *buckets; + CFIndex idx, nbuckets; + if (set1 == set2) return true; + if (set1->_count != set2->_count) return false; + cb1 = __CFSetGetCallBacks(set1); + cb2 = __CFSetGetCallBacks(set2); + if (cb1->equal != cb2->equal) return false; + if (0 == set1->_count) return true; /* after function comparison! */ + buckets = set1->_buckets; + nbuckets = set1->_bucketsNum; + for (idx = 0; idx < nbuckets; idx++) { + if (__CFSetBucketIsOccupied(set1, &buckets[idx])) { + if (1 != CFSetGetCountOfValue(set2, buckets[idx]._key)) { + return false; + } + } + } + return true; +} + +static CFHashCode __CFSetHash(CFTypeRef cf) { + CFSetRef set = (CFSetRef)cf; + return set->_count; +} + +static CFStringRef __CFSetCopyDescription(CFTypeRef cf) { + CFSetRef set = (CFSetRef)cf; + const CFSetCallBacks *cb; + const struct __CFSetBucket *buckets; + CFIndex idx, nbuckets; + CFMutableStringRef result; + cb = __CFSetGetCallBacks(set); + buckets = set->_buckets; + nbuckets = set->_bucketsNum; + result = CFStringCreateMutable(kCFAllocatorSystemDefault, 0); + CFStringAppendFormat(result, NULL, CFSTR("{count = %u, capacity = %u, values = (\n"), set, CFGetAllocator(set), set->_count, set->_capacity); + for (idx = 0; idx < nbuckets; idx++) { + if (__CFSetBucketIsOccupied(set, &buckets[idx])) { + CFStringRef desc = NULL; + if (NULL != cb->copyDescription) { + desc = (CFStringRef)INVOKE_CALLBACK2(((CFStringRef (*)(const void *, void *))cb->copyDescription), buckets[idx]._key, set->_context); + } + if (NULL != desc) { + CFStringAppendFormat(result, NULL, CFSTR("\t%u : %@\n"), idx, desc, NULL); + CFRelease(desc); + } else { + CFStringAppendFormat(result, NULL, CFSTR("\t%u : <%p>\n"), idx, buckets[idx]._key, NULL); + } + } + } + CFStringAppend(result, CFSTR(")}")); + return result; +} + +static void __CFSetDeallocate(CFTypeRef cf) { + CFMutableSetRef set = (CFMutableSetRef)cf; + CFAllocatorRef allocator = __CFGetAllocator(set); + if (__CFSetGetType(set) == __kCFSetImmutable) { + __CFBitfieldSetValue(((CFRuntimeBase *)set)->_info, 1, 0, __kCFSetFixedMutable); + } + CFSetRemoveAllValues(set); + if (__CFSetGetType(set) == __kCFSetMutable && set->_buckets) { + CFAllocatorDeallocate(allocator, set->_buckets); + } +} + +static CFTypeID __kCFSetTypeID = _kCFRuntimeNotATypeID; + +static const CFRuntimeClass __CFSetClass = { + 0, + "CFSet", + NULL, // init + NULL, // copy + __CFSetDeallocate, + (void *)__CFSetEqual, + __CFSetHash, + NULL, // + __CFSetCopyDescription +}; + +__private_extern__ void __CFSetInitialize(void) { + __kCFSetTypeID = _CFRuntimeRegisterClass(&__CFSetClass); +} + +CFTypeID CFSetGetTypeID(void) { + return __kCFSetTypeID; +} + +static CFSetRef __CFSetInit(CFAllocatorRef allocator, UInt32 flags, CFIndex capacity, const CFSetCallBacks *callBacks) { + struct __CFSet *memory; + UInt32 size; + CFIndex idx; + __CFBitfieldSetValue(flags, 31, 2, 0); + if (__CFSetCallBacksMatchNull(callBacks)) { + __CFBitfieldSetValue(flags, 3, 2, __kCFSetHasNullCallBacks); + } else if (__CFSetCallBacksMatchCFType(callBacks)) { + __CFBitfieldSetValue(flags, 3, 2, __kCFSetHasCFTypeCallBacks); + } else { + __CFBitfieldSetValue(flags, 3, 2, __kCFSetHasCustomCallBacks); + } + size = __CFSetGetSizeOfType(flags) - sizeof(CFRuntimeBase); + switch (__CFBitfieldGetValue(flags, 1, 0)) { + case __kCFSetImmutable: + case __kCFSetFixedMutable: + size += __CFSetNumBucketsForCapacity(capacity) * sizeof(struct __CFSetBucket); + break; + case __kCFSetMutable: + break; + } + memory = (struct __CFSet *)_CFRuntimeCreateInstance(allocator, __kCFSetTypeID, size, NULL); + if (NULL == memory) { + return NULL; + } + __CFBitfieldSetValue(memory->_base._info, 6, 0, flags); + memory->_count = 0; + memory->_bucketsUsed = 0; + memory->_emptyMarker = (const void *)0xa1b1c1d3; + memory->_deletedMarker = (const void *)0xa1b1c1d5; + memory->_context = NULL; + switch (__CFBitfieldGetValue(flags, 1, 0)) { + case __kCFSetImmutable: + if (__CFOASafe) __CFSetLastAllocationEventName(memory, "CFSet (immutable)"); + memory->_capacity = capacity; /* Don't round up capacity */ + memory->_bucketsNum = __CFSetNumBucketsForCapacity(memory->_capacity); + memory->_buckets = (struct __CFSetBucket *)((uint8_t *)memory + __CFSetGetSizeOfType(flags)); + for (idx = memory->_bucketsNum; idx--;) { + memory->_buckets[idx]._key = memory->_emptyMarker; + } + break; + case __kCFSetFixedMutable: + if (__CFOASafe) __CFSetLastAllocationEventName(memory, "CFSet (mutable-fixed)"); + memory->_capacity = capacity; /* Don't round up capacity */ + memory->_bucketsNum = __CFSetNumBucketsForCapacity(memory->_capacity); + memory->_buckets = (struct __CFSetBucket *)((uint8_t *)memory + __CFSetGetSizeOfType(flags)); + for (idx = memory->_bucketsNum; idx--;) { + memory->_buckets[idx]._key = memory->_emptyMarker; + } + break; + case __kCFSetMutable: + if (__CFOASafe) __CFSetLastAllocationEventName(memory, "CFSet (mutable-variable)"); + memory->_capacity = __CFSetRoundUpCapacity(1); + memory->_bucketsNum = 0; + memory->_buckets = NULL; + break; + } + if (__kCFSetHasCustomCallBacks == __CFBitfieldGetValue(flags, 3, 2)) { + const CFSetCallBacks *cb = __CFSetGetCallBacks((CFSetRef)memory); + *(CFSetCallBacks *)cb = *callBacks; + 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)); + } + return (CFSetRef)memory; +} + +CFSetRef CFSetCreate(CFAllocatorRef allocator, const void **values, CFIndex numValues, const CFSetCallBacks *callBacks) { + CFSetRef result; + UInt32 flags; + CFIndex idx; + CFAssert2(0 <= numValues, __kCFLogAssertion, "%s(): numValues (%d) cannot be less than zero", __PRETTY_FUNCTION__, numValues); + result = __CFSetInit(allocator, __kCFSetImmutable, numValues, callBacks); + flags = __CFBitfieldGetValue(((const CFRuntimeBase *)result)->_info, 1, 0); + if (flags == __kCFSetImmutable) { + __CFBitfieldSetValue(((CFRuntimeBase *)result)->_info, 1, 0, __kCFSetFixedMutable); + } + for (idx = 0; idx < numValues; idx++) { + CFSetAddValue((CFMutableSetRef)result, values[idx]); + } + __CFBitfieldSetValue(((CFRuntimeBase *)result)->_info, 1, 0, flags); + return result; +} + +CFMutableSetRef CFSetCreateMutable(CFAllocatorRef allocator, CFIndex capacity, const CFSetCallBacks *callBacks) { + CFAssert2(0 <= capacity, __kCFLogAssertion, "%s(): capacity (%d) cannot be less than zero", __PRETTY_FUNCTION__, capacity); + return (CFMutableSetRef)__CFSetInit(allocator, (0 == capacity) ? __kCFSetMutable : __kCFSetFixedMutable, capacity, callBacks); +} + +CFSetRef CFSetCreateCopy(CFAllocatorRef allocator, CFSetRef set) { + CFSetRef result; + const CFSetCallBacks *cb; + CFIndex numValues = CFSetGetCount(set); + const void **list, *buffer[256]; + list = (numValues <= 256) ? buffer : CFAllocatorAllocate(allocator, numValues * sizeof(void *), 0); + if (list != buffer && __CFOASafe) __CFSetLastAllocationEventName(list, "CFSet (temp)"); + CFSetGetValues(set, list); + cb = CF_IS_OBJC(__kCFSetTypeID, set) ? &kCFTypeSetCallBacks : __CFSetGetCallBacks(set); + result = CFSetCreate(allocator, list, numValues, cb); + if (list != buffer) CFAllocatorDeallocate(allocator, list); + return result; +} + +CFMutableSetRef CFSetCreateMutableCopy(CFAllocatorRef allocator, CFIndex capacity, CFSetRef set) { + CFMutableSetRef result; + const CFSetCallBacks *cb; + CFIndex idx, numValues = CFSetGetCount(set); + const void **list, *buffer[256]; + CFAssert3(0 == capacity || numValues <= capacity, __kCFLogAssertion, "%s(): for fixed-mutable sets, capacity (%d) must be greater than or equal to initial number of values (%d)", __PRETTY_FUNCTION__, capacity, numValues); + list = (numValues <= 256) ? buffer : CFAllocatorAllocate(allocator, numValues * sizeof(void *), 0); + if (list != buffer && __CFOASafe) __CFSetLastAllocationEventName(list, "CFSet (temp)"); + CFSetGetValues(set, list); + cb = CF_IS_OBJC(__kCFSetTypeID, set) ? &kCFTypeSetCallBacks : __CFSetGetCallBacks(set); + result = CFSetCreateMutable(allocator, capacity, cb); + if (0 == capacity) _CFSetSetCapacity(result, numValues); + for (idx = 0; idx < numValues; idx++) { + CFSetAddValue(result, list[idx]); + } + if (list != buffer) CFAllocatorDeallocate(allocator, list); + return result; +} + +void _CFSetSetContext(CFSetRef set, void *context) { + ((struct __CFSet *)set)->_context = context; +} + +CFIndex CFSetGetCount(CFSetRef set) { + CF_OBJC_FUNCDISPATCH0(__kCFSetTypeID, CFIndex, set, "count"); + __CFGenericValidateType(set, __kCFSetTypeID); + return set->_count; +} + +CFIndex CFSetGetCountOfValue(CFSetRef set, const void *value) { + struct __CFSetBucket *match; + CF_OBJC_FUNCDISPATCH1(__kCFSetTypeID, CFIndex, set, "countForObject:", value); + __CFGenericValidateType(set, __kCFSetTypeID); + if (0 == set->_count) return 0; + __CFSetFindBuckets1(set, value, &match); + return (match ? 1 : 0); +} + +Boolean CFSetContainsValue(CFSetRef set, const void *value) { + struct __CFSetBucket *match; + CF_OBJC_FUNCDISPATCH1(__kCFSetTypeID, char, set, "containsObject:", value); + __CFGenericValidateType(set, __kCFSetTypeID); + if (0 == set->_count) return false; + __CFSetFindBuckets1(set, value, &match); + return (match ? true : false); +} + +const void *CFSetGetValue(CFSetRef set, const void *value) { + struct __CFSetBucket *match; + CF_OBJC_FUNCDISPATCH1(__kCFSetTypeID, const void *, set, "member:", value); + __CFGenericValidateType(set, __kCFSetTypeID); + if (0 == set->_count) return NULL; + __CFSetFindBuckets1(set, value, &match); + return (match ? match->_key : NULL); +} + +Boolean CFSetGetValueIfPresent(CFSetRef set, const void *candidate, const void **value) { + struct __CFSetBucket *match; + CF_OBJC_FUNCDISPATCH2(__kCFSetTypeID, char, set, "_getValue:forObj:", (void * *)value, candidate); + __CFGenericValidateType(set, __kCFSetTypeID); + if (0 == set->_count) return false; + __CFSetFindBuckets1(set, candidate, &match); + return (match ? ((value ? *value = match->_key : NULL), true) : false); +} + +void CFSetGetValues(CFSetRef set, const void **values) { + struct __CFSetBucket *buckets; + CFIndex idx, cnt, nbuckets; + CF_OBJC_FUNCDISPATCH1(__kCFSetTypeID, void, set, "getObjects:", (void * *)values); + __CFGenericValidateType(set, __kCFSetTypeID); + buckets = set->_buckets; + nbuckets = set->_bucketsNum; + for (idx = 0; idx < nbuckets; idx++) { + if (__CFSetBucketIsOccupied(set, &buckets[idx])) { + for (cnt = 1; cnt--;) { + if (values) *values++ = buckets[idx]._key; + } + } + } +} + +void CFSetApplyFunction(CFSetRef set, CFSetApplierFunction applier, void *context) { + struct __CFSetBucket *buckets; + CFIndex idx, cnt, nbuckets; + FAULT_CALLBACK((void **)&(applier)); + CF_OBJC_FUNCDISPATCH2(__kCFSetTypeID, void, set, "_applyValues:context:", applier, context); + __CFGenericValidateType(set, __kCFSetTypeID); + buckets = set->_buckets; + nbuckets = set->_bucketsNum; + for (idx = 0; idx < nbuckets; idx++) { + if (__CFSetBucketIsOccupied(set, &buckets[idx])) { + for (cnt = 1; cnt--;) { + INVOKE_CALLBACK2(applier, buckets[idx]._key, context); + } + } + } +} + +static void __CFSetGrow(CFMutableSetRef set, CFIndex numNewValues) { + struct __CFSetBucket *oldbuckets = set->_buckets; + CFIndex idx, oldnbuckets = set->_bucketsNum; + CFIndex oldCount = set->_count; + set->_capacity = __CFSetRoundUpCapacity(oldCount + numNewValues); + set->_bucketsNum = __CFSetNumBucketsForCapacity(set->_capacity); + set->_buckets = CFAllocatorAllocate(__CFGetAllocator(set), set->_bucketsNum * sizeof(struct __CFSetBucket), 0); + if (NULL == set->_buckets) HALT; + if (__CFOASafe) __CFSetLastAllocationEventName(set->_buckets, "CFSet (store)"); + for (idx = set->_bucketsNum; idx--;) { + set->_buckets[idx]._key = set->_emptyMarker; + } + if (NULL == oldbuckets) return; + for (idx = 0; idx < oldnbuckets; idx++) { + if (__CFSetBucketIsOccupied(set, &oldbuckets[idx])) { + struct __CFSetBucket *match, *nomatch; + __CFSetFindBuckets2(set, oldbuckets[idx]._key, &match, &nomatch); + CFAssert3(!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__, oldbuckets[idx]._key, match->_key); + nomatch->_key = oldbuckets[idx]._key; + } + } + CFAssert1(set->_count == oldCount, __kCFLogAssertion, "%s(): set count differs after rehashing; error", __PRETTY_FUNCTION__); + CFAllocatorDeallocate(__CFGetAllocator(set), oldbuckets); +} + +// This function is for Foundation's benefit; no one else should use it. +void _CFSetSetCapacity(CFMutableSetRef set, CFIndex cap) { + if (CF_IS_OBJC(__kCFSetTypeID, set)) return; +#if defined(DEBUG) + __CFGenericValidateType(set, __kCFSetTypeID); + CFAssert1(__CFSetGetType(set) != __kCFSetImmutable && __CFSetGetType(set) != __kCFSetFixedMutable, __kCFLogAssertion, "%s(): set is immutable or fixed-mutable", __PRETTY_FUNCTION__); + CFAssert3(set->_count <= cap, __kCFLogAssertion, "%s(): desired capacity (%d) is less than count (%d)", __PRETTY_FUNCTION__, cap, set->_count); +#endif + __CFSetGrow(set, cap - set->_count); +} + +// This function is for Foundation's benefit; no one else should use it. +bool _CFSetIsMutable(CFSetRef set) { + return (__CFSetGetType(set) != __kCFSetImmutable); +} + +void CFSetAddValue(CFMutableSetRef set, const void *value) { + struct __CFSetBucket *match, *nomatch; + const CFSetCallBacks *cb; + const void *newValue; + CF_OBJC_FUNCDISPATCH1(__kCFSetTypeID, void, set, "addObject:", value); + __CFGenericValidateType(set, __kCFSetTypeID); + switch (__CFSetGetType(set)) { + case __kCFSetMutable: + if (set->_bucketsUsed == set->_capacity || NULL == set->_buckets) { + __CFSetGrow(set, 1); + } + break; + case __kCFSetFixedMutable: + CFAssert3(set->_count < set->_capacity, __kCFLogAssertion, "%s(): capacity exceeded on fixed-capacity set %p (capacity = %d)", __PRETTY_FUNCTION__, set, set->_capacity); + break; + default: + CFAssert2(__CFSetGetType(set) != __kCFSetImmutable, __kCFLogAssertion, "%s(): immutable set %p passed to mutating operation", __PRETTY_FUNCTION__, set); + break; + } + __CFSetFindBuckets2(set, value, &match, &nomatch); + if (match) { + } else { + cb = __CFSetGetCallBacks(set); + if (cb->retain) { + newValue = (void *)INVOKE_CALLBACK3(((const void *(*)(CFAllocatorRef, const void *, void *))cb->retain), __CFGetAllocator(set), value, set->_context); + } else { + newValue = value; + } + if (set->_emptyMarker == newValue) { + __CFSetFindNewEmptyMarker(set); + } + if (set->_deletedMarker == newValue) { + __CFSetFindNewDeletedMarker(set); + } + nomatch->_key = newValue; + set->_bucketsUsed++; + set->_count++; + } +} + +__private_extern__ const void *__CFSetAddValueAndReturn(CFMutableSetRef set, const void *value) { + struct __CFSetBucket *match, *nomatch; + const CFSetCallBacks *cb; + const void *newValue; +// #warning not toll-free bridged, but internal + __CFGenericValidateType(set, __kCFSetTypeID); + switch (__CFSetGetType(set)) { + case __kCFSetMutable: + if (set->_bucketsUsed == set->_capacity || NULL == set->_buckets) { + __CFSetGrow(set, 1); + } + break; + case __kCFSetFixedMutable: + CFAssert3(set->_count < set->_capacity, __kCFLogAssertion, "%s(): capacity exceeded on fixed-capacity set %p (capacity = %d)", __PRETTY_FUNCTION__, set, set->_capacity); + break; + default: + CFAssert2(__CFSetGetType(set) != __kCFSetImmutable, __kCFLogAssertion, "%s(): immutable set %p passed to mutating operation", __PRETTY_FUNCTION__, set); + break; + } + __CFSetFindBuckets2(set, value, &match, &nomatch); + if (match) { + return match->_key; + } else { + cb = __CFSetGetCallBacks(set); + if (cb->retain) { + newValue = (void *)INVOKE_CALLBACK3(((const void *(*)(CFAllocatorRef, const void *, void *))cb->retain), __CFGetAllocator(set), value, set->_context); + } else { + newValue = value; + } + if (set->_emptyMarker == newValue) { + __CFSetFindNewEmptyMarker(set); + } + if (set->_deletedMarker == newValue) { + __CFSetFindNewDeletedMarker(set); + } + nomatch->_key = newValue; + set->_bucketsUsed++; + set->_count++; + return newValue; + } +} + +void CFSetReplaceValue(CFMutableSetRef set, const void *value) { + struct __CFSetBucket *match; + const CFSetCallBacks *cb; + const void *newValue; + CF_OBJC_FUNCDISPATCH1(__kCFSetTypeID, void, set, "_replaceObject:", value); + __CFGenericValidateType(set, __kCFSetTypeID); + switch (__CFSetGetType(set)) { + case __kCFSetMutable: + case __kCFSetFixedMutable: + break; + default: + CFAssert2(__CFSetGetType(set) != __kCFSetImmutable, __kCFLogAssertion, "%s(): immutable set %p passed to mutating operation", __PRETTY_FUNCTION__, set); + break; + } + if (0 == set->_count) return; + __CFSetFindBuckets1(set, value, &match); + if (!match) return; + cb = __CFSetGetCallBacks(set); + if (cb->retain) { + newValue = (void *)INVOKE_CALLBACK3(((const void *(*)(CFAllocatorRef, const void *, void *))cb->retain), __CFGetAllocator(set), value, set->_context); + } else { + newValue = value; + } + if (cb->release) { + INVOKE_CALLBACK3(((void (*)(CFAllocatorRef, const void *, void *))cb->release), __CFGetAllocator(set), match->_key, set->_context); + match->_key = set->_deletedMarker; + } + if (set->_emptyMarker == newValue) { + __CFSetFindNewEmptyMarker(set); + } + if (set->_deletedMarker == newValue) { + __CFSetFindNewDeletedMarker(set); + } + match->_key = newValue; +} + +void CFSetSetValue(CFMutableSetRef set, const void *value) { + struct __CFSetBucket *match, *nomatch; + const CFSetCallBacks *cb; + const void *newValue; + CF_OBJC_FUNCDISPATCH1(__kCFSetTypeID, void, set, "_setObject:", value); + __CFGenericValidateType(set, __kCFSetTypeID); + switch (__CFSetGetType(set)) { + case __kCFSetMutable: + if (set->_bucketsUsed == set->_capacity || NULL == set->_buckets) { + __CFSetGrow(set, 1); + } + break; + case __kCFSetFixedMutable: + break; + default: + CFAssert2(__CFSetGetType(set) != __kCFSetImmutable, __kCFLogAssertion, "%s(): immutable set %p passed to mutating operation", __PRETTY_FUNCTION__, set); + break; + } + __CFSetFindBuckets2(set, value, &match, &nomatch); + cb = __CFSetGetCallBacks(set); + if (cb->retain) { + newValue = (void *)INVOKE_CALLBACK3(((const void *(*)(CFAllocatorRef, const void *, void *))cb->retain), __CFGetAllocator(set), value, set->_context); + } else { + newValue = value; + } + if (match) { + if (cb->release) { + INVOKE_CALLBACK3(((void (*)(CFAllocatorRef, const void *, void *))cb->release), __CFGetAllocator(set), match->_key, set->_context); + match->_key = set->_deletedMarker; + } + if (set->_emptyMarker == newValue) { + __CFSetFindNewEmptyMarker(set); + } + if (set->_deletedMarker == newValue) { + __CFSetFindNewDeletedMarker(set); + } + match->_key = newValue; + } else { + CFAssert3(__kCFSetFixedMutable != __CFSetGetType(set) || set->_count < set->_capacity, __kCFLogAssertion, "%s(): capacity exceeded on fixed-capacity set %p (capacity = %d)", __PRETTY_FUNCTION__, set, set->_capacity); + if (set->_emptyMarker == newValue) { + __CFSetFindNewEmptyMarker(set); + } + if (set->_deletedMarker == newValue) { + __CFSetFindNewDeletedMarker(set); + } + nomatch->_key = newValue; + set->_bucketsUsed++; + set->_count++; + } +} + +void CFSetRemoveValue(CFMutableSetRef set, const void *value) { + struct __CFSetBucket *match; + const CFSetCallBacks *cb; + CF_OBJC_FUNCDISPATCH1(__kCFSetTypeID, void, set, "removeObject:", value); + __CFGenericValidateType(set, __kCFSetTypeID); + switch (__CFSetGetType(set)) { + case __kCFSetMutable: + case __kCFSetFixedMutable: + break; + default: + CFAssert2(__CFSetGetType(set) != __kCFSetImmutable, __kCFLogAssertion, "%s(): immutable set %p passed to mutating operation", __PRETTY_FUNCTION__, set); + break; + } + if (0 == set->_count) return; + __CFSetFindBuckets1(set, value, &match); + if (!match) return; + set->_count--; + if (1) { + cb = __CFSetGetCallBacks(set); + if (cb->release) { + INVOKE_CALLBACK3(((void (*)(CFAllocatorRef, const void *, void *))cb->release), __CFGetAllocator(set), match->_key, set->_context); + } + match->_key = set->_deletedMarker; + set->_bucketsUsed--; + } +} + +void CFSetRemoveAllValues(CFMutableSetRef set) { + struct __CFSetBucket *buckets; + const CFSetCallBacks *cb; + CFAllocatorRef allocator; + CFIndex idx, nbuckets; + CF_OBJC_FUNCDISPATCH0(__kCFSetTypeID, void, set, "removeAllObjects"); + __CFGenericValidateType(set, __kCFSetTypeID); + switch (__CFSetGetType(set)) { + case __kCFSetMutable: + case __kCFSetFixedMutable: + break; + default: + CFAssert2(__CFSetGetType(set) != __kCFSetImmutable, __kCFLogAssertion, "%s(): immutable set %p passed to mutating operation", __PRETTY_FUNCTION__, set); + break; + } + if (0 == set->_count) return; + buckets = set->_buckets; + nbuckets = set->_bucketsNum; + cb = __CFSetGetCallBacks(set); + allocator = __CFGetAllocator(set); + for (idx = 0; idx < nbuckets; idx++) { + if (__CFSetBucketIsOccupied(set, &buckets[idx])) { + if (cb->release) { + INVOKE_CALLBACK3(((void (*)(CFAllocatorRef, const void *, void *))cb->release), allocator, buckets[idx]._key, set->_context); + } + buckets[idx]._key = set->_emptyMarker; + } + } + set->_bucketsUsed = 0; + set->_count = 0; +} + diff --git a/Collections.subproj/CFSet.h b/Collections.subproj/CFSet.h new file mode 100644 index 0000000..260cdad --- /dev/null +++ b/Collections.subproj/CFSet.h @@ -0,0 +1,513 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFSet.h + Copyright (c) 1998-2003, Apple, Inc. All rights reserved. +*/ +/*! + @header CFSet + CFSet implements a container which stores unique values. +*/ + +#if !defined(__COREFOUNDATION_CFSET__) +#define __COREFOUNDATION_CFSET__ 1 + +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +/*! + @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 The maximum number of values that can be contained + by the CFSet. The set starts empty, and can grow to this + number of values (and it can have less). If this parameter + is 0, the set's maximum capacity is unlimited (or rather, + 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 The maximum number of values that can be contained + by the CFSet. The set starts with the same values as the + set to be copied, and can grow to this number of values. + If this parameter is 0, the set's maximum capacity is + unlimited (or rather, 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. + @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. If the set is a fixed-capacity set and it + is full before this operation, 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); + +#if defined(__cplusplus) +} +#endif + +#endif /* ! __COREFOUNDATION_CFSET__ */ + diff --git a/Collections.subproj/CFStorage.c b/Collections.subproj/CFStorage.c new file mode 100644 index 0000000..fb1880f --- /dev/null +++ b/Collections.subproj/CFStorage.c @@ -0,0 +1,597 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFStorage.c + Copyright 1999-2002, 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 +*/ + +#include "CFStorage.h" +#include "CFInternal.h" + +#if defined(__MACH__) +#include +#else +enum { + vm_page_size = 4096 +}; +#endif + +enum { + __CFStorageMaxLeafCapacity = 65536 +}; + +#define COPYMEM(src,dst,n) memmove((dst), (src), (n)) +#define PAGE_LIMIT ((CFIndex)vm_page_size / 2) + +static int roundToPage(int num) { + return (num + vm_page_size - 1) & ~(vm_page_size - 1); +} + +typedef struct __CFStorageNode { + CFIndex numBytes; /* Number of actual bytes in this node and all its children */ + bool isLeaf; + union { + struct { + CFIndex capacityInBytes; + uint8_t *memory; + } leaf; + struct { + struct __CFStorageNode *child[3]; + } notLeaf; + } info; +} CFStorageNode; + +struct __CFStorage { + CFRuntimeBase base; + CFIndex valueSize; + CFRange cachedRange; + uint8_t *cachedNodeMemory; + CFIndex maxLeafCapacity; + CFStorageNode rootNode; +}; + +/* 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 returns the relative byte number of the specified byte in the child +*/ +static void __CFStorageFindChild(CFStorageNode *node, CFIndex byteNum, bool forInsertion, CFIndex *childNum, CFIndex *relativeByteNum) { + if (node->isLeaf) *childNum = 0; + else { + 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++; + } + if (relativeByteNum) *relativeByteNum = byteNum; +} + +/* Allocates the memory and initializes the capacity in a leaf +*/ +static void __CFStorageAllocLeafNodeMemory(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)) { + node->info.leaf.memory = CFAllocatorReallocate(CFGetAllocator(storage), node->info.leaf.memory, cap, 0); // This will free... ??? Use allocator + if (__CFOASafe) __CFSetLastAllocationEventName(node->info.leaf.memory, "CFStorage (node bytes)"); + node->info.leaf.capacityInBytes = cap; + } +} + +static CFStorageNode *__CFStorageCreateNode(CFStorageRef storage, bool isLeaf, CFIndex numBytes) { + CFStorageNode *newNode = CFAllocatorAllocate(CFGetAllocator(storage), sizeof(CFStorageNode), 0); + 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; +} + +/* 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, CFRange *validConsecutiveByteRange) { + if (node->isLeaf) { + if (validConsecutiveByteRange) *validConsecutiveByteRange = CFRangeMake(0, node->numBytes); + __CFStorageAllocLeafNodeMemory(storage, node, node->numBytes, false); + 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, 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; + } +} + +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(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(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(storage, node->info.notLeaf.child[childNum], rangeToDelete, compact); + if (node->info.notLeaf.child[childNum]->numBytes == 0) { // Delete empty node and compact + int cnt; + CFAllocatorDeallocate(CFGetAllocator(storage), node->info.notLeaf.child[childNum]); + for (cnt = childNum; cnt < 2; cnt++) { + 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(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); + CFAllocatorDeallocate(CFGetAllocator(storage), node->info.notLeaf.child[2]); + node->info.notLeaf.child[2] = NULL; + } + CFAllocatorDeallocate(CFGetAllocator(storage), node->info.notLeaf.child[1]); + 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]) node->info.notLeaf.child[cCnt] = __CFStorageCreateNode(storage, false, 0); + for (cnt = 0; cnt < sCnt; cnt++) { + node->info.notLeaf.child[cCnt]->numBytes += gChildren[gCnt]->numBytes; + node->info.notLeaf.child[cCnt]->info.notLeaf.child[cnt] = gChildren[gCnt++]; + } + } else { + if (node->info.notLeaf.child[cCnt]) { + CFAllocatorDeallocate(CFGetAllocator(storage), 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(CFStorageRef storage, CFStorageNode *node, CFIndex byteNum, CFIndex size) { + 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(storage, true, size); + return newNode; + } else if (byteNum == 0) { // Inserting at front; also easy, but we need to swap node and newNode + CFStorageNode *newNode = __CFStorageCreateNode(storage, true, 0); + *newNode = *node; + node->isLeaf = true; + node->numBytes = size; + node->info.leaf.capacityInBytes = 0; + node->info.leaf.memory = NULL; + 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(storage, true, node->numBytes - byteNum); + if (node->info.leaf.memory) { // We allocate memory lazily... + __CFStorageAllocLeafNodeMemory(storage, newNode, node->numBytes - byteNum, false); + COPYMEM(node->info.leaf.memory + byteNum, newNode->info.leaf.memory, node->numBytes - byteNum); + __CFStorageAllocLeafNodeMemory(storage, node, byteNum + size, false); + } + node->numBytes += size - (node->numBytes - byteNum); + return newNode; + } else { // Inserting some of new into one node, rest into another; remember that the assumption is size <= storage->maxLeafCapacity + CFStorageNode *newNode = __CFStorageCreateNode(storage, true, node->numBytes + size - storage->maxLeafCapacity); // New stuff + if (node->info.leaf.memory) { // We allocate memory lazily... + __CFStorageAllocLeafNodeMemory(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); + } + node->numBytes = storage->maxLeafCapacity; + return newNode; + } + } else { // No need to create new nodes! + if (node->info.leaf.memory) { + __CFStorageAllocLeafNodeMemory(storage, node, node->numBytes + size, false); + COPYMEM(node->info.leaf.memory + byteNum, node->info.leaf.memory + byteNum + size, node->numBytes - byteNum); + } + node->numBytes += size; + return NULL; + } + } else { + CFIndex relativeByteNum; + CFIndex childNum; + CFStorageNode *newNode; + __CFStorageFindChild(node, byteNum, true, &childNum, &relativeByteNum); + newNode = __CFStorageInsert(storage, node->info.notLeaf.child[childNum], relativeByteNum, size); + if (newNode) { + if (node->info.notLeaf.child[2] == NULL) { // There's an empty slot for the new node, cool + if (childNum == 0) node->info.notLeaf.child[2] = node->info.notLeaf.child[1]; // Make room + node->info.notLeaf.child[childNum + 1] = newNode; + node->numBytes += size; + return NULL; + } else { + CFStorageNode *anotherNode = __CFStorageCreateNode(storage, false, 0); // Create another node + if (childNum == 0) { // Last two children go to new node + anotherNode->info.notLeaf.child[0] = node->info.notLeaf.child[1]; + anotherNode->info.notLeaf.child[1] = node->info.notLeaf.child[2]; + node->info.notLeaf.child[1] = newNode; + node->info.notLeaf.child[2] = NULL; + } else if (childNum == 1) { // Last child goes to new node + anotherNode->info.notLeaf.child[0] = newNode; + anotherNode->info.notLeaf.child[1] = node->info.notLeaf.child[2]; + node->info.notLeaf.child[2] = NULL; + } else { // New node contains the new comers... + anotherNode->info.notLeaf.child[0] = node->info.notLeaf.child[2]; + 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 bool __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 = CFStorageGetValueAtIndex(storage1, loc, &range1); + if (loc >= range2.location + range2.length) ptr2 = 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; + result = CFStringCreateMutable(CFGetAllocator(storage), 0); + CFStringAppendFormat(result, NULL, CFSTR("[count = %u, capacity = %u]\n"), storage, CFGetAllocator(storage), __CFStorageGetCount(storage), __CFStorageGetCapacity(storage)); + __CFStorageDescribeNode(&storage->rootNode, result, 0); + return result; +} + +static void __CFStorageNodeDealloc(CFAllocatorRef allocator, CFStorageNode *node, bool freeNodeItself) { + if (node->isLeaf) { + CFAllocatorDeallocate(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) CFAllocatorDeallocate(allocator, node); +} + +static void __CFStorageDeallocate(CFTypeRef cf) { + CFStorageRef storage = (CFStorageRef)cf; + CFAllocatorRef allocator = CFGetAllocator(storage); + __CFStorageNodeDealloc(allocator, &storage->rootNode, false); +} + +static CFTypeID __kCFStorageTypeID = _kCFRuntimeNotATypeID; + +static const CFRuntimeClass __CFStorageClass = { + 0, + "CFStorage", + NULL, // init + NULL, // copy + __CFStorageDeallocate, + (void *)__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; + storage->cachedRange.location = 0; + storage->cachedRange.length = 0; + storage->cachedNodeMemory = NULL; + 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; + 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 + The node cache in the storage base is also in terms of values +*/ +void *CFStorageGetValueAtIndex(CFStorageRef storage, CFIndex idx, CFRange *validConsecutiveValueRange) { + uint8_t *result; + if (idx < storage->cachedRange.location + storage->cachedRange.length && idx >= storage->cachedRange.location) { + result = storage->cachedNodeMemory + (idx - storage->cachedRange.location) * storage->valueSize; + } else { + CFRange range; + result = __CFStorageFindByte(storage, &storage->rootNode, idx * storage->valueSize, &range); + storage->cachedRange.location = range.location / storage->valueSize; // range is range of bytes; convert that to values + storage->cachedRange.length = range.length / storage->valueSize; + storage->cachedNodeMemory = result - (idx - storage->cachedRange.location) * storage->valueSize; + } + if (validConsecutiveValueRange) *validConsecutiveValueRange = storage->cachedRange; + return result; +} + +/* 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; + CFIndex insertThisTime = numBytesToInsert; + if (insertThisTime > storage->maxLeafCapacity) { + insertThisTime = (storage->maxLeafCapacity / storage->valueSize) * storage->valueSize; + } + newNode = __CFStorageInsert(storage, &storage->rootNode, byteNum, insertThisTime); + if (newNode) { + CFStorageNode *tempRootNode = __CFStorageCreateNode(storage, false, 0); // Will copy the (static) rootNode over to this + *tempRootNode = storage->rootNode; + storage->rootNode.isLeaf = false; + storage->rootNode.info.notLeaf.child[0] = tempRootNode; + storage->rootNode.info.notLeaf.child[1] = newNode; + storage->rootNode.info.notLeaf.child[2] = NULL; + storage->rootNode.numBytes = tempRootNode->numBytes + newNode->numBytes; + } + numBytesToInsert -= insertThisTime; + byteNum += insertThisTime; + } + // ??? Need to update the cache + storage->cachedNodeMemory = NULL; + storage->cachedRange = CFRangeMake(0, 0); +} + +/* Deletes the values in the specified range + This function gets rid of levels if necessary... +*/ +void CFStorageDeleteValues(CFStorageRef storage, CFRange range) { + range.location *= storage->valueSize; + range.length *= storage->valueSize; + __CFStorageDelete(storage, &storage->rootNode, range, true); + while (__CFStorageGetNumChildren(&storage->rootNode) == 1) { + CFStorageNode *child = storage->rootNode.info.notLeaf.child[0]; // The single child + storage->rootNode = *child; + CFAllocatorDeallocate(CFGetAllocator(storage), 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 + storage->cachedNodeMemory = NULL; + storage->cachedRange = CFRangeMake(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); + ((uint8_t *)values) += cntThisTime * storage->valueSize; + range.location += cntThisTime; + range.length -= cntThisTime; + } +} + +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); + ((const uint8_t *)values) += cntThisTime * storage->valueSize; + range.location += cntThisTime; + range.length -= cntThisTime; + } +} + +#undef COPYMEM +#undef PAGE_LIMIT + diff --git a/Collections.subproj/CFStorage.h b/Collections.subproj/CFStorage.h new file mode 100644 index 0000000..e0e9069 --- /dev/null +++ b/Collections.subproj/CFStorage.h @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFStorage.h + Copyright (c) 1999-2003, 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 + +#if defined(__cplusplus) +extern "C" { +#endif + +/*! + @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); + + +#if defined(__cplusplus) +} +#endif + +#endif /* ! __COREFOUNDATION_CFSTORAGE__ */ + diff --git a/Collections.subproj/CFTree.c b/Collections.subproj/CFTree.c new file mode 100644 index 0000000..b6edeaf --- /dev/null +++ b/Collections.subproj/CFTree.c @@ -0,0 +1,440 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFTree.c + Copyright 1998-2002, Apple, Inc. All rights reserved. + Responsibility: Christopher Kane +*/ + +#include +#include "CFInternal.h" +#include "CFUtilities.h" + +struct __CFTreeCallBacks { + CFTreeRetainCallBack retain; + CFTreeReleaseCallBack release; + CFTreeCopyDescriptionCallBack copyDescription; +}; + +struct __CFTree { + CFRuntimeBase _base; + CFTreeRef _parent; /* Not retained */ + CFTreeRef _sibling; /* Not retained */ + CFTreeRef _child; /* All children get a retain from the parent */ + /* This is the context, exploded. + * Currently the only valid version is 0, so we do not store that. + * _callbacks initialized if not a special form. */ + void *_info; + struct __CFTreeCallBacks *_callbacks; +}; + +static const struct __CFTreeCallBacks __kCFTypeTreeCallBacks = {CFRetain, CFRelease, CFCopyDescription}; +static const struct __CFTreeCallBacks __kCFNullTreeCallBacks = {NULL, NULL, NULL}; + +enum { /* Bits 0-1 */ + __kCFTreeHasNullCallBacks = 0, + __kCFTreeHasCFTypeCallBacks = 1, + __kCFTreeHasCustomCallBacks = 3 /* callbacks pointed to by _callbacks */ +}; + +CF_INLINE uint32_t __CFTreeGetCallBacksType(CFTreeRef tree) { + return (__CFBitfieldGetValue(tree->_base._info, 1, 0)); +} + +CF_INLINE const struct __CFTreeCallBacks *__CFTreeGetCallBacks(CFTreeRef tree) { + switch (__CFTreeGetCallBacksType(tree)) { + case __kCFTreeHasNullCallBacks: + return &__kCFNullTreeCallBacks; + case __kCFTreeHasCFTypeCallBacks: + return &__kCFTypeTreeCallBacks; + case __kCFTreeHasCustomCallBacks: + break; + } + return tree->_callbacks; +} + +CF_INLINE bool __CFTreeCallBacksMatchNull(const CFTreeContext *c) { + return (NULL == c || (c->retain == NULL && c->release == NULL && c->copyDescription == NULL)); +} + +CF_INLINE bool __CFTreeCallBacksMatchCFType(const CFTreeContext *c) { + return (NULL != c && (c->retain == CFRetain && c->release == CFRelease && c->copyDescription == CFCopyDescription)); +} + +static CFStringRef __CFTreeCopyDescription(CFTypeRef cf) { + CFTreeRef tree = (CFTreeRef)cf; + CFMutableStringRef result; + CFStringRef contextDesc = NULL; + const struct __CFTreeCallBacks *cb; + CFAllocatorRef allocator; + allocator = CFGetAllocator(tree); + result = CFStringCreateMutable(allocator, 0); + cb = __CFTreeGetCallBacks(tree); + if (NULL != cb->copyDescription) { + contextDesc = (CFStringRef)INVOKE_CALLBACK1(cb->copyDescription, tree->_info); + } + if (NULL == contextDesc) { + contextDesc = CFStringCreateWithFormat(allocator, NULL, CFSTR(""), tree->_info); + } + CFStringAppendFormat(result, NULL, CFSTR("{children = %u, context = %@}"), cf, allocator, CFTreeGetChildCount(tree), contextDesc); + return result; +} + +static void __CFTreeDeallocate(CFTypeRef cf) { + CFTreeRef tree = (CFTreeRef)cf; + const struct __CFTreeCallBacks *cb; + CFTreeRemoveAllChildren(tree); + cb = __CFTreeGetCallBacks(tree); + if (NULL != cb->release) { + INVOKE_CALLBACK1(cb->release, tree->_info); + } + if (__kCFTreeHasCustomCallBacks == __CFTreeGetCallBacksType(tree)) { + CFAllocatorDeallocate(CFGetAllocator(tree), tree->_callbacks); + } +} + +static CFTypeID __kCFTreeTypeID = _kCFRuntimeNotATypeID; + +static const CFRuntimeClass __CFTreeClass = { + 0, + "CFTree", + NULL, // init + NULL, // copy + __CFTreeDeallocate, + NULL, // equal + NULL, // hash + NULL, // + __CFTreeCopyDescription +}; + +__private_extern__ void __CFTreeInitialize(void) { + __kCFTreeTypeID = _CFRuntimeRegisterClass(&__CFTreeClass); +} + +CFTypeID CFTreeGetTypeID(void) { + return __kCFTreeTypeID; +} + +CFTreeRef CFTreeCreate(CFAllocatorRef allocator, const CFTreeContext *context) { + CFTreeRef memory; + uint32_t size; + + CFAssert1(NULL != context, __kCFLogAssertion, "%s(): pointer to context may not be NULL", __PRETTY_FUNCTION__); + CFAssert1(0 == context->version, __kCFLogAssertion, "%s(): context version not initialized to 0", __PRETTY_FUNCTION__); + size = sizeof(struct __CFTree) - sizeof(CFRuntimeBase); + memory = (CFTreeRef)_CFRuntimeCreateInstance(allocator, __kCFTreeTypeID, size, NULL); + if (NULL == memory) { + return NULL; + } + memory->_parent = NULL; + memory->_sibling = NULL; + memory->_child = NULL; + + /* Start the context off in a recognizable state */ + __CFBitfieldSetValue(memory->_base._info, 1, 0, __kCFTreeHasNullCallBacks); + CFTreeSetContext(memory, context); + return memory; +} + +CFIndex CFTreeGetChildCount(CFTreeRef tree) { + SInt32 cnt = 0; + __CFGenericValidateType(tree, __kCFTreeTypeID); + tree = tree->_child; + while (NULL != tree) { + cnt++; + tree = tree->_sibling; + } + return cnt; +} + +CFTreeRef CFTreeGetParent(CFTreeRef tree) { + __CFGenericValidateType(tree, __kCFTreeTypeID); + return tree->_parent; +} + +CFTreeRef CFTreeGetNextSibling(CFTreeRef tree) { + __CFGenericValidateType(tree, __kCFTreeTypeID); + return tree->_sibling; +} + +CFTreeRef CFTreeGetFirstChild(CFTreeRef tree) { + __CFGenericValidateType(tree, __kCFTreeTypeID); + return tree->_child; +} + +CFTreeRef CFTreeFindRoot(CFTreeRef tree) { + __CFGenericValidateType(tree, __kCFTreeTypeID); + while (NULL != tree->_parent) { + tree = tree->_parent; + } + return tree; +} + +void CFTreeGetContext(CFTreeRef tree, CFTreeContext *context) { + const struct __CFTreeCallBacks *cb; + __CFGenericValidateType(tree, __kCFTreeTypeID); + CFAssert1(0 == context->version, __kCFLogAssertion, "%s(): context version not initialized to 0", __PRETTY_FUNCTION__); + cb = __CFTreeGetCallBacks(tree); + context->version = 0; + context->info = tree->_info; +#if defined(__ppc__) + context->retain = (void *)((uintptr_t)cb->retain & ~0x3); + context->release = (void *)((uintptr_t)cb->release & ~0x3); + context->copyDescription = (void *)((uintptr_t)cb->copyDescription & ~0x3); +#else + context->retain = cb->retain; + context->release = cb->release; + context->copyDescription = cb->copyDescription; +#endif +} + +void CFTreeSetContext(CFTreeRef tree, const CFTreeContext *context) { + uint32_t newtype, oldtype = __CFTreeGetCallBacksType(tree); + struct __CFTreeCallBacks *oldcb = (struct __CFTreeCallBacks *)__CFTreeGetCallBacks(tree); + struct __CFTreeCallBacks *newcb; + void *oldinfo = tree->_info; + + if (__CFTreeCallBacksMatchNull(context)) { + newtype = __kCFTreeHasNullCallBacks; + } else if (__CFTreeCallBacksMatchCFType(context)) { + newtype = __kCFTreeHasCFTypeCallBacks; + } else { + newtype = __kCFTreeHasCustomCallBacks; + tree->_callbacks = CFAllocatorAllocate(CFGetAllocator(tree), sizeof(struct __CFTreeCallBacks), 0); + if (__CFOASafe) __CFSetLastAllocationEventName(tree->_callbacks, "CFTree (callbacks)"); + tree->_callbacks->retain = context->retain; + tree->_callbacks->release = context->release; + tree->_callbacks->copyDescription = context->copyDescription; + FAULT_CALLBACK((void **)&(tree->_callbacks->retain)); + FAULT_CALLBACK((void **)&(tree->_callbacks->release)); + FAULT_CALLBACK((void **)&(tree->_callbacks->copyDescription)); + } + __CFBitfieldSetValue(tree->_base._info, 1, 0, newtype); + newcb = (struct __CFTreeCallBacks *)__CFTreeGetCallBacks(tree); + if (NULL != newcb->retain) { + tree->_info = (void *)INVOKE_CALLBACK1(newcb->retain, context->info); + } else { + tree->_info = context->info; + } + if (NULL != oldcb->release) { + INVOKE_CALLBACK1(oldcb->release, oldinfo); + } + if (oldtype == __kCFTreeHasCustomCallBacks) { + CFAllocatorDeallocate(CFGetAllocator(tree), oldcb); + } +} + +#if 0 +CFTreeRef CFTreeFindNextSibling(CFTreeRef tree, const void *info) { + __CFGenericValidateType(tree, __kCFTreeTypeID); + tree = tree->_sibling; + while (NULL != tree) { + if (info == tree->_context.info || (tree->_context.equal && tree->_context.equal(info, tree->_context.info))) { + return tree; + } + tree = tree->_sibling; + } + return NULL; +} + +CFTreeRef CFTreeFindFirstChild(CFTreeRef tree, const void *info) { + __CFGenericValidateType(tree, __kCFTreeTypeID); + tree = tree->_child; + while (NULL != tree) { + if (info == tree->_context.info || (tree->_context.equal && tree->_context.equal(info, tree->_context.info))) { + return tree; + } + tree = tree->_sibling; + } + return NULL; +} + +CFTreeRef CFTreeFind(CFTreeRef tree, const void *info) { + __CFGenericValidateType(tree, __kCFTreeTypeID); + if (info == tree->_context.info || (tree->_context.equal && tree->_context.equal(info, tree->info))) { + return tree; + } + tree = tree->_child; + while (NULL != tree) { + CFTreeRef found = CFTreeFind(tree, info); + if (NULL != found) { + return found; + } + tree = tree->_sibling; + } + return NULL; +} +#endif + +CFTreeRef CFTreeGetChildAtIndex(CFTreeRef tree, CFIndex idx) { + __CFGenericValidateType(tree, __kCFTreeTypeID); + tree = tree->_child; + while (NULL != tree) { + if (0 == idx) return tree; + idx--; + tree = tree->_sibling; + } + return NULL; +} + +void CFTreeGetChildren(CFTreeRef tree, CFTreeRef *children) { + __CFGenericValidateType(tree, __kCFTreeTypeID); + tree = tree->_child; + while (NULL != tree) { + *children++ = tree; + tree = tree->_sibling; + } +} + +void CFTreeApplyFunctionToChildren(CFTreeRef tree, CFTreeApplierFunction applier, void *context) { + __CFGenericValidateType(tree, __kCFTreeTypeID); + CFAssert1(NULL != applier, __kCFLogAssertion, "%s(): pointer to applier function may not be NULL", __PRETTY_FUNCTION__); + tree = tree->_child; + while (NULL != tree) { + applier(tree, context); + tree = tree->_sibling; + } +} + +void CFTreePrependChild(CFTreeRef tree, CFTreeRef newChild) { + __CFGenericValidateType(tree, __kCFTreeTypeID); + __CFGenericValidateType(newChild, __kCFTreeTypeID); + CFAssert1(NULL == newChild->_parent, __kCFLogAssertion, "%s(): must remove newChild from previous parent first", __PRETTY_FUNCTION__); + CFAssert1(NULL == newChild->_sibling, __kCFLogAssertion, "%s(): must remove newChild from previous parent first", __PRETTY_FUNCTION__); + CFRetain(newChild); + newChild->_parent = tree; + newChild->_sibling = tree->_child; + tree->_child = newChild; +} + +void CFTreeAppendChild(CFTreeRef tree, CFTreeRef newChild) { + __CFGenericValidateType(tree, __kCFTreeTypeID); + __CFGenericValidateType(newChild, __kCFTreeTypeID); + CFAssert1(NULL == newChild->_parent, __kCFLogAssertion, "%s(): must remove newChild from previous parent first", __PRETTY_FUNCTION__); + CFAssert1(NULL == newChild->_sibling, __kCFLogAssertion, "%s(): must remove newChild from previous parent first", __PRETTY_FUNCTION__); + if (newChild->_parent) { + HALT; + } + CFRetain(newChild); + newChild->_parent = tree; + newChild->_sibling = NULL; + if (!tree->_child) { + tree->_child = newChild; + } else { + CFTreeRef child; + for (child = tree->_child; child->_sibling; child = child->_sibling) + ; + child->_sibling = newChild; + } +} + +void CFTreeInsertSibling(CFTreeRef tree, CFTreeRef newSibling) { + __CFGenericValidateType(tree, __kCFTreeTypeID); + __CFGenericValidateType(newSibling, __kCFTreeTypeID); + CFAssert1(NULL != tree->_parent, __kCFLogAssertion, "%s(): tree must have a parent", __PRETTY_FUNCTION__); + CFAssert1(NULL == newSibling->_parent, __kCFLogAssertion, "%s(): must remove newSibling from previous parent first", __PRETTY_FUNCTION__); + CFAssert1(NULL == newSibling->_sibling, __kCFLogAssertion, "%s(): must remove newSibling from previous parent first", __PRETTY_FUNCTION__); + CFRetain(newSibling); + newSibling->_parent = tree->_parent; + newSibling->_sibling = tree->_sibling; + tree->_sibling = newSibling; +} + +void CFTreeRemove(CFTreeRef tree) { + __CFGenericValidateType(tree, __kCFTreeTypeID); + if (NULL != tree->_parent) { + if (tree == tree->_parent->_child) { + tree->_parent->_child = tree->_sibling; + } else { + CFTreeRef prevSibling = NULL; + for (prevSibling = tree->_parent->_child; prevSibling; prevSibling = prevSibling->_sibling) { + if (prevSibling->_sibling == tree) { + prevSibling->_sibling = tree->_sibling; + break; + } + } + } + tree->_parent = NULL; + tree->_sibling = NULL; + CFRelease(tree); + } +} + +void CFTreeRemoveAllChildren(CFTreeRef tree) { + CFTreeRef nextChild; + __CFGenericValidateType(tree, __kCFTreeTypeID); + nextChild = tree->_child; + tree->_child = NULL; + while (NULL != nextChild) { + CFTreeRef nextSibling = nextChild->_sibling; + nextChild->_parent = NULL; + nextChild->_sibling = NULL; + CFRelease(nextChild); + nextChild = nextSibling; + } +} + +struct _tcompareContext { + CFComparatorFunction func; + void *context; +}; + +static CFComparisonResult __CFTreeCompareValues(const void *v1, const void *v2, struct _tcompareContext *context) { + const void **val1 = (const void **)v1; + const void **val2 = (const void **)v2; + return (CFComparisonResult)(INVOKE_CALLBACK3(context->func, *val1, *val2, context->context)); +} + +void CFTreeSortChildren(CFTreeRef tree, CFComparatorFunction comparator, void *context) { + CFIndex children; + __CFGenericValidateType(tree, __kCFTreeTypeID); + CFAssert1(NULL != comparator, __kCFLogAssertion, "%s(): pointer to comparator function may not be NULL", __PRETTY_FUNCTION__); + children = CFTreeGetChildCount(tree); + if (1 < children) { + CFIndex idx; + CFTreeRef nextChild; + struct _tcompareContext ctx; + CFTreeRef *list, buffer[128]; + + list = (children < 128) ? buffer : CFAllocatorAllocate(kCFAllocatorDefault, children * sizeof(CFTreeRef), 0); + if (__CFOASafe && list != buffer) __CFSetLastAllocationEventName(tree->_callbacks, "CFTree (temp)"); + nextChild = tree->_child; + for (idx = 0; NULL != nextChild; idx++) { + list[idx] = nextChild; + nextChild = nextChild->_sibling; + } + + ctx.func = comparator; + ctx.context = context; + CFQSortArray(list, children, sizeof(CFTreeRef), (CFComparatorFunction)__CFTreeCompareValues, &ctx); + + tree->_child = list[0]; + for (idx = 1; idx < children; idx++) { + list[idx - 1]->_sibling = list[idx]; + } + list[idx - 1]->_sibling = NULL; + if (list != buffer) CFAllocatorDeallocate(kCFAllocatorDefault, list); + } +} + diff --git a/Collections.subproj/CFTree.h b/Collections.subproj/CFTree.h new file mode 100644 index 0000000..9a950ba --- /dev/null +++ b/Collections.subproj/CFTree.h @@ -0,0 +1,352 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFTree.h + Copyright (c) 1998-2003, Apple, Inc. All rights reserved. +*/ +/*! + @header CFTree + CFTree implements a container which stores references to other CFTrees. + Each tree may have a parent, and a variable number of children. +*/ + +#if !defined(__COREFOUNDATION_CFTREE__) +#define __COREFOUNDATION_CFTREE__ 1 + +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +/*! + @typedef CFTreeRetainCallBack + Type of the callback function used to add a retain to the user-specified + info parameter. This callback may returns the value to use whenever the + info parameter is retained, which is usually the value parameter passed + to this callback, but may be a different value if a different value + should be used. + @param info A user-supplied info parameter provided in a CFTreeContext. + @result The retained info parameter. +*/ +typedef const void * (*CFTreeRetainCallBack)(const void *info); + +/*! + @typedef CFTreeReleaseCallBack + Type of the callback function used to remove a retain previously + added to the user-specified info parameter. + @param info A user-supplied info parameter provided in a CFTreeContext. +*/ +typedef void (*CFTreeReleaseCallBack)(const void *info); + +/*! + @typedef CFTreeCopyDescriptionCallBack + Type of the callback function used to provide a description of the + user-specified info parameter. + @param info A user-supplied info parameter provided in a CFTreeContext. + @result A description of the info parameter. +*/ +typedef CFStringRef (*CFTreeCopyDescriptionCallBack)(const void *info); + +/*! + @typedef CFTreeContext + Structure containing user-specified data and callbacks for a CFTree. + @field version The version number of the structure type being passed + in as a parameter to the CFTree creation function. + This structure is version 0. + @field info A C pointer to a user-specified block of data. + @field retain The callback used to add a retain for the info field. + If this parameter is not a pointer to a function of the correct + prototype, the behavior is undefined. The value may be NULL. + @field release The calllback used to remove a retain previously added + for the info field. If this parameter is not a pointer to a + function of the correct prototype, the behavior is undefined. + The value may be NULL. + @field copyDescription The callback used to provide a description of + the info field. +*/ +typedef struct { + CFIndex version; + void * info; + CFTreeRetainCallBack retain; + CFTreeReleaseCallBack release; + CFTreeCopyDescriptionCallBack copyDescription; +} CFTreeContext; + +/*! + @typedef CFTreeApplierFunction + Type of the callback function used by the apply functions of + CFTree. + @param value The current value from the CFTree + @param context The user-defined context parameter give to the apply + function. +*/ +typedef void (*CFTreeApplierFunction)(const void *value, void *context); + +/*! + @typedef CFTreeRef + This is the type of a reference to CFTrees. +*/ +typedef struct __CFTree * CFTreeRef; + +/*! + @function CFTreeGetTypeID + Returns the type identifier of all CFTree instances. +*/ +CF_EXPORT +CFTypeID CFTreeGetTypeID(void); + +/*! + @function CFTreeCreate + Creates a new mutable tree. + @param allocator The CFAllocator which should be used to allocate + memory for the tree and storage for its children. 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 context A C pointer to a CFTreeContext structure to be copied + and used as the context of the new tree. The info parameter + will be retained by the tree if a retain function is provided. + If this value is not a valid C pointer to a CFTreeContext + structure-sized block of storage, the result is undefined. + If the version number of the storage is not a valid CFTreeContext + version number, the result is undefined. + @result A reference to the new CFTree. +*/ +CF_EXPORT +CFTreeRef CFTreeCreate(CFAllocatorRef allocator, const CFTreeContext *context); + +/*! + @function CFTreeGetParent + Returns the parent of the specified tree. + @param tree The tree to be queried. If this parameter is not a valid + CFTree, the behavior is undefined. + @result The parent of the tree. +*/ +CF_EXPORT +CFTreeRef CFTreeGetParent(CFTreeRef tree); + +/*! + @function CFTreeGetNextSibling + Returns the sibling after the specified tree in the parent tree's list. + @param tree The tree to be queried. If this parameter is not a valid + CFTree, the behavior is undefined. + @result The next sibling of the tree. +*/ +CF_EXPORT +CFTreeRef CFTreeGetNextSibling(CFTreeRef tree); + +/*! + @function CFTreeGetFirstChild + Returns the first child of the tree. + @param tree The tree to be queried. If this parameter is not a valid + CFTree, the behavior is undefined. + @result The first child of the tree. +*/ +CF_EXPORT +CFTreeRef CFTreeGetFirstChild(CFTreeRef tree); + +/*! + @function CFTreeGetContext + Returns the context of the specified tree. + @param tree The tree to be queried. If this parameter is not a valid + CFTree, the behavior is undefined. + @param context A C pointer to a CFTreeContext structure to be filled in with + the context of the specified tree. If this value is not a valid C + pointer to a CFTreeContext structure-sized block of storage, the + result is undefined. If the version number of the storage is not + a valid CFTreeContext version number, the result is undefined. +*/ +CF_EXPORT +void CFTreeGetContext(CFTreeRef tree, CFTreeContext *context); + +/*! + @function CFTreeGetChildCount + Returns the number of children of the specified tree. + @param tree The tree to be queried. If this parameter is not a valid + CFTree, the behavior is undefined. + @result The number of children. +*/ +CF_EXPORT +CFIndex CFTreeGetChildCount(CFTreeRef tree); + +/*! + @function CFTreeGetChildAtIndex + Returns the nth child of the specified tree. + @param tree The tree to be queried. If this parameter is not a valid + CFTree, the behavior is undefined. + @param idx The index of the child tree to be returned. If this parameter + is less than zero or greater than the number of children of the + tree, the result is undefined. + @result A reference to the specified child tree. +*/ +CF_EXPORT +CFTreeRef CFTreeGetChildAtIndex(CFTreeRef tree, CFIndex idx); + +/*! + @function CFTreeGetChildren + Fills the buffer with children from the tree. + @param tree The tree to be queried. If this parameter is not a valid + CFTree, the behavior is undefined. + @param children A C array of pointer-sized values to be filled with + children from the tree. If this parameter is not a valid pointer to a + C array of at least CFTreeGetChildCount() pointers, the behavior is undefined. + @result A reference to the specified child tree. +*/ +CF_EXPORT +void CFTreeGetChildren(CFTreeRef tree, CFTreeRef *children); + +/*! + @function CFTreeApplyFunctionToChildren + Calls a function once for each child of the tree. Note that the applier + only operates one level deep, and does not operate on descendents further + removed than the immediate children of the tree. + @param heap The tree to be operated upon. If this parameter is not a + valid CFTree, the behavior is undefined. + @param applier The callback function to call once for each child of + the given tree. If this parameter is not a pointer to a + function of the correct prototype, the behavior is undefined. + If there are values in the tree 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 CFTreeApplyFunctionToChildren(CFTreeRef tree, CFTreeApplierFunction applier, void *context); + +/*! + @function CFTreeFindRoot + Returns the root tree of which the specified tree is a descendent. + @param tree The tree to be queried. If this parameter is not a valid + CFTree, the behavior is undefined. + @result A reference to the root of the tree. +*/ +CF_EXPORT +CFTreeRef CFTreeFindRoot(CFTreeRef tree); + +/*! + @function CFTreeSetContext + Replaces the context of a tree. The tree releases its retain on the + info of the previous context, and retains the info of the new context. + @param tree The tree to be operated on. If this parameter is not a valid + CFTree, the behavior is undefined. + @param context A C pointer to a CFTreeContext structure to be copied + and used as the context of the new tree. The info parameter + will be retained by the tree if a retain function is provided. + If this value is not a valid C pointer to a CFTreeContext + structure-sized block of storage, the result is undefined. + If the version number of the storage is not a valid CFTreeContext + version number, the result is undefined. +*/ +CF_EXPORT +void CFTreeSetContext(CFTreeRef tree, const CFTreeContext *context); + +/*! + @function CFTreePrependChild + Adds the newChild to the specified tree as the first in its list of children. + @param tree The tree to be operated on. If this parameter is not a valid + CFTree, the behavior is undefined. + @param newChild The child to be added. + If this parameter is not a valid CFTree, the behavior is undefined. + If this parameter is a tree which is already a child of any tree, + the behavior is undefined. +*/ +CF_EXPORT +void CFTreePrependChild(CFTreeRef tree, CFTreeRef newChild); + +/*! + @function CFTreeAppendChild + Adds the newChild to the specified tree as the last in its list of children. + @param tree The tree to be operated on. If this parameter is not a valid + CFTree, the behavior is undefined. + @param newChild The child to be added. + If this parameter is not a valid CFTree, the behavior is undefined. + If this parameter is a tree which is already a child of any tree, + the behavior is undefined. +*/ +CF_EXPORT +void CFTreeAppendChild(CFTreeRef tree, CFTreeRef newChild); + +/*! + @function CFTreeInsertSibling + Inserts newSibling into the the parent tree's linked list of children after + tree. The newSibling will have the same parent as tree. + @param tree The tree to insert newSibling after. If this parameter is not a valid + CFTree, the behavior is undefined. If the tree does not have a + parent, the behavior is undefined. + @param newSibling The sibling to be added. + If this parameter is not a valid CFTree, the behavior is undefined. + If this parameter is a tree which is already a child of any tree, + the behavior is undefined. +*/ +CF_EXPORT +void CFTreeInsertSibling(CFTreeRef tree, CFTreeRef newSibling); + +/*! + @function CFTreeRemove + Removes the tree from its parent. + @param tree The tree to be removed. If this parameter is not a valid + CFTree, the behavior is undefined. +*/ +CF_EXPORT +void CFTreeRemove(CFTreeRef tree); + +/*! + @function CFTreeRemoveAllChildren + Removes all the children of the tree. + @param tree The tree to remove all children from. If this parameter is not a valid + CFTree, the behavior is undefined. +*/ +CF_EXPORT +void CFTreeRemoveAllChildren(CFTreeRef tree); + +/*! + @function CFTreeSortChildren + Sorts the children of the specified tree using the specified comparator function. + @param tree The tree to be operated on. If this parameter is not a valid + CFTree, the behavior is undefined. + @param comparator The function with the comparator function type + signature which is used in the sort operation to compare + children of the tree with the given value. If this parameter + is not a pointer to a function of the correct prototype, the + the behavior is undefined. The children of the tree 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 CFTreeSortChildren(CFTreeRef tree, CFComparatorFunction comparator, void *context); + +#if defined(__cplusplus) +} +#endif + +#endif /* ! __COREFOUNDATION_CFTREE__ */ + diff --git a/Locale.subproj/CFLocale.h b/Locale.subproj/CFLocale.h new file mode 100644 index 0000000..5fa2e48 --- /dev/null +++ b/Locale.subproj/CFLocale.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFLocale.h + Copyright (c) 2002-2003, Apple, Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFLOCALE__) +#define __COREFOUNDATION_CFLOCALE__ 1 + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_3 + +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +typedef const struct __CFLocale *CFLocaleRef; + +#if defined(__cplusplus) +} +#endif + +#endif + +#endif /* ! __COREFOUNDATION_CFLOCALE__ */ + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f2f5768 --- /dev/null +++ b/Makefile @@ -0,0 +1,436 @@ +# Simple makefile for building CoreFoundation on Darwin +# +# These make variables (or environment variables) are used +# if defined: +# SRCROOT path location of root of source hierarchy; +# defaults to ".", but must be set to a +# destination path for installsrc target. +# OBJROOT path location where .o files will be put; +# defaults to "/tmp/CoreFoundation.obj". +# SYMROOT path location where build products will be +# put; defaults to "/tmp/CoreFoundation.sym". +# DSTROOT path location where installed products will +# be put; defaults to "/tmp/CoreFoundation.dst". +# OBJROOT and SYMROOT should not be directories shared with other +# built projects, and should not be the same directory. +# PLATFORM name of platform being built on +# USER name of user building the project +# ARCHS list of archs for which to build +# RC_ARCHS more archs for which to build (build system) +# OTHER_CFLAGS other flags to be passed to compiler +# RC_CFLAGS more flags to be passed to compiler (build system) +# OTHER_LFLAGS other flags to be passed to the link stage +# + +CURRENT_PROJECT_VERSION = 299 + +# First figure out the platform if not specified, so we can use it in the +# rest of this file. Currently defined values: Darwin, Linux, FreeBSD, WIN32 +ifeq "$(PLATFORM)" "" +PLATFORM := $(shell uname) +endif + +ifeq "$(PLATFORM)" "Darwin" +PLATFORM_CFLAGS = -D__MACH__=1 -fconstant-cfstrings +endif + +ifeq "$(PLATFORM)" "Linux" +PLATFORM_CFLAGS = -D__LINUX__=1 +endif + +ifeq "$(PLATFORM)" "FreeBSD" +PLATFORM_CFLAGS = -D__FREEBSD__=1 +endif + +ifeq "$(PLATFORM)" "WIN32" +PLATFORM_CFLAGS = -D__WIN32__=1 +OBJROOT = CoreFoundation.obj +SYMROOT = CoreFoundation.sym +DSTROOT = CoreFoundation.dst +endif + +ifndef SRCROOT +SRCROOT = . +endif + +ifndef OBJROOT +OBJROOT = /tmp/CoreFoundation.obj +endif + +ifndef SYMROOT +SYMROOT = /tmp/CoreFoundation.sym +endif + +ifndef DSTROOT +DSTROOT = /tmp/CoreFoundation.dst +endif + +SILENT = @ +ifeq "$(PLATFORM)" "WIN32" +CC = gcc +ECHO = echo +MKDIRS = mkdir -p +COPY = cp +COPY_RECUR = cp -r +REMOVE = rm +REMOVE_RECUR = rm -rf +SYMLINK = ln -s +CHMOD = chmod +CHOWN = chown +TAR = tar +STRIP = strip +DLLTOOL = dlltool +else +ifeq "$(PLATFORM)" "Darwin" +CC = /usr/bin/cc +else +CC = /usr/bin/gcc +endif +ECHO = /bin/echo +MKDIRS = /bin/mkdir -p +COPY = /bin/cp +COPY_RECUR = /bin/cp -r +REMOVE = /bin/rm +REMOVE_RECUR = /bin/rm -rf +SYMLINK = /bin/ln -s +CHMOD = /bin/chmod +CHOWN = /usr/sbin/chown +TAR = /usr/bin/tar +STRIP = /usr/bin/strip +endif + +ifeq "$(PLATFORM)" "Darwin" +WARNING_FLAGS = -Wno-precomp -Wno-four-char-constants +endif + +ifeq "$(PLATFORM)" "Darwin" +ifneq "$(ARCHS)" "" +ARCH_FLAGS = $(foreach A, $(ARCHS), $(addprefix -arch , $(A))) +else +ifneq "$(RC_ARCHS)" "" +ARCH_FLAGS = $(foreach A, $(RC_ARCHS), $(addprefix -arch , $(A))) +else +ARCH_FLAGS = -arch ppc +endif +endif +endif + +ifeq "$(PLATFORM)" "FreeBSD" +ARCH_FLAGS = -march=i386 +endif + +ifeq "$(PLATFORM)" "Linux" +ARCH_FLAGS = +endif + +ifeq "$(USER)" "" +USER = unknown +endif + +CFLAGS = -DCF_BUILDING_CF=1 -g -fno-common -pipe $(PLATFORM_CFLAGS) \ + $(WARNING_FLAGS) -I$(SYMROOT)/ProjectHeaders -I. + +ifeq "$(PLATFORM)" "WIN32" +LFLAGS = -lmsvcrt -lnetapi32 -lobjc -lole32 -lws2_32 +else +LFLAGS = +endif + +ifeq "$(wildcard /System/Library/Frameworks)" "" +LIBRARY_STYLE = Library +LIBRARY_EXT = .so +ifeq "$(PLATFORM)" "Linux" +LIBRARY_EXT = .a +endif +ifeq "$(PLATFORM)" "WIN32" +LIBRARY_EXT = .dll +endif +HEADER_INSTALLDIR = /usr/local/include/CoreFoundation +INSTALLDIR = /usr/local/lib +CHARACTERSETS_INSTALLDIR = /usr/local/share/CoreFoundation +else +LIBRARY_STYLE = Framework +INSTALLDIR = /System/Library/Frameworks +FRAMEWORK_DIR = /System/Library/Frameworks/CoreFoundation.framework +CHARACTERSETS_INSTALLDIR = /System/Library/CoreServices +endif + +ifeq "$(PLATFORM)" "Darwin" +CFLAGS += $(ARCH_FLAGS) -F$(SYMROOT) +LFLAGS += $(ARCH_FLAGS) -dynamiclib -dynamic -compatibility_version 150 \ + -current_version $(CURRENT_PROJECT_VERSION) -Wl,-init,___CFInitialize +endif + +ifeq "$(PLATFORM)" "FreeBSD" +LFLAGS += -shared +endif + +ifeq "$(PLATFORM)" "Linux" +LFLAGS += -lpthread +endif + +CFLAGS += $(OTHER_CFLAGS) $(RC_CFLAGS) +LFLAGS += $(OTHER_LFLAGS) + + +SUBPROJECTS = AppServices Base Collections Locale NumberDate Parsing PlugIn \ + RunLoop String StringEncodings URL + +AppServices_PUBHEADERS = CFUserNotification.h +AppServices_SOURCES = CFUserNotification.c +Base_PROJHEADERS = CFPriv.h CFInternal.h ForFoundationOnly.h CFRuntime.h \ + CFUtilities.h +Base_PUBHEADERS = CFBase.h CFByteOrder.h CoreFoundation.h CFUUID.h +Base_SOURCES = CFBase.c CFUtilities.c CFSortFunctions.c \ + CFRuntime.c CFFileUtilities.c CFPlatform.c CFUUID.c uuid.c +Collections_PROJHEADERS = CFStorage.h +Collections_PUBHEADERS = CFArray.h CFBag.h CFBinaryHeap.h CFBitVector.h \ + CFData.h CFDictionary.h CFSet.h CFTree.h +Collections_SOURCES = CFArray.c CFBag.c CFBinaryHeap.c CFBitVector.c \ + CFData.c CFDictionary.c CFSet.c CFStorage.c CFTree.c +Locale_PUBHEADERS = CFLocale.h +NumberDate_PROJHEADERS = +NumberDate_PUBHEADERS = CFDate.h CFNumber.h CFTimeZone.h +NumberDate_SOURCES = CFDate.c CFNumber.c CFTimeZone.c +Parsing_PROJHEADERS = CFXMLInputStream.h +Parsing_PUBHEADERS = CFPropertyList.h CFXMLParser.h CFXMLNode.h +Parsing_SOURCES = CFBinaryPList.c CFPropertyList.c CFXMLParser.c \ + CFXMLInputStream.c CFXMLNode.c CFXMLTree.c +PlugIn_PROJHEADERS = CFBundlePriv.h CFBundle_BinaryTypes.h CFBundle_Internal.h \ + CFPlugIn_Factory.h +PlugIn_PUBHEADERS = CFBundle.h CFPlugIn.h CFPlugInCOM.h +PlugIn_SOURCES = CFBundle.c CFBundle_Resources.c CFPlugIn.c CFPlugIn_Factory.c \ + CFPlugIn_Instance.c CFPlugIn_PlugIn.c +ifeq "$(PLATFORM)" "Darwin" +RunLoop_PROJHEADERS = CFWindowsMessageQueue.h +RunLoop_PUBHEADERS = CFRunLoop.h CFSocket.h CFMachPort.h CFMessagePort.h +RunLoop_SOURCES = CFMachPort.c CFMessagePort.c CFRunLoop.c CFSocket.c \ + CFWindowsMessageQueue.c +endif +ifeq "$(PLATFORM)" "WIN32" +RunLoop_PROJHEADERS = +RunLoop_PUBHEADERS = CFRunLoop.h CFMachPort.h CFMessagePort.h +RunLoop_SOURCES = CFMachPort.c CFMessagePort.c CFRunLoop.c +endif +String_PROJHEADERS = CFCharacterSetPriv.h +String_PUBHEADERS = CFCharacterSet.h CFString.h +String_SOURCES = CFCharacterSet.c CFString.c CFStringEncodings.c \ + CFStringScanner.c CFStringUtilities.c +StringEncodings_PROJHEADERS = CFStringEncodingConverter.h CFUniChar.h \ + CFStringEncodingConverterExt.h CFUniCharPriv.h \ + CFStringEncodingConverterPriv.h CFUnicodeDecomposition.h \ + CFUnicodePrecomposition.h +StringEncodings_PUBHEADERS = +StringEncodings_SOURCES = CFStringEncodingConverter.c CFBuiltinConverters.c \ + CFUnicodeDecomposition.c CFUnicodePrecomposition.c CFUniChar.c +URL_PROJHEADERS = +URL_PUBHEADERS = CFURL.h CFURLAccess.h +URL_SOURCES = CFURL.c CFURLAccess.c + +OTHER_SOURCES = version.c Makefile APPLE_LICENSE PropertyList.dtd + +default: build +all: build +build: prebuild actual-build postbuild + +# These are the main targets: +# build builds the library to OBJROOT and SYMROOT +# installsrc copies the sources to SRCROOT +# installhdrs install only the headers to DSTROOT +# install build, then install the headers and library to DSTROOT +# clean removes build products in OBJROOT and SYMROOT + +installsrc: + $(SILENT) $(ECHO) "Installing source..." +ifeq "$(SRCROOT)" "." + $(SILENT) $(ECHO) "SRCROOT must be defined to be the destination directory; it cannot be '.'" + exit 1 +endif + $(SILENT) $(MKDIRS) $(SRCROOT) + $(SILENT) $(MKDIRS) $(foreach S, $(SUBPROJECTS), $(SRCROOT)/$(S).subproj) + -$(SILENT) $(foreach S, $(SUBPROJECTS), $(COPY) $(foreach F, $($(S)_SOURCES), $(S).subproj/$(F)) $(SRCROOT)/$(S).subproj;) + -$(SILENT) $(foreach S, $(SUBPROJECTS), $(COPY) $(foreach F, $($(S)_PROJHEADERS), $(S).subproj/$(F)) $(SRCROOT)/$(S).subproj;) + -$(SILENT) $(foreach S, $(SUBPROJECTS), $(COPY) $(foreach F, $($(S)_PUBHEADERS), $(S).subproj/$(F)) $(SRCROOT)/$(S).subproj;) + $(SILENT) $(COPY) $(OTHER_SOURCES) $(SRCROOT) + $(SILENT) $(COPY_RECUR) CharacterSets $(SRCROOT) + $(SILENT) $(REMOVE_RECUR) $(SRCROOT)/CharacterSets/CVS + +installhdrs: + $(SILENT) $(ECHO) "Installing headers..." +ifeq "$(LIBRARY_STYLE)" "Framework" + $(SILENT) $(REMOVE) -f $(DSTROOT)/$(FRAMEWORK_DIR)/Headers + $(SILENT) $(REMOVE) -f $(DSTROOT)/$(FRAMEWORK_DIR)/PrivateHeaders + $(SILENT) $(REMOVE) -f $(DSTROOT)/$(FRAMEWORK_DIR)/Versions/Current + $(SILENT) $(MKDIRS) $(DSTROOT)/$(FRAMEWORK_DIR)/Versions/A/Headers + $(SILENT) $(MKDIRS) $(DSTROOT)/$(FRAMEWORK_DIR)/Versions/A/PrivateHeaders + $(SILENT) $(SYMLINK) A $(DSTROOT)/$(FRAMEWORK_DIR)/Versions/Current + $(SILENT) $(SYMLINK) Versions/Current/Headers $(DSTROOT)/$(FRAMEWORK_DIR)/Headers + $(SILENT) $(SYMLINK) Versions/Current/PrivateHeaders $(DSTROOT)/$(FRAMEWORK_DIR)/PrivateHeaders + -$(SILENT) $(CHMOD) +w $(DSTROOT)/$(FRAMEWORK_DIR)/Versions/A/Headers/*.h + -$(SILENT) $(CHMOD) +w $(DSTROOT)/$(FRAMEWORK_DIR)/Versions/A/PrivateHeaders/*.h + $(SILENT) $(COPY) $(foreach S, $(SUBPROJECTS), $(foreach F, $($(S)_PUBHEADERS), $(SRCROOT)/$(S).subproj/$(F))) $(DSTROOT)/$(FRAMEWORK_DIR)/Versions/A/Headers + $(SILENT) $(COPY) Base.subproj/CFPriv.h Base.subproj/CFRuntime.h $(DSTROOT)/$(FRAMEWORK_DIR)/Versions/A/PrivateHeaders + $(SILENT) $(CHOWN) -R root:wheel $(DSTROOT)/$(FRAMEWORK_DIR) + -$(SILENT) $(CHMOD) -w $(DSTROOT)/$(FRAMEWORK_DIR)/Versions/A/Headers/*.h + -$(SILENT) $(CHMOD) -w $(DSTROOT)/$(FRAMEWORK_DIR)/Versions/A/PrivateHeaders/*.h +endif +ifeq "$(LIBRARY_STYLE)" "Library" + $(SILENT) $(MKDIRS) $(DSTROOT)/$(HEADER_INSTALLDIR) + -$(SILENT) $(CHMOD) +w $(DSTROOT)/$(HEADER_INSTALLDIR)/*.h + $(SILENT) $(COPY) $(foreach S, $(SUBPROJECTS), $(foreach F, $($(S)_PUBHEADERS), $(SRCROOT)/$(S).subproj/$(F))) $(DSTROOT)/$(HEADER_INSTALLDIR) + $(SILENT) $(CHMOD) -w $(DSTROOT)/$(HEADER_INSTALLDIR)/*.h +endif + +install: build + $(SILENT) $(ECHO) "Installing..." +ifeq "$(LIBRARY_STYLE)" "Framework" + $(SILENT) $(MKDIRS) $(DSTROOT)/$(FRAMEWORK_DIR) + -$(SILENT) $(CHMOD) -R +w $(DSTROOT)/$(FRAMEWORK_DIR) + $(SILENT) $(REMOVE_RECUR) $(DSTROOT)/$(FRAMEWORK_DIR) + $(SILENT) (cd $(SYMROOT) && $(TAR) -cf - CoreFoundation.framework) | (cd $(DSTROOT)/$(INSTALLDIR) && $(TAR) -xf -) + $(SILENT) $(STRIP) -S $(DSTROOT)/$(FRAMEWORK_DIR)/Versions/A/CoreFoundation + $(SILENT) $(STRIP) -S $(DSTROOT)/$(FRAMEWORK_DIR)/Versions/A/CoreFoundation_debug + $(SILENT) $(STRIP) -S $(DSTROOT)/$(FRAMEWORK_DIR)/Versions/A/CoreFoundation_profile + $(SILENT) $(CHMOD) -R ugo-w $(DSTROOT)/$(FRAMEWORK_DIR) + $(SILENT) $(CHMOD) -R o+rX $(DSTROOT)/$(FRAMEWORK_DIR) + $(SILENT) $(CHOWN) -R root:wheel $(DSTROOT)/$(FRAMEWORK_DIR) +endif +ifeq "$(LIBRARY_STYLE)" "Library" + $(SILENT) $(MKDIRS) $(DSTROOT)/$(INSTALLDIR) + -$(SILENT) $(CHMOD) +w $(DSTROOT)/$(INSTALLDIR) + $(SILENT) $(REMOVE) -f $(DSTROOT)/$(INSTALLDIR)/libCoreFoundation$(LIBRARY_EXT) + $(SILENT) $(REMOVE) -f $(DSTROOT)/$(INSTALLDIR)/libCoreFoundation_debug$(LIBRARY_EXT) + $(SILENT) $(REMOVE) -f $(DSTROOT)/$(INSTALLDIR)/libCoreFoundation_profile$(LIBRARY_EXT) + $(SILENT) $(COPY) $(SYMROOT)/libCoreFoundation$(LIBRARY_EXT) $(DSTROOT)/$(INSTALLDIR)/libCoreFoundation$(LIBRARY_EXT) +ifneq "$(LIBRARY_EXT)" ".a" + $(SILENT) $(COPY) $(SYMROOT)/libCoreFoundation_debug$(LIBRARY_EXT) $(DSTROOT)/$(INSTALLDIR)/libCoreFoundation_debug$(LIBRARY_EXT) + $(SILENT) $(COPY) $(SYMROOT)/libCoreFoundation_profile$(LIBRARY_EXT) $(DSTROOT)/$(INSTALLDIR)/libCoreFoundation_profile$(LIBRARY_EXT) +endif + -$(SILENT) $(CHOWN) root:wheel $(DSTROOT)/$(INSTALLDIR)/libCoreFoundation$(LIBRARY_EXT) + -$(SILENT) $(CHOWN) root:wheel $(DSTROOT)/$(INSTALLDIR)/libCoreFoundation_debug$(LIBRARY_EXT) + -$(SILENT) $(CHOWN) root:wheel $(DSTROOT)/$(INSTALLDIR)/libCoreFoundation_profile$(LIBRARY_EXT) + $(SILENT) $(CHMOD) 755 $(DSTROOT)/$(INSTALLDIR)/libCoreFoundation$(LIBRARY_EXT) +ifneq "$(LIBRARY_EXT)" ".a" + $(SILENT) $(CHMOD) 755 $(DSTROOT)/$(INSTALLDIR)/libCoreFoundation_debug$(LIBRARY_EXT) + $(SILENT) $(CHMOD) 755 $(DSTROOT)/$(INSTALLDIR)/libCoreFoundation_profile$(LIBRARY_EXT) +endif + $(SILENT) $(MKDIRS) $(DSTROOT)/$(HEADER_INSTALLDIR) + -$(SILENT) $(CHMOD) +w $(DSTROOT)/$(HEADER_INSTALLDIR)/*.h + $(SILENT) $(COPY) $(foreach S, $(SUBPROJECTS), $(foreach F, $($(S)_PUBHEADERS), $(SRCROOT)/$(S).subproj/$(F))) $(DSTROOT)/$(HEADER_INSTALLDIR) + $(SILENT) $(CHMOD) -w $(DSTROOT)/$(HEADER_INSTALLDIR)/*.h +endif + $(SILENT) $(MKDIRS) $(DSTROOT)/$(CHARACTERSETS_INSTALLDIR) + -$(SILENT) $(CHMOD) 755 $(DSTROOT)/$(CHARACTERSETS_INSTALLDIR) + $(SILENT) $(MKDIRS) $(DSTROOT)/$(CHARACTERSETS_INSTALLDIR)/CharacterSets + -$(SILENT) $(CHMOD) -R +w $(DSTROOT)/$(CHARACTERSETS_INSTALLDIR)/CharacterSets + $(SILENT) $(REMOVE_RECUR) $(DSTROOT)/$(CHARACTERSETS_INSTALLDIR)/CharacterSets + $(SILENT) $(COPY_RECUR) $(SRCROOT)/CharacterSets $(DSTROOT)/$(CHARACTERSETS_INSTALLDIR) + $(SILENT) $(REMOVE_RECUR) $(DSTROOT)/$(CHARACTERSETS_INSTALLDIR)/CharacterSets/CVS + $(SILENT) $(CHOWN) -R root:wheel $(DSTROOT)/$(CHARACTERSETS_INSTALLDIR)/CharacterSets + $(SILENT) $(CHMOD) 444 $(DSTROOT)/$(CHARACTERSETS_INSTALLDIR)/CharacterSets/* + $(SILENT) $(CHMOD) 755 $(DSTROOT)/$(CHARACTERSETS_INSTALLDIR)/CharacterSets + +clean: + $(SILENT) $(ECHO) "Deleting build products..." + $(SILENT) $(REMOVE_RECUR) $(SYMROOT)/ProjectHeaders +ifeq "$(LIBRARY_STYLE)" "Framework" + $(SILENT) $(REMOVE_RECUR) $(SYMROOT)/CoreFoundation.framework +endif +ifeq "$(LIBRARY_STYLE)" "Library" + $(SILENT) $(REMOVE) -f $(SYMROOT)/libCoreFoundation$(LIBRARY_EXT) + $(SILENT) $(REMOVE) -f $(SYMROOT)/libCoreFoundation_debug$(LIBRARY_EXT) + $(SILENT) $(REMOVE) -f $(SYMROOT)/libCoreFoundation_profile$(LIBRARY_EXT) +endif +ifeq "$(PLATFORM)" "WIN32" + $(SILENT) $(REMOVE) -f $(SYMROOT)/CoreFoundation*$(LIBRARY_EXT) + $(SILENT) $(REMOVE) -f $(SYMROOT)/CoreFoundation*.lib +endif + $(SILENT) $(REMOVE) -f $(OBJROOT)/*.o + +prebuild: + $(SILENT) $(ECHO) "Prebuild-setup..." +ifeq "$(LIBRARY_STYLE)" "Framework" + $(SILENT) $(MKDIRS) $(SYMROOT) + $(SILENT) $(REMOVE_RECUR) $(SYMROOT)/ProjectHeaders + $(SILENT) $(MKDIRS) $(SYMROOT)/ProjectHeaders +# $(SILENT) $(REMOVE_RECUR) $(SYMROOT)/CoreFoundation.framework + $(SILENT) $(REMOVE) -f $(SYMROOT)/CoreFoundation.framework/Versions/Current + $(SILENT) $(REMOVE) -f $(SYMROOT)/CoreFoundation.framework/Headers + $(SILENT) $(REMOVE) -f $(SYMROOT)/CoreFoundation.framework/PrivateHeaders + $(SILENT) $(REMOVE) -f $(SYMROOT)/CoreFoundation.framework/Resources + $(SILENT) $(MKDIRS) $(SYMROOT)/CoreFoundation.framework/Versions/A/Headers + $(SILENT) $(MKDIRS) $(SYMROOT)/CoreFoundation.framework/Versions/A/PrivateHeaders + $(SILENT) $(MKDIRS) $(SYMROOT)/CoreFoundation.framework/Versions/A/Resources + $(SILENT) $(SYMLINK) A $(SYMROOT)/CoreFoundation.framework/Versions/Current + $(SILENT) $(SYMLINK) Versions/Current/Headers $(SYMROOT)/CoreFoundation.framework/Headers + $(SILENT) $(SYMLINK) Versions/Current/PrivateHeaders $(SYMROOT)/CoreFoundation.framework/PrivateHeaders + $(SILENT) $(SYMLINK) Versions/Current/Resources $(SYMROOT)/CoreFoundation.framework/Resources + $(SILENT) $(ECHO) "Copying headers..." + $(SILENT) $(COPY) $(foreach S, $(SUBPROJECTS), $(foreach F, $($(S)_PROJHEADERS), $(SRCROOT)/$(S).subproj/$(F))) $(SYMROOT)/ProjectHeaders + $(SILENT) $(COPY) $(foreach S, $(SUBPROJECTS), $(foreach F, $($(S)_PUBHEADERS), $(SRCROOT)/$(S).subproj/$(F))) $(SYMROOT)/CoreFoundation.framework/Versions/A/Headers + $(SILENT) $(COPY) Base.subproj/CFPriv.h Base.subproj/CFRuntime.h $(SYMROOT)/CoreFoundation.framework/Versions/A/PrivateHeaders +endif +ifeq "$(LIBRARY_STYLE)" "Library" + $(SILENT) $(MKDIRS) $(SYMROOT) + $(SILENT) $(REMOVE_RECUR) $(SYMROOT)/ProjectHeaders + $(SILENT) $(MKDIRS) $(SYMROOT)/ProjectHeaders + $(SILENT) $(ECHO) "Copying headers..." + $(SILENT) $(COPY) $(foreach S, $(SUBPROJECTS), $(foreach F, $($(S)_PROJHEADERS), $(SRCROOT)/$(S).subproj/$(F))) $(SYMROOT)/ProjectHeaders + $(SILENT) $(MKDIRS) $(SYMROOT)/ProjectHeaders/CoreFoundation + $(SILENT) $(COPY) $(foreach S, $(SUBPROJECTS), $(foreach F, $($(S)_PUBHEADERS), $(SRCROOT)/$(S).subproj/$(F))) $(SYMROOT)/ProjectHeaders/CoreFoundation +endif + +actual-build: + $(SILENT) $(ECHO) "Building..." + $(SILENT) $(MKDIRS) $(OBJROOT) + $(SILENT) for x in $(foreach S, $(SUBPROJECTS), $(foreach F, $($(S)_SOURCES), $(SRCROOT)/$(S).subproj/$(F))) ; do \ + if [ ! $(OBJROOT)/`basename $$x .c`.opt.o -nt $$x ] ; then \ + $(ECHO) " ..." $$x " (optimized)" ; \ + $(CC) $(CFLAGS) $$x -O -c -o $(OBJROOT)/`basename $$x .c`.opt.o ; \ + fi ; \ + if [ ! $(OBJROOT)/`basename $$x .c`.debug.o -nt $$x ] ; then \ + $(ECHO) " ..." $$x " (debug)" ; \ + $(CC) $(CFLAGS) $$x -DDEBUG -c -o $(OBJROOT)/`basename $$x .c`.debug.o ; \ + fi ; \ + if [ ! $(OBJROOT)/`basename $$x .c`.profile.o -nt $$x ] ; then \ + $(ECHO) " ..." $$x " (profile)" ; \ + $(CC) $(CFLAGS) $$x -DPROFILE -pg -O -c -o $(OBJROOT)/`basename $$x .c`.profile.o ; \ + fi \ + done + $(SILENT) $(CC) $(CFLAGS) $(SRCROOT)/version.c -DVERSION=$(CURRENT_PROJECT_VERSION) -DUSER=$(USER) -O -c -o $(OBJROOT)/version.opt.o + $(SILENT) $(CC) $(CFLAGS) $(SRCROOT)/version.c -DVERSION=$(CURRENT_PROJECT_VERSION) -DUSER=$(USER) -DDEBUG -c -o $(OBJROOT)/version.debug.o + $(SILENT) $(CC) $(CFLAGS) $(SRCROOT)/version.c -DVERSION=$(CURRENT_PROJECT_VERSION) -DUSER=$(USER) -DPROFILE -pg -O -c -o $(OBJROOT)/version.profile.o + $(SILENT) $(ECHO) "Linking..." +ifeq "$(PLATFORM)" "Darwin" + $(SILENT) $(CC) $(LFLAGS) -O -install_name $(FRAMEWORK_DIR)/Versions/A/CoreFoundation -o $(SYMROOT)/CoreFoundation.framework/Versions/A/CoreFoundation $(OBJROOT)/*.opt.o + $(SILENT) $(CC) $(LFLAGS) -install_name $(FRAMEWORK_DIR)/Versions/A/CoreFoundation_debug -o $(SYMROOT)/CoreFoundation.framework/Versions/A/CoreFoundation_debug $(OBJROOT)/*.debug.o + $(SILENT) $(CC) $(LFLAGS) -pg -O -install_name $(FRAMEWORK_DIR)/Versions/A/CoreFoundation_profile -o $(SYMROOT)/CoreFoundation.framework/Versions/A/CoreFoundation_profile $(OBJROOT)/*.profile.o +endif +ifeq "$(PLATFORM)" "Linux" + $(SILENT) $(ECHO) "NOTE: Producing static libraries on Linux" + $(SILENT) ar cr $(SYMROOT)/libCoreFoundation$(LIBRARY_EXT) $(OBJROOT)/*.opt.o + $(SILENT) ar cr $(SYMROOT)/libCoreFoundation_debug$(LIBRARY_EXT) $(OBJROOT)/*.debug.o + $(SILENT) ar cr $(SYMROOT)/libCoreFoundation_profile$(LIBRARY_EXT) $(OBJROOT)/*.profile.o +endif +ifeq "$(PLATFORM)" "FreeBSD" + $(SILENT) $(CC) $(LFLAGS) -O -o $(SYMROOT)/libCoreFoundation$(LIBRARY_EXT) (OBJROOT)/*.opt.o + $(SILENT) $(CC) $(LFLAGS) -o $(SYMROOT)/libCoreFoundation_debug$(LIBRARY_EXT) (OBJROOT)/*.debug.o + $(SILENT) $(CC) $(LFLAGS) -pg -O -o $(SYMROOT)/libCoreFoundation_profile$(LIBRARY_EXT) (OBJROOT)/*.profile.o +endif +ifeq "$(PLATFORM)" "WIN32" + $(SILENT) $(DLLTOOL) -e CoreFoundation -l CoreFoundation.lib $(OBJROOT)/*.opt.o + $(SILENT) $(CC) -mdll $(OBJROOT)/*.opt.o CoreFoundation -o CoreFoundation$(LIBRARY_EXT) $(LFLAGS) + $(SILENT) $(DLLTOOL) -e CoreFoundation_debug -l CoreFoundation_debug.lib $(OBJROOT)/*.debug.o + $(SILENT) $(CC) -mdll $(OBJROOT)/*.debug.o CoreFoundation_debug -o CoreFoundation_debug$(LIBRARY_EXT) $(LFLAGS) + $(SILENT) $(COPY) *.lib $(SYMROOT) + $(SILENT) $(COPY) *.dll $(SYMROOT) + $(SILENT) $(REMOVE) *.dll *.lib CoreFoundation CoreFoundation_debug +endif + $(SILENT) $(ECHO) "Done!" + +postbuild: +ifeq "$(LIBRARY_STYLE)" "Framework" + -$(SILENT) $(SYMLINK) Versions/Current/CoreFoundation $(SYMROOT)/CoreFoundation.framework/CoreFoundation + -$(SILENT) $(SYMLINK) Versions/Current/CoreFoundation_debug $(SYMROOT)/CoreFoundation.framework/CoreFoundation_debug + -$(SILENT) $(SYMLINK) Versions/Current/CoreFoundation_profile $(SYMROOT)/CoreFoundation.framework/CoreFoundation_profile +endif diff --git a/NumberDate.subproj/CFDate.c b/NumberDate.subproj/CFDate.c new file mode 100644 index 0000000..dd62749 --- /dev/null +++ b/NumberDate.subproj/CFDate.c @@ -0,0 +1,518 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFDate.c + Copyright 1998-2002, Apple, Inc. All rights reserved. + Responsibility: Christopher Kane +*/ + +#include +#include +#include +#include +#include +#include +#include "CFInternal.h" +#include +#if defined(__MACH__) || defined(__LINUX__) + #include +#endif +#if defined(__WIN32__) + #include +#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. */ + +struct __CFDate { + CFRuntimeBase _base; + CFAbsoluteTime _time; /* immutable */ +}; + +#if defined(__MACH__) +static double __CFTSRRate = 0.0; +static CFAbsoluteTime __CFBootAbsoluteTime = 0.0; +static CFTimeInterval __CFLastSyncOffset = -1.0E+20; + +__private_extern__ int64_t __CFTimeIntervalToTSR(CFTimeInterval ti) { + return (int64_t)(ti * __CFTSRRate); +} + +__private_extern__ CFTimeInterval __CFTSRToTimeInterval(int64_t tsr) { + return (CFTimeInterval)((double)tsr / __CFTSRRate); +} + +__private_extern__ int64_t __CFAbsoluteTimeToTSR(CFAbsoluteTime at) { + CFTimeInterval delta = __CFReadTSR() / __CFTSRRate; + if (__CFLastSyncOffset + 0.01 < delta) { + struct timeval tv; + /* 0.01 seconds is arbitrarily chosen, but keeps the error under + 0.00001 seconds generally; we need a number which is large enough + to cut down on the number of gettimeofday() calls, but small + enough that radical changes to the calendar clock are noticed + reasonably quickly. */ + gettimeofday(&tv, NULL); + /* hope we don't context-switch here */ + delta = __CFReadTSR() / __CFTSRRate; + __CFLastSyncOffset = delta; + CFAbsoluteTime newBoot = ((double)tv.tv_sec - kCFAbsoluteTimeIntervalSince1970) + 1.0E-6 * (double)tv.tv_usec - delta; + if (0.4 < fabs(newBoot - __CFBootAbsoluteTime)) { + /* 0.4 arbitrarily chosen to keep the reported absolute time too + * the 'actual' value, but not update __CFBootAbsoluteTime with + * small changes to keep it from jittering. */ + __CFBootAbsoluteTime = newBoot; + } + } + return (at - __CFBootAbsoluteTime) * __CFTSRRate; +} + +__private_extern__ CFAbsoluteTime __CFTSRToAbsoluteTime(int64_t tsr) { + CFTimeInterval delta = __CFReadTSR() / __CFTSRRate; + if (__CFLastSyncOffset + 0.01 < delta) { + struct timeval tv; + /* 0.01 seconds is arbitrarily chosen, but keeps the error under + 0.00001 seconds generally; we need a number which is large enough + to cut down on the number of gettimeofday() calls, but small + enough that radical changes to the calendar clock are noticed + reasonably quickly. */ + gettimeofday(&tv, NULL); + /* hope we don't context-switch here */ + delta = __CFReadTSR() / __CFTSRRate; + __CFLastSyncOffset = delta; + CFAbsoluteTime newBoot = ((double)tv.tv_sec - kCFAbsoluteTimeIntervalSince1970) + 1.0E-6 * (double)tv.tv_usec - delta; + if (0.4 < fabs(newBoot - __CFBootAbsoluteTime)) { + /* 0.4 arbitrarily chosen to keep the reported absolute time too + * the 'actual' value, but not update __CFBootAbsoluteTime with + * small changes to keep it from jittering. */ + __CFBootAbsoluteTime = newBoot; + } + } + return __CFBootAbsoluteTime + (tsr / __CFTSRRate); +} +#endif + +CFAbsoluteTime CFAbsoluteTimeGetCurrent(void) { + CFAbsoluteTime ret; +#if defined(__WIN32__) + FILETIME ft; + GetSystemTimeAsFileTime(&ft); + ret = (CFTimeInterval)ft.dwHighDateTime * 429.49672960; + ret += (CFTimeInterval)ft.dwLowDateTime / 10000000.0; + ret -= (11644473600.0 + kCFAbsoluteTimeIntervalSince1970); + /* seconds between 1601 and 1970, 1970 and 2001 */ +#endif +#if defined(__MACH__) + ret = __CFTSRToAbsoluteTime(__CFReadTSR()); +#endif +#if defined(__svr4__) || defined(__hpux__) || defined(__LINUX__) + struct timeval tv; + gettimeofday(&tv, NULL); + ret = (CFTimeInterval)tv.tv_sec - kCFAbsoluteTimeIntervalSince1970; + ret += (1.0E-6 * (CFTimeInterval)tv.tv_usec); +#endif + return ret; +} + +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("{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 +}; + +__private_extern__ void __CFDateInitialize(void) { +#if defined(__MACH__) + struct mach_timebase_info info; + mach_timebase_info(&info); + __CFTSRRate = (1.0E9 / (double)info.numer) * (double)info.denom; +#endif + __kCFDateTypeID = _CFRuntimeRegisterClass(&__CFDateClass); +} + +CFTypeID CFDateGetTypeID(void) { + return __kCFDateTypeID; +} + +CFDateRef CFDateCreate(CFAllocatorRef allocator, CFAbsoluteTime at) { + CFDateRef memory; + uint32_t size; + size = sizeof(struct __CFDate) - sizeof(CFRuntimeBase); + memory = _CFRuntimeCreateInstance(allocator, __kCFDateTypeID, size, NULL); + if (NULL == memory) { + return NULL; + } + ((struct __CFDate *)memory)->_time = at; + return memory; +} + +CFTimeInterval CFDateGetAbsoluteTime(CFDateRef date) { + CF_OBJC_FUNCDISPATCH0(__kCFDateTypeID, CFTimeInterval, date, "timeIntervalSinceReferenceDate"); + __CFGenericValidateType(date, CFDateGetTypeID()); + return date->_time; +} + +CFTimeInterval CFDateGetTimeIntervalSinceDate(CFDateRef date, CFDateRef otherDate) { + CF_OBJC_FUNCDISPATCH1(__kCFDateTypeID, 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(__kCFDateTypeID, 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; +} + +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}; + +static inline bool isleap(int32_t year) { + int32_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 */ +static inline uint8_t __CFDaysInMonth(int8_t month, int32_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 */ +static inline uint16_t __CFDaysBeforeMonth(int8_t month, int32_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 */ +static inline uint16_t __CFDaysAfterMonth(int8_t month, int32_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(int32_t absolute, int32_t *year, int8_t *month, int8_t *day) { + int32_t b = absolute / 146097; // take care of as many multiples of 400 years as possible + int32_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 int32_t __CFAbsoluteFromYMD(int32_t year, int8_t month, int8_t day) { + int32_t absolute = 0, idx; + if (year < 0) { + for (idx = year; idx < 0; idx++) + absolute -= __CFDaysAfterMonth(0, idx, isleap(idx)); + } else { + for (idx = year - 1; 0 <= idx; 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) && (__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; + int32_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 = (int32_t)(float)floor(fixedat / 86400.0); + __CFYMDFromAbsolute(absolute, &year, &month, &day); + 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); + 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 + (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; + } + return units; +} + +SInt32 CFAbsoluteTimeGetDayOfWeek(CFAbsoluteTime at, CFTimeZoneRef tz) { + int32_t absolute; + CFAbsoluteTime fixedat; + if (NULL != tz) { + __CFGenericValidateType(tz, CFTimeZoneGetTypeID()); + } + fixedat = at + (NULL != tz ? CFTimeZoneGetSecondsFromGMT(tz, at) : 0.0); + absolute = (int32_t)(float)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; + int32_t absolute, year; + int8_t month, day; + if (NULL != tz) { + __CFGenericValidateType(tz, CFTimeZoneGetTypeID()); + } + fixedat = at + (NULL != tz ? CFTimeZoneGetSecondsFromGMT(tz, at) : 0.0); + absolute = (int32_t)(float)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) { + int32_t absolute, year; + int8_t month, day; + int32_t absolute0101, dow0101; + CFAbsoluteTime fixedat; + if (NULL != tz) { + __CFGenericValidateType(tz, CFTimeZoneGetTypeID()); + } + fixedat = at + (NULL != tz ? CFTimeZoneGetSecondsFromGMT(tz, at) : 0.0); + absolute = (int32_t)(float)floor(fixedat / 86400.0); + __CFYMDFromAbsolute(absolute, &year, &month, &day); + absolute0101 = __CFAbsoluteFromYMD(year, 1, 1); + dow0101 = (absolute0101 < 0) ? ((absolute0101 + 1) % 7 + 7) : (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) { + int32_t absolute20101, dow20101; + absolute20101 = __CFAbsoluteFromYMD(year + 1, 1, 1); + dow20101 = (absolute20101 < 0) ? ((absolute20101 + 1) % 7 + 7) : (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/NumberDate.subproj/CFDate.h b/NumberDate.subproj/CFDate.h new file mode 100644 index 0000000..91da62c --- /dev/null +++ b/NumberDate.subproj/CFDate.h @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFDate.h + Copyright (c) 1998-2003, Apple, Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFDATE__) +#define __COREFOUNDATION_CFDATE__ 1 + +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +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 { + 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; + +typedef 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 +} 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); + +#if defined(__cplusplus) +} +#endif + +#endif /* ! __COREFOUNDATION_CFDATE__ */ + diff --git a/NumberDate.subproj/CFNumber.c b/NumberDate.subproj/CFNumber.c new file mode 100644 index 0000000..84ea524 --- /dev/null +++ b/NumberDate.subproj/CFNumber.c @@ -0,0 +1,575 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFNumber.c + Copyright 1999-2002, Apple, Inc. All rights reserved. + Responsibility: Ali Ozer +*/ + +#include +#include "CFInternal.h" +#include "CFUtilities.h" +#include +#include +#if defined(__WIN32__) +#define isnan _isnan +#define isinf !_finite +#endif + +/* Various assertions +*/ +#define __CFAssertIsBoolean(cf) __CFGenericValidateType(cf, __kCFBooleanTypeID) +#define __CFAssertIsNumber(cf) __CFGenericValidateType(cf, __kCFNumberTypeID) +#define __CFAssertIsValidNumberType(type) CFAssert2((type > 0 && type <= kCFNumberMaxType && __CFNumberCanonicalType[type]), __kCFLogAssertion, "%s(): bad CFNumber type %d", __PRETTY_FUNCTION__, type); +#define __CFInvalidNumberStorageType(type) CFAssert2(true, __kCFLogAssertion, "%s(): bad CFNumber storage 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) + +struct __CFBoolean { + CFRuntimeBase _base; +}; + +static struct __CFBoolean __kCFBooleanTrue = { + INIT_CFRUNTIME_BASE(NULL, 0, 0x0080) +}; +const CFBooleanRef kCFBooleanTrue = &__kCFBooleanTrue; + +static struct __CFBoolean __kCFBooleanFalse = { + INIT_CFRUNTIME_BASE(NULL, 0, 0x0080) +}; +const CFBooleanRef kCFBooleanFalse = &__kCFBooleanFalse; + +static CFStringRef __CFBooleanCopyDescription(CFTypeRef cf) { + CFBooleanRef boolean = (CFBooleanRef)cf; + return CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("{value = %s}"), cf, CFGetAllocator(cf), (boolean == kCFBooleanTrue) ? "true" : "false"); +} + +static CFStringRef __CFBooleanCopyFormattingDescription(CFTypeRef cf, CFDictionaryRef formatOptions) { + CFBooleanRef boolean = (CFBooleanRef)cf; + return 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._isa = __CFISAForTypeID(__kCFBooleanTypeID); + _CFRuntimeSetInstanceTypeID(&__kCFBooleanFalse, __kCFBooleanTypeID); + __kCFBooleanFalse._base._isa = __CFISAForTypeID(__kCFBooleanTypeID); +} + +CFTypeID CFBooleanGetTypeID(void) { + return __kCFBooleanTypeID; +} + +Boolean CFBooleanGetValue(CFBooleanRef boolean) { + CF_OBJC_FUNCDISPATCH0(__kCFBooleanTypeID, Boolean, boolean, "boolValue"); + return (boolean == kCFBooleanTrue) ? true : false; +} + + +/*** CFNumber ***/ + +typedef union { + SInt32 valSInt32; + int64_t valSInt64; + Float32 valFloat32; + Float64 valFloat64; +} __CFNumberValue; + +struct __CFNumber { /* Only as many bytes as necessary are allocated */ + CFRuntimeBase _base; + __CFNumberValue value; +}; + +static struct __CFNumber __kCFNumberNaN = { + INIT_CFRUNTIME_BASE(NULL, 0, 0x0080), {0} +}; +const CFNumberRef kCFNumberNaN = &__kCFNumberNaN; + +static struct __CFNumber __kCFNumberNegativeInfinity = { + INIT_CFRUNTIME_BASE(NULL, 0, 0x0080), {0} +}; +const CFNumberRef kCFNumberNegativeInfinity = &__kCFNumberNegativeInfinity; + +static struct __CFNumber __kCFNumberPositiveInfinity = { + INIT_CFRUNTIME_BASE(NULL, 0, 0x0080), {0} +}; +const CFNumberRef kCFNumberPositiveInfinity = &__kCFNumberPositiveInfinity; + + +/* Eight bits in base: + Bits 6..0: type (bits 4..0 is CFNumberType) +*/ +enum { + __kCFNumberIsPositiveInfinity = (0x20 | kCFNumberFloat64Type), + __kCFNumberIsNegativeInfinity = (0x40 | kCFNumberFloat64Type), + __kCFNumberIsNaN = (0x60 | kCFNumberFloat64Type) +}; + + +/* ??? These tables should be changed on different architectures, depending on the actual sizes of basic C types such as int, long, float; also size of CFIndex. We can probably compute these tables at runtime. +*/ + +/* Canonical types are the types that the implementation knows how to deal with. There should be one for each type that is distinct; so this table basically is a type equivalence table. All functions that take a type from the outside world should call __CFNumberGetCanonicalTypeForType() before doing anything with it. +*/ +static const unsigned char __CFNumberCanonicalType[kCFNumberMaxType + 1] = { + 0, kCFNumberSInt8Type, kCFNumberSInt16Type, kCFNumberSInt32Type, kCFNumberSInt64Type, kCFNumberFloat32Type, kCFNumberFloat64Type, + kCFNumberSInt8Type, kCFNumberSInt16Type, kCFNumberSInt32Type, kCFNumberSInt32Type, kCFNumberSInt64Type, kCFNumberFloat32Type, kCFNumberFloat64Type, + kCFNumberSInt32Type +}; + +/* This table determines what storage format is used for any given type. + !!! These are the only types that can occur in the types field of a CFNumber. + !!! If the number or kind of types returned by this array changes, also need to fix NSNumber and NSCFNumber. +*/ +static const unsigned char __CFNumberStorageType[kCFNumberMaxType + 1] = { + 0, kCFNumberSInt32Type, kCFNumberSInt32Type, kCFNumberSInt32Type, kCFNumberSInt64Type, kCFNumberFloat32Type, kCFNumberFloat64Type, + kCFNumberSInt32Type, kCFNumberSInt32Type, kCFNumberSInt32Type, kCFNumberSInt32Type, kCFNumberSInt64Type, kCFNumberFloat32Type, kCFNumberFloat64Type, + kCFNumberSInt32Type +}; + +// Returns the type that is used to store the specified type +CF_INLINE CFNumberType __CFNumberGetStorageTypeForType(CFNumberType type) { + return __CFNumberStorageType[type]; +} + +// Returns the canonical type used to represent the specified type +CF_INLINE CFNumberType __CFNumberGetCanonicalTypeForType(CFNumberType type) { + return __CFNumberCanonicalType[type]; +} + +// Extracts and returns the type out of the CFNumber +CF_INLINE CFNumberType __CFNumberGetType(CFNumberRef num) { + return __CFBitfieldGetValue(num->_base._info, 4, 0); +} + +// Returns true if the argument type is float or double +CF_INLINE Boolean __CFNumberTypeIsFloat(CFNumberType type) { + return (type == kCFNumberFloat64Type) || (type == kCFNumberFloat32Type) || (type == kCFNumberDoubleType) || (type == kCFNumberFloatType); +} + +// Returns the number of bytes necessary to store the specified type +// Needs to handle all canonical types +CF_INLINE CFIndex __CFNumberSizeOfType(CFNumberType type) { + switch (type) { + case kCFNumberSInt8Type: return sizeof(int8_t); + case kCFNumberSInt16Type: return sizeof(int16_t); + case kCFNumberSInt32Type: return sizeof(SInt32); + case kCFNumberSInt64Type: return sizeof(int64_t); + case kCFNumberFloat32Type: return sizeof(Float32); + case kCFNumberFloat64Type: return sizeof(Float64); + default: return 0; + } +} + +// Copies an external value of a given type into the appropriate slot in the union (does no type conversion) +// Needs to handle all canonical types +#define SET_VALUE(valueUnion, type, valuePtr) \ + switch (type) { \ + case kCFNumberSInt8Type: (valueUnion)->valSInt32 = *(int8_t *)(valuePtr); break; \ + case kCFNumberSInt16Type: (valueUnion)->valSInt32 = *(int16_t *)(valuePtr); break; \ + case kCFNumberSInt32Type: (valueUnion)->valSInt32 = *(SInt32 *)(valuePtr); break; \ + case kCFNumberSInt64Type: (valueUnion)->valSInt64 = *(int64_t *)(valuePtr); break; \ + case kCFNumberFloat32Type: (valueUnion)->valFloat32 = *(Float32 *)(valuePtr); break; \ + case kCFNumberFloat64Type: (valueUnion)->valFloat64 = *(Float64 *)(valuePtr); break; \ + default: break; \ + } + +// Casts the specified value into the specified type and copies it into the provided memory +// Needs to handle all canonical types +#define GET_VALUE(value, type, resultPtr) \ + switch (type) { \ + case kCFNumberSInt8Type: *(int8_t *)(valuePtr) = (int8_t)value; break; \ + case kCFNumberSInt16Type: *(int16_t *)(valuePtr) = (int16_t)value; break; \ + case kCFNumberSInt32Type: *(SInt32 *)(valuePtr) = (SInt32)value; break; \ + case kCFNumberSInt64Type: *(int64_t *)(valuePtr) = (int64_t)value; break; \ + case kCFNumberFloat32Type: *(Float32 *)(valuePtr) = (Float32)value; break; \ + case kCFNumberFloat64Type: *(Float64 *)(valuePtr) = (Float64)value; break; \ + default: break; \ + } + +// Extracts the stored type out of the union and copies it in the desired type into the provided memory +// Needs to handle all storage types +CF_INLINE void __CFNumberGetValue(const __CFNumberValue *value, CFNumberType numberType, CFNumberType typeToGet, void *valuePtr) { + switch (numberType) { + case kCFNumberSInt32Type: GET_VALUE(value->valSInt32, typeToGet, resultPtr); break; + case kCFNumberSInt64Type: GET_VALUE(value->valSInt64, typeToGet, resultPtr); break; + case kCFNumberFloat32Type: GET_VALUE(value->valFloat32, typeToGet, resultPtr); break; + case kCFNumberFloat64Type: GET_VALUE(value->valFloat64, typeToGet, resultPtr); break; + default: break; \ + } +} + +// Sees if two value union structs have the same value (will do type conversion) +static Boolean __CFNumberEqualValue(const __CFNumberValue *value1, CFNumberType type1, const __CFNumberValue *value2, CFNumberType type2) { + if (__CFNumberTypeIsFloat(type1) || __CFNumberTypeIsFloat(type2)) { + Float64 d1, d2; + __CFNumberGetValue(value1, type1, kCFNumberFloat64Type, &d1); + __CFNumberGetValue(value2, type2, kCFNumberFloat64Type, &d2); + if (_CFExecutableLinkedOnOrAfter(CFSystemVersionJaguar)) { + if (isnan(d1) && isnan(d2)) return true; // Not mathematically sound, but required + } + return d1 == d2; + } else { + int64_t i1, i2; + __CFNumberGetValue(value1, type1, kCFNumberSInt64Type, &i1); + __CFNumberGetValue(value2, type2, kCFNumberSInt64Type, &i2); + return i1 == i2; + } +} + +static Boolean __CFNumberEqual(CFTypeRef cf1, CFTypeRef cf2) { + CFNumberRef number1 = (CFNumberRef)cf1; + CFNumberRef number2 = (CFNumberRef)cf2; + return __CFNumberEqualValue(&(number1->value), __CFNumberGetType(number1), &(number2->value), __CFNumberGetType(number2)); +} + +static CFHashCode __CFNumberHash(CFTypeRef cf) { + CFNumberRef number = (CFNumberRef)cf; + switch (__CFNumberGetType(cf)) { + case kCFNumberSInt32Type: return _CFHashInt(number->value.valSInt32); + case kCFNumberSInt64Type: return _CFHashDouble((double)(number->value.valSInt64)); + case kCFNumberFloat32Type: return _CFHashDouble((double)(number->value.valFloat32)); + case kCFNumberFloat64Type: return _CFHashDouble((double)(number->value.valFloat64)); + default: + __CFInvalidNumberStorageType(__CFNumberGetType(cf)); + return 0; + } +} + +#define bufSize 100 +#define emitChar(ch) \ + {if (buf - stackBuf == bufSize) {CFStringAppendCharacters(mstr, stackBuf, bufSize); buf = stackBuf;} *buf++ = ch;} + +static void __CFNumberEmitInt64(CFMutableStringRef mstr, int64_t value, int32_t width, UniChar pad, bool explicitPlus) { + UniChar stackBuf[bufSize], *buf = stackBuf; + uint64_t uvalue, factor, tmp; + int32_t w; + bool neg; + + neg = (value < 0) ? true : false; + uvalue = (neg) ? -value : value; + if (neg || explicitPlus) width--; + width--; + factor = 1; + tmp = uvalue; + while (9 < tmp) { + width--; + factor *= 10; + tmp /= 10; + } + for (w = 0; w < width; w++) emitChar(pad); + if (neg) { + emitChar('-'); + } else if (explicitPlus) { + emitChar('+'); + } + while (0 < factor) { + UniChar ch = '0' + (uvalue / factor); + uvalue %= factor; + emitChar(ch); + factor /= 10; + } + if (buf > stackBuf) CFStringAppendCharacters(mstr, stackBuf, buf - stackBuf); +} + +static CFStringRef __CFNumberCopyDescription(CFTypeRef cf) { + CFNumberRef number = (CFNumberRef)cf; + CFMutableStringRef mstr = CFStringCreateMutable(kCFAllocatorSystemDefault, 0); + CFStringAppendFormat(mstr, NULL, CFSTR("{value = "), cf, CFGetAllocator(cf)); + switch (__CFNumberGetType(number)) { + case kCFNumberSInt32Type: + __CFNumberEmitInt64(mstr, number->value.valSInt32, 0, ' ', true); + CFStringAppendFormat(mstr, NULL, CFSTR(", type = kCFNumberSInt32Type}")); + break; + case kCFNumberSInt64Type: + __CFNumberEmitInt64(mstr, number->value.valSInt64, 0, ' ', true); + CFStringAppendFormat(mstr, NULL, CFSTR(", type = kCFNumberSInt64Type}")); + break; + case kCFNumberFloat32Type: + // debugging formatting is intentionally more verbose and explicit about the value of the number + if (isnan(number->value.valFloat32)) { + CFStringAppend(mstr, CFSTR("nan")); + } else if (isinf(number->value.valFloat32)) { + CFStringAppend(mstr, (0.0f < number->value.valFloat32) ? CFSTR("+infinity") : CFSTR("-infinity")); + } else if (0.0f == number->value.valFloat32) { + CFStringAppend(mstr, (copysign(1.0, number->value.valFloat32) < 0.0) ? CFSTR("-0.0") : CFSTR("+0.0")); + } else { + CFStringAppendFormat(mstr, NULL, CFSTR("%+.10f"), number->value.valFloat32); + } + CFStringAppend(mstr, CFSTR(", type = kCFNumberFloat32Type}")); + break; + case kCFNumberFloat64Type: + // debugging formatting is intentionally more verbose and explicit about the value of the number + if (isnan(number->value.valFloat64)) { + CFStringAppend(mstr, CFSTR("nan")); + } else if (isinf(number->value.valFloat64)) { + CFStringAppend(mstr, (0.0 < number->value.valFloat64) ? CFSTR("+infinity") : CFSTR("-infinity")); + } else if (0.0 == number->value.valFloat64) { + CFStringAppend(mstr, (copysign(1.0, number->value.valFloat64) < 0.0) ? CFSTR("-0.0") : CFSTR("+0.0")); + } else { + CFStringAppendFormat(mstr, NULL, CFSTR("%+.20f"), number->value.valFloat64); + } + CFStringAppend(mstr, CFSTR(", type = kCFNumberFloat64Type}")); + break; + default: + __CFInvalidNumberStorageType(__CFNumberGetType(number)); + CFRelease(mstr); + return NULL; + } + return mstr; +} + +// This function separated out from __CFNumberCopyFormattingDescription() so the plist creation can use it as well. + +__private_extern__ CFStringRef __CFNumberCopyFormattingDescriptionAsFloat64(CFTypeRef cf) { + double d; + CFNumberGetValue(cf, kCFNumberFloat64Type, &d); + if (_CFExecutableLinkedOnOrAfter(CFSystemVersionJaguar)) { + if (isnan(d)) { + return CFRetain(CFSTR("nan")); + } + if (isinf(d)) { + return CFRetain((0.0 < d) ? CFSTR("+infinity") : CFSTR("-infinity")); + } + if (0.0 == d) { + return 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(kCFAllocatorDefault, NULL, CFSTR("%.*g"), DBL_DIG + 2, d); + } + return CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%lf"), d); +} + +static CFStringRef __CFNumberCopyFormattingDescription(CFTypeRef cf, CFDictionaryRef formatOptions) { + CFNumberRef number = (CFNumberRef)cf; + CFMutableStringRef mstr; + int64_t value; + switch (__CFNumberGetType(number)) { + case kCFNumberSInt32Type: + case kCFNumberSInt64Type: + value = (__CFNumberGetType(number) == kCFNumberSInt32Type) ? number->value.valSInt32 : number->value.valSInt64; + mstr = CFStringCreateMutable(kCFAllocatorSystemDefault, 0); + __CFNumberEmitInt64(mstr, value, 0, ' ', false); + return mstr; + case kCFNumberFloat32Type: + if (_CFExecutableLinkedOnOrAfter(CFSystemVersionJaguar)) { + if (isnan(number->value.valFloat32)) { + return CFRetain(CFSTR("nan")); + } + if (isinf(number->value.valFloat32)) { + return CFRetain((0.0f < number->value.valFloat32) ? CFSTR("+infinity") : CFSTR("-infinity")); + } + if (0.0f == number->value.valFloat32) { + return CFRetain(CFSTR("0.0")); + } + // if %g is used here, need to use FLT_DIG + 2 on Mac OS X, but %f needs +1 + return CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%.*g"), FLT_DIG + 2, number->value.valFloat32); + } + return CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%f"), number->value.valFloat32); + break; + case kCFNumberFloat64Type: + return __CFNumberCopyFormattingDescriptionAsFloat64(number); + break; + default: + __CFInvalidNumberStorageType(__CFNumberGetType(number)); + return NULL; + } +} + +static CFTypeID __kCFNumberTypeID = _kCFRuntimeNotATypeID; + +static const CFRuntimeClass __CFNumberClass = { + 0, + "CFNumber", + NULL, // init + NULL, // copy + NULL, + __CFNumberEqual, + __CFNumberHash, + __CFNumberCopyFormattingDescription, + __CFNumberCopyDescription +}; + +__private_extern__ void __CFNumberInitialize(void) { + uint64_t dnan = BITSFORDOUBLENAN; + uint64_t negInf = BITSFORDOUBLENEGINF; + uint64_t posInf = BITSFORDOUBLEPOSINF; + + __kCFNumberTypeID = _CFRuntimeRegisterClass(&__CFNumberClass); + + _CFRuntimeSetInstanceTypeID(&__kCFNumberNaN, __kCFNumberTypeID); + __kCFNumberNaN._base._isa = __CFISAForTypeID(__kCFNumberTypeID); + __CFBitfieldSetValue(__kCFNumberNaN._base._info, 6, 0, __kCFNumberIsNaN); + __kCFNumberNaN.value.valFloat64 = *(double *)&dnan; + + _CFRuntimeSetInstanceTypeID(& __kCFNumberNegativeInfinity, __kCFNumberTypeID); + __kCFNumberNegativeInfinity._base._isa = __CFISAForTypeID(__kCFNumberTypeID); + __CFBitfieldSetValue(__kCFNumberNegativeInfinity._base._info, 6, 0, __kCFNumberIsNegativeInfinity); + __kCFNumberNegativeInfinity.value.valFloat64 = *(double *)&negInf; + + _CFRuntimeSetInstanceTypeID(& __kCFNumberPositiveInfinity, __kCFNumberTypeID); + __kCFNumberPositiveInfinity._base._isa = __CFISAForTypeID(__kCFNumberTypeID); + __CFBitfieldSetValue(__kCFNumberPositiveInfinity._base._info, 6, 0, __kCFNumberIsPositiveInfinity); + __kCFNumberPositiveInfinity.value.valFloat64 = *(double *)&posInf; +} + +CFTypeID CFNumberGetTypeID(void) { + return __kCFNumberTypeID; +} + +CFNumberRef CFNumberCreate(CFAllocatorRef allocator, CFNumberType type, const void *valuePtr) { + CFNumberRef num; + CFNumberType equivType, storageType; + + if ((type == kCFNumberFloat32Type) || (type == kCFNumberFloatType)) { + Float32 val = *(Float32 *)(valuePtr); + if (isnan(val)) return CFRetain(kCFNumberNaN); + if (isinf(val)) return CFRetain((val < 0.0) ? kCFNumberNegativeInfinity : kCFNumberPositiveInfinity); + } else if ((type == kCFNumberFloat64Type) || (type == kCFNumberDoubleType)) { + Float64 val = *(Float64 *)(valuePtr); + if (isnan(val)) return CFRetain(kCFNumberNaN); + if (isinf(val)) return CFRetain((val < 0.0) ? kCFNumberNegativeInfinity : kCFNumberPositiveInfinity); + } + + equivType = __CFNumberGetCanonicalTypeForType(type); + storageType = __CFNumberGetStorageTypeForType(type); + + num = (CFNumberRef)_CFRuntimeCreateInstance(allocator, __kCFNumberTypeID, __CFNumberSizeOfType(storageType), NULL); + if (NULL == num) { + return NULL; + } + SET_VALUE((__CFNumberValue *)&(num->value), equivType, valuePtr); + __CFBitfieldSetValue(((struct __CFNumber *)num)->_base._info, 6, 0, storageType); + return num; +} + +CFNumberType CFNumberGetType(CFNumberRef number) { + CF_OBJC_FUNCDISPATCH0(__kCFNumberTypeID, CFNumberType, number, "_cfNumberType"); + + __CFAssertIsNumber(number); + return __CFNumberGetType(number); +} + +CFIndex CFNumberGetByteSize(CFNumberRef number) { + __CFAssertIsNumber(number); + return __CFNumberSizeOfType(CFNumberGetType(number)); +} + +Boolean CFNumberIsFloatType(CFNumberRef number) { + __CFAssertIsNumber(number); + return __CFNumberTypeIsFloat(CFNumberGetType(number)); +} + +Boolean CFNumberGetValue(CFNumberRef number, CFNumberType type, void *valuePtr) { + uint8_t localMemory[sizeof(__CFNumberValue)]; + __CFNumberValue localValue; + CFNumberType numType; + CFNumberType storageTypeForType; + + CF_OBJC_FUNCDISPATCH2(__kCFNumberTypeID, Boolean, number, "_getValue:forType:", valuePtr, __CFNumberGetCanonicalTypeForType(type)); + + __CFAssertIsNumber(number); + __CFAssertIsValidNumberType(type); + + storageTypeForType = __CFNumberGetStorageTypeForType(type); + type = __CFNumberGetCanonicalTypeForType(type); + if (!valuePtr) valuePtr = &localMemory; + + numType = __CFNumberGetType(number); + __CFNumberGetValue((__CFNumberValue *)&(number->value), numType, type, valuePtr); + + // If the types match, then we're fine! + if (numType == storageTypeForType) return true; + + // Test to see if the returned value is intact... + SET_VALUE(&localValue, type, valuePtr); + return __CFNumberEqualValue(&localValue, storageTypeForType, &(number->value), numType); +} + +CFComparisonResult CFNumberCompare(CFNumberRef number1, CFNumberRef number2, void *context) { + CFNumberType type1, type2; + + CF_OBJC_FUNCDISPATCH1(__kCFNumberTypeID, CFComparisonResult, number1, "compare:", number2); + CF_OBJC_FUNCDISPATCH1(__kCFNumberTypeID, CFComparisonResult, number2, "_reverseCompare:", number1); + + __CFAssertIsNumber(number1); + __CFAssertIsNumber(number2); + + type1 = __CFNumberGetType(number1); + type2 = __CFNumberGetType(number2); + + if (__CFNumberTypeIsFloat(type1) || __CFNumberTypeIsFloat(type2)) { + Float64 d1, d2; + double s1, s2; + __CFNumberGetValue(&(number1->value), type1, kCFNumberFloat64Type, &d1); + __CFNumberGetValue(&(number2->value), type2, kCFNumberFloat64Type, &d2); + s1 = copysign(1.0, d1); + s2 = copysign(1.0, d2); + if (!_CFExecutableLinkedOnOrAfter(CFSystemVersionJaguar)) { + return (d1 > d2) ? kCFCompareGreaterThan : ((d1 < d2) ? kCFCompareLessThan : kCFCompareEqualTo); + } + 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; + } else { + int64_t i1, i2; + __CFNumberGetValue(&(number1->value), type1, kCFNumberSInt64Type, &i1); + __CFNumberGetValue(&(number2->value), type2, kCFNumberSInt64Type, &i2); + return (i1 > i2) ? kCFCompareGreaterThan : ((i1 < i2) ? kCFCompareLessThan : kCFCompareEqualTo); + } +} + diff --git a/NumberDate.subproj/CFNumber.h b/NumberDate.subproj/CFNumber.h new file mode 100644 index 0000000..b83b658 --- /dev/null +++ b/NumberDate.subproj/CFNumber.h @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFNumber.h + Copyright (c) 1999-2003, Apple, Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFNUMBER__) +#define __COREFOUNDATION_CFNUMBER__ 1 + +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +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); + +typedef enum { + /* Types from MacTypes.h */ + 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, + kCFNumberMaxType = 14 +} 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); + +#if defined(__cplusplus) +} +#endif + +#endif /* ! __COREFOUNDATION_CFNUMBER__ */ + diff --git a/NumberDate.subproj/CFTimeZone.c b/NumberDate.subproj/CFTimeZone.c new file mode 100644 index 0000000..14af1dc --- /dev/null +++ b/NumberDate.subproj/CFTimeZone.c @@ -0,0 +1,1472 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFTimeZone.c + Copyright 1998-2002, Apple, Inc. All rights reserved. + Responsibility: Christopher Kane +*/ + +#include +#include +#include "CFUtilities.h" +#include "CFInternal.h" +#include +#include +#include +#if !defined(__WIN32__) +#include +#else +#include +#include +#include +#include +#endif +#include +#include +#include + +#if defined(__WIN32__) +#include +#endif + +// For Windows(TM) time zone information, see registry key: +// HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Windows NT/CurrentVersion/Time Zones + +#if defined(__WIN32__) +#define TZZONEINFO "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones" +#else +#define TZZONELINK "/etc/localtime" +#define TZZONEINFO "/usr/share/zoneinfo/" +#endif + +static CFTimeZoneRef __CFTimeZoneSystem = NULL; +static CFTimeZoneRef __CFTimeZoneDefault = NULL; +static CFDictionaryRef __CFTimeZoneAbbreviationDict = NULL; +static CFSpinLock_t __CFTimeZoneAbbreviationLock = 0; +static CFDictionaryRef __CFTimeZoneCompatibilityMappingDict = NULL; +static CFDictionaryRef __CFTimeZoneCompatibilityMappingDict2 = NULL; +static CFSpinLock_t __CFTimeZoneCompatibilityMappingLock = 0; +static CFArrayRef __CFKnownTimeZoneList = NULL; +static CFMutableDictionaryRef __CFTimeZoneCache = NULL; +static CFSpinLock_t __CFTimeZoneGlobalLock = 0; + +CF_INLINE void __CFTimeZoneLockGlobal(void) { + __CFSpinLock(&__CFTimeZoneGlobalLock); +} + +CF_INLINE void __CFTimeZoneUnlockGlobal(void) { + __CFSpinUnlock(&__CFTimeZoneGlobalLock); +} + +CF_INLINE void __CFTimeZoneLockAbbreviations(void) { + __CFSpinLock(&__CFTimeZoneAbbreviationLock); +} + +CF_INLINE void __CFTimeZoneUnlockAbbreviations(void) { + __CFSpinUnlock(&__CFTimeZoneAbbreviationLock); +} + +CF_INLINE void __CFTimeZoneLockCompatibilityMapping(void) { + __CFSpinLock(&__CFTimeZoneCompatibilityMappingLock); +} + +CF_INLINE void __CFTimeZoneUnlockCompatibilityMapping(void) { + __CFSpinUnlock(&__CFTimeZoneCompatibilityMappingLock); +} + +/* This function should be used for WIN32 instead of + * __CFCopyRecursiveDirectoryList function. + * It takes TimeZone names from the registry + * (Aleksey Dukhnyakov) + */ +#if defined(__WIN32__) +static CFMutableArrayRef __CFCopyWindowsTimeZoneList() { + CFMutableArrayRef result = NULL; + HKEY hkResult; + TCHAR lpName[MAX_PATH+1]; + DWORD dwIndex, retCode; + + if (RegOpenKey(HKEY_LOCAL_MACHINE,_T(TZZONEINFO),&hkResult) != + ERROR_SUCCESS ) + return NULL; + + result = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); + + for (dwIndex=0; (retCode = RegEnumKey(hkResult,dwIndex,lpName,MAX_PATH)) != ERROR_NO_MORE_ITEMS ; dwIndex++) { + + if (retCode != ERROR_SUCCESS) { + RegCloseKey(hkResult); + CFRelease(result); + return NULL; + } + else { +#if defined(UNICODE) + CFStringRef string = CFStringCreateWithBytes(kCFAllocatorDefault, lpName, _tcslen(lpName), kCFStringEncodingUnicode, false); +#else + CFStringRef string = CFStringCreateWithBytes(kCFAllocatorDefault, lpName, _tcslen(lpName), CFStringGetSystemEncoding(), false); +#endif + CFArrayAppendValue(result, string); + CFRelease(string); + } + } + + RegCloseKey(hkResult); + return result; +} +#endif + +static CFMutableArrayRef __CFCopyRecursiveDirectoryList(const char *topDir) { + CFMutableArrayRef result = NULL, temp; + long fd, numread, plen, basep = 0; + CFIndex idx, cnt, usedLen; + char *dirge, path[CFMaxPathSize]; + +#if !defined(__WIN32__) +// No d_namlen in dirent struct on Linux +#if defined(__LINUX__) + #define dentDNameLen strlen(dent->d_name) +#else + #define dentDNameLen dent->d_namlen +#endif + fd = open(topDir, O_RDONLY, 0); + if (fd < 0) { + return NULL; + } + dirge = malloc(8192); + result = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); + numread = getdirentries(fd, dirge, 8192, &basep); + while (0 < numread) { + struct dirent *dent = (struct dirent *)dirge; + for (; dent < (struct dirent *)(dirge + numread); dent = (struct dirent *)((char *)dent + dent->d_reclen)) { + if (0 == dent->d_fileno) continue; + if (1 == dentDNameLen && '.' == dent->d_name[0]) continue; + if (2 == dentDNameLen && '.' == dent->d_name[0] && '.' == dent->d_name[1]) continue; + if (dent->d_type == DT_UNKNOWN) { + struct stat statbuf; + strcpy(path, topDir); + strcat(path, "/"); + plen = strlen(path); + memmove(path + plen, dent->d_name, dentDNameLen); + path[plen + dentDNameLen] = '\0'; + if (0 <= stat(path, &statbuf) && (statbuf.st_mode & S_IFMT) == S_IFDIR) { + dent->d_type = DT_DIR; + } + } + if (DT_DIR == dent->d_type) { + strcpy(path, topDir); + strcat(path, "/"); + plen = strlen(path); + memmove(path + plen, dent->d_name, dentDNameLen); + path[plen + dentDNameLen] = '\0'; + temp = __CFCopyRecursiveDirectoryList(path); + for (idx = 0, cnt = CFArrayGetCount(temp); idx < cnt; idx++) { + CFStringRef string, item = CFArrayGetValueAtIndex(temp, idx); + memmove(path, dent->d_name, dentDNameLen); + path[dentDNameLen] = '/'; + CFStringGetBytes(item, CFRangeMake(0, CFStringGetLength(item)), kCFStringEncodingUTF8, 0, false, path + dentDNameLen + 1, CFMaxPathLength - dentDNameLen - 2, &usedLen); + string = CFStringCreateWithBytes(kCFAllocatorDefault, path, dentDNameLen + 1 + usedLen, kCFStringEncodingUTF8, false); + CFArrayAppendValue(result, string); + CFRelease(string); + } + CFRelease(temp); + } else { + CFStringRef string = CFStringCreateWithBytes(kCFAllocatorDefault, dent->d_name, dentDNameLen, kCFStringEncodingUTF8, false); + CFArrayAppendValue(result, string); + CFRelease(string); + } + } + numread = getdirentries(fd, dirge, 8192, &basep); + } + close(fd); + free(dirge); + if (-1 == numread) { + CFRelease(result); + return NULL; + } +#endif + return result; +} + +typedef struct _CFTZPeriod { + int32_t startSec; + CFStringRef abbrev; + uint32_t info; +} CFTZPeriod; + +struct __CFTimeZone { + CFRuntimeBase _base; + CFStringRef _name; /* immutable */ + CFDataRef _data; /* immutable */ + CFTZPeriod *_periods; /* immutable */ + int32_t _periodCnt; /* immutable */ +}; + +/* startSec is the whole integer seconds from a CFAbsoluteTime, giving dates + * between 1933 and 2069; info outside these years is discarded on read-in */ +/* Bits 31-18 of the info are unused */ +/* Bit 17 of the info is used for the is-DST state */ +/* Bit 16 of the info is used for the sign of the offset (1 == negative) */ +/* Bits 15-0 of the info are used for abs(offset) in seconds from GMT */ + +CF_INLINE void __CFTZPeriodInit(CFTZPeriod *period, int32_t startTime, CFStringRef abbrev, int32_t offset, Boolean isDST) { + period->startSec = startTime; + period->abbrev = abbrev ? CFRetain(abbrev) : NULL; + __CFBitfieldSetValue(period->info, 15, 0, abs(offset)); + __CFBitfieldSetValue(period->info, 16, 16, (offset < 0 ? 1 : 0)); + __CFBitfieldSetValue(period->info, 17, 17, (isDST ? 1 : 0)); +} + +CF_INLINE int32_t __CFTZPeriodStartSeconds(const CFTZPeriod *period) { + return period->startSec; +} + +CF_INLINE CFStringRef __CFTZPeriodAbbreviation(const CFTZPeriod *period) { + return period->abbrev; +} + +CF_INLINE int32_t __CFTZPeriodGMTOffset(const CFTZPeriod *period) { + int32_t v = __CFBitfieldGetValue(period->info, 15, 0); + if (__CFBitfieldGetValue(period->info, 16, 16)) v = -v; + return v; +} + +CF_INLINE Boolean __CFTZPeriodIsDST(const CFTZPeriod *period) { + return (Boolean)__CFBitfieldGetValue(period->info, 17, 17); +} + +static CFComparisonResult __CFCompareTZPeriods(const void *val1, const void *val2, void *context) { + CFTZPeriod *tzp1 = (CFTZPeriod *)val1; + CFTZPeriod *tzp2 = (CFTZPeriod *)val2; + // we treat equal as less than, as the code which uses the + // result of the bsearch doesn't expect exact matches + // (they're pretty rare, so no point in over-coding for them) + if (__CFTZPeriodStartSeconds(tzp1) <= __CFTZPeriodStartSeconds(tzp2)) return kCFCompareLessThan; + return kCFCompareGreaterThan; +} + +CF_INLINE CFIndex __CFBSearchTZPeriods(CFTimeZoneRef tz, CFAbsoluteTime at) { + CFTZPeriod elem; + CFIndex idx; + __CFTZPeriodInit(&elem, (int32_t)(float)floor(at), NULL, 0, false); + idx = CFBSearch(&elem, sizeof(CFTZPeriod), tz->_periods, tz->_periodCnt, __CFCompareTZPeriods, NULL); + if (tz->_periodCnt <= idx) { + idx = tz->_periodCnt; + } else if (0 == idx) { + // We want anything before the time zone records start to be not in DST; + // we assume that if period[0] is DST, then period[1] is not; could do a search instead. + idx = __CFTZPeriodIsDST(&(tz->_periods[0])) ? 2 : 1; + } + return idx - 1; +} + +/* +** Each time zone data file begins with. . . +*/ + +struct tzhead { + char tzh_reserved[20]; /* reserved for future use */ + char tzh_ttisgmtcnt[4]; /* coded number of trans. time flags */ + char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */ + char tzh_leapcnt[4]; /* coded number of leap seconds */ + char tzh_timecnt[4]; /* coded number of transition times */ + char tzh_typecnt[4]; /* coded number of local time types */ + char tzh_charcnt[4]; /* coded number of abbr. chars */ +}; + +/* +** . . .followed by. . . +** +** tzh_timecnt (char [4])s coded transition times a la time(2) +** tzh_timecnt (UInt8)s types of local time starting at above +** tzh_typecnt repetitions of +** one (char [4]) coded GMT offset in seconds +** one (UInt8) used to set tm_isdst +** one (UInt8) that's an abbreviation list index +** tzh_charcnt (char)s '\0'-terminated zone abbreviations +** tzh_leapcnt repetitions of +** one (char [4]) coded leap second transition times +** one (char [4]) total correction after above +** tzh_ttisstdcnt (char)s indexed by type; if 1, transition +** time is standard time, if 0, +** transition time is wall clock time +** if absent, transition times are +** assumed to be wall clock time +** tzh_ttisgmtcnt (char)s indexed by type; if 1, transition +** time is GMT, if 0, +** transition time is local time +** if absent, transition times are +** assumed to be local time +*/ + +CF_INLINE int32_t __CFDetzcode(const unsigned char *bufp) { + int32_t result = (bufp[0] & 0x80) ? ~0L : 0L; + result = (result << 8) | (bufp[0] & 0xff); + result = (result << 8) | (bufp[1] & 0xff); + result = (result << 8) | (bufp[2] & 0xff); + result = (result << 8) | (bufp[3] & 0xff); + return result; +} + +CF_INLINE void __CFEntzcode(int32_t value, unsigned char *bufp) { + bufp[0] = (value >> 24) & 0xff; + bufp[1] = (value >> 16) & 0xff; + bufp[2] = (value >> 8) & 0xff; + bufp[3] = (value >> 0) & 0xff; +} + +static Boolean __CFParseTimeZoneData(CFAllocatorRef allocator, CFDataRef data, CFTZPeriod **tzpp, CFIndex *cntp) { +#if !defined(__WIN32__) + int32_t len, timecnt, typecnt, charcnt, idx, cnt; + const char *p, *timep, *typep, *ttisp, *charp; + CFStringRef *abbrs; + Boolean result = true; + + p = CFDataGetBytePtr(data); + len = CFDataGetLength(data); + if (len < (int32_t)sizeof(struct tzhead)) { + return false; + } + p += 20 + 4 + 4 + 4; /* skip reserved, ttisgmtcnt, ttisstdcnt, leapcnt */ + timecnt = __CFDetzcode(p); + p += 4; + typecnt = __CFDetzcode(p); + p += 4; + charcnt = __CFDetzcode(p); + p += 4; + if (typecnt <= 0 || timecnt < 0 || charcnt < 0) { + return false; + } + if (len - (int32_t)sizeof(struct tzhead) < (4 + 1) * timecnt + (4 + 1 + 1) * typecnt + charcnt) { + return false; + } + timep = p; + typep = timep + 4 * timecnt; + ttisp = typep + timecnt; + charp = ttisp + (4 + 1 + 1) * typecnt; + cnt = (0 < timecnt) ? timecnt : 1; + *tzpp = CFAllocatorAllocate(allocator, cnt * sizeof(CFTZPeriod), 0); + if (__CFOASafe) __CFSetLastAllocationEventName(*tzpp, "CFTimeZone (store)"); + memset(*tzpp, 0, cnt * sizeof(CFTZPeriod)); + abbrs = CFAllocatorAllocate(allocator, (charcnt + 1) * sizeof(CFStringRef), 0); + if (__CFOASafe) __CFSetLastAllocationEventName(*tzpp, "CFTimeZone (temp)"); + for (idx = 0; idx < charcnt + 1; idx++) { + abbrs[idx] = NULL; + } + for (idx = 0; idx < cnt; idx++) { + CFAbsoluteTime at; + int32_t itime, offset; + uint8_t type, dst, abbridx; + + at = (CFAbsoluteTime)(__CFDetzcode(timep) + 0.0) - kCFAbsoluteTimeIntervalSince1970; + if (0 == timecnt) itime = INT_MIN; + else if (at < (CFAbsoluteTime)INT_MIN) itime = INT_MIN; + else if ((CFAbsoluteTime)INT_MAX < at) itime = INT_MAX; + else itime = (int32_t)at; + timep += 4; /* harmless if 0 == timecnt */ + type = (0 < timecnt) ? (uint8_t)*typep++ : 0; + if (typecnt <= type) { + result = false; + break; + } + offset = __CFDetzcode(ttisp + 6 * type); + dst = (uint8_t)*(ttisp + 6 * type + 4); + if (0 != dst && 1 != dst) { + result = false; + break; + } + abbridx = (uint8_t)*(ttisp + 6 * type + 5); + if (charcnt < abbridx) { + result = false; + break; + } + if (NULL == abbrs[abbridx]) { + abbrs[abbridx] = CFStringCreateWithCString(allocator, &charp[abbridx], kCFStringEncodingASCII); + } + __CFTZPeriodInit(*tzpp + idx, itime, abbrs[abbridx], offset, (dst ? true : false)); + } + for (idx = 0; idx < charcnt + 1; idx++) { + if (NULL != abbrs[idx]) { + CFRelease(abbrs[idx]); + } + } + CFAllocatorDeallocate(allocator, abbrs); + if (result) { + // dump all but the last INT_MIN and the first INT_MAX + for (idx = 0; idx < cnt; idx++) { + if (((*tzpp + idx)->startSec == INT_MIN) && (idx + 1 < cnt) && (((*tzpp + idx + 1)->startSec == INT_MIN))) { + cnt--; + memmove((*tzpp + idx), (*tzpp + idx + 1), sizeof(CFTZPeriod) * (cnt - idx)); + idx--; + } + } + // Don't combine these loops! Watch the idx decrementing... + for (idx = 0; idx < cnt; idx++) { + if (((*tzpp + idx)->startSec == INT_MAX) && (0 < idx) && (((*tzpp + idx - 1)->startSec == INT_MAX))) { + cnt--; + memmove((*tzpp + idx), (*tzpp + idx + 1), sizeof(CFTZPeriod) * (cnt - idx)); + idx--; + } + } + CFQSortArray(*tzpp, cnt, sizeof(CFTZPeriod), __CFCompareTZPeriods, NULL); + *cntp = cnt; + } else { + CFAllocatorDeallocate(allocator, *tzpp); + *tzpp = NULL; + } + return result; +#else +/* We use Win32 function to find TimeZone + * (Aleksey Dukhnyakov) + */ + *tzpp = CFAllocatorAllocate(allocator, sizeof(CFTZPeriod), 0); + __CFTZPeriodInit(*tzpp, 0, NULL, 0, false); + *cntp = 1; + return TRUE; +#endif +} + +static Boolean __CFTimeZoneEqual(CFTypeRef cf1, CFTypeRef cf2) { + CFTimeZoneRef tz1 = (CFTimeZoneRef)cf1; + CFTimeZoneRef tz2 = (CFTimeZoneRef)cf2; + if (!CFEqual(CFTimeZoneGetName(tz1), CFTimeZoneGetName(tz2))) return false; + if (!CFEqual(CFTimeZoneGetData(tz1), CFTimeZoneGetData(tz2))) return false; + return true; +} + +static CFHashCode __CFTimeZoneHash(CFTypeRef cf) { + CFTimeZoneRef tz = (CFTimeZoneRef)cf; + return CFHash(CFTimeZoneGetName(tz)); +} + +static CFStringRef __CFTimeZoneCopyDescription(CFTypeRef cf) { + CFTimeZoneRef tz = (CFTimeZoneRef)cf; + CFStringRef result, abbrev; + CFAbsoluteTime at; + at = CFAbsoluteTimeGetCurrent(); + abbrev = CFTimeZoneCopyAbbreviation(tz, at); + result = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("{name = %@; abbreviation = %@; GMT offset = %g; is DST = %s}"), cf, CFGetAllocator(tz), tz->_name, abbrev, CFTimeZoneGetSecondsFromGMT(tz, at), CFTimeZoneIsDaylightSavingTime(tz, at) ? "true" : "false"); + CFRelease(abbrev); + return result; +} + +static void __CFTimeZoneDeallocate(CFTypeRef cf) { + CFTimeZoneRef tz = (CFTimeZoneRef)cf; + CFAllocatorRef allocator = CFGetAllocator(tz); + CFIndex idx; + if (tz->_name) CFRelease(tz->_name); + if (tz->_data) CFRelease(tz->_data); + for (idx = 0; idx < tz->_periodCnt; idx++) { + if (NULL != tz->_periods[idx].abbrev) CFRelease(tz->_periods[idx].abbrev); + } + if (NULL != tz->_periods) CFAllocatorDeallocate(allocator, tz->_periods); +} + +static CFTypeID __kCFTimeZoneTypeID = _kCFRuntimeNotATypeID; + +static const CFRuntimeClass __CFTimeZoneClass = { + 0, + "CFTimeZone", + NULL, // init + NULL, // copy + __CFTimeZoneDeallocate, + __CFTimeZoneEqual, + __CFTimeZoneHash, + NULL, // + __CFTimeZoneCopyDescription +}; + +__private_extern__ void __CFTimeZoneInitialize(void) { + __kCFTimeZoneTypeID = _CFRuntimeRegisterClass(&__CFTimeZoneClass); +} + +CFTypeID CFTimeZoneGetTypeID(void) { + return __kCFTimeZoneTypeID; +} + +static CFTimeZoneRef __CFTimeZoneCreateSystem(void) { + CFTimeZoneRef result = NULL; +#if defined(__WIN32__) +/* The GetTimeZoneInformation function retrieves the current + * time-zone parameters for Win32 + * (Aleksey Dukhnyakov) + */ + CFDataRef data; + TIME_ZONE_INFORMATION tz; + DWORD dw_result; + dw_result=GetTimeZoneInformation(&tz); + + if ( dw_result == TIME_ZONE_ID_STANDARD || + dw_result == TIME_ZONE_ID_DAYLIGHT ) { + CFStringRef name = CFStringCreateWithCharacters(kCFAllocatorDefault, tz.StandardName, wcslen(tz.StandardName)); + data = CFDataCreate(kCFAllocatorDefault, (UInt8*)&tz, sizeof(tz)); + result = CFTimeZoneCreate(kCFAllocatorSystemDefault, name, data); + CFRelease(name); + CFRelease(data); + if (result) return result; + } +#else + char *tzenv; + int ret; + char linkbuf[CFMaxPathSize]; + + tzenv = getenv("TZFILE"); + if (NULL != tzenv) { + CFStringRef name = CFStringCreateWithBytes(kCFAllocatorDefault, tzenv, strlen(tzenv), kCFStringEncodingUTF8, false); + result = CFTimeZoneCreateWithName(kCFAllocatorSystemDefault, name, false); + CFRelease(name); + if (result) return result; + } + tzenv = getenv("TZ"); + if (NULL != tzenv) { + CFStringRef name = CFStringCreateWithBytes(kCFAllocatorDefault, tzenv, strlen(tzenv), kCFStringEncodingUTF8, false); + result = CFTimeZoneCreateWithName(kCFAllocatorSystemDefault, name, true); + CFRelease(name); + if (result) return result; + } + ret = readlink(TZZONELINK, linkbuf, sizeof(linkbuf)); + if (0 < ret) { + CFStringRef name; + linkbuf[ret] = '\0'; + if (strncmp(linkbuf, TZZONEINFO, sizeof(TZZONEINFO) - 1) == 0) { + name = CFStringCreateWithBytes(kCFAllocatorDefault, linkbuf + sizeof(TZZONEINFO) - 1, strlen(linkbuf) - sizeof(TZZONEINFO) + 1, kCFStringEncodingUTF8, false); + } else { + name = CFStringCreateWithBytes(kCFAllocatorDefault, linkbuf, strlen(linkbuf), kCFStringEncodingUTF8, false); + } + result = CFTimeZoneCreateWithName(kCFAllocatorSystemDefault, name, false); + CFRelease(name); + if (result) return result; + } +#endif + return CFTimeZoneCreateWithTimeIntervalFromGMT(kCFAllocatorSystemDefault, 0.0); +} + +CFTimeZoneRef CFTimeZoneCopySystem(void) { + CFTimeZoneRef tz; + __CFTimeZoneLockGlobal(); + if (NULL == __CFTimeZoneSystem) { + __CFTimeZoneUnlockGlobal(); + tz = __CFTimeZoneCreateSystem(); + __CFTimeZoneLockGlobal(); + if (NULL == __CFTimeZoneSystem) { + __CFTimeZoneSystem = tz; + } else { + if (tz) CFRelease(tz); + } + } + tz = __CFTimeZoneSystem ? CFRetain(__CFTimeZoneSystem) : NULL; + __CFTimeZoneUnlockGlobal(); + return tz; +} + +void CFTimeZoneResetSystem(void) { + __CFTimeZoneLockGlobal(); + if (__CFTimeZoneDefault == __CFTimeZoneSystem) { + if (__CFTimeZoneDefault) CFRelease(__CFTimeZoneDefault); + __CFTimeZoneDefault = NULL; + } + if (__CFTimeZoneSystem) CFRelease(__CFTimeZoneSystem); + __CFTimeZoneSystem = NULL; + __CFTimeZoneUnlockGlobal(); +} + +CFTimeZoneRef CFTimeZoneCopyDefault(void) { + CFTimeZoneRef tz; + __CFTimeZoneLockGlobal(); + if (NULL == __CFTimeZoneDefault) { + __CFTimeZoneUnlockGlobal(); + tz = CFTimeZoneCopySystem(); + __CFTimeZoneLockGlobal(); + if (NULL == __CFTimeZoneDefault) { + __CFTimeZoneDefault = tz; + } else { + if (tz) CFRelease(tz); + } + } + tz = __CFTimeZoneDefault ? CFRetain(__CFTimeZoneDefault) : NULL; + __CFTimeZoneUnlockGlobal(); + return tz; +} + +void CFTimeZoneSetDefault(CFTimeZoneRef tz) { + __CFGenericValidateType(tz, __kCFTimeZoneTypeID); + __CFTimeZoneLockGlobal(); + if (tz != __CFTimeZoneDefault) { + if (tz) CFRetain(tz); + if (__CFTimeZoneDefault) CFRelease(__CFTimeZoneDefault); + __CFTimeZoneDefault = tz; + } + __CFTimeZoneUnlockGlobal(); +} + +static CFDictionaryRef __CFTimeZoneCopyCompatibilityDictionary(void); + +CFArrayRef CFTimeZoneCopyKnownNames(void) { + CFArrayRef tzs; + __CFTimeZoneLockGlobal(); + if (NULL == __CFKnownTimeZoneList) { + CFMutableArrayRef list; +/* TimeZone information locate in the registry for Win32 + * (Aleksey Dukhnyakov) + */ +#if !defined(__WIN32__) + list = __CFCopyRecursiveDirectoryList(TZZONEINFO); +#else + list = __CFCopyWindowsTimeZoneList(); +#endif + // Remove undesirable ancient cruft + CFDictionaryRef dict = __CFTimeZoneCopyCompatibilityDictionary(); + CFIndex idx; + for (idx = CFArrayGetCount(list); idx--; ) { + CFStringRef item = CFArrayGetValueAtIndex(list, idx); + if (CFDictionaryContainsKey(dict, item)) { + CFArrayRemoveValueAtIndex(list, idx); + } + } + __CFKnownTimeZoneList = CFArrayCreateCopy(kCFAllocatorSystemDefault, list); + CFRelease(list); + } + tzs = __CFKnownTimeZoneList ? CFRetain(__CFKnownTimeZoneList) : NULL; + __CFTimeZoneUnlockGlobal(); + return tzs; +} + +static const unsigned char *__CFTimeZoneAbbreviationDefaults = +#if defined(__WIN32__) +/* + * TimeZone abbreviations for Win32 + * (Andrew Dzubandovsky) + * + */ +"" +" " +" " +" " +" AFG Afghanistan Standard Time" +" ALS Alaskan Standard Time" +" ARA Arab Standard Time" +" ARB Arabian Standard Time" +" ARC Arabic Standard Time" +" ATL Atlantic Standard Time" +" ASC AUS Central Standard Time" +" ASE AUS Eastern Standard Time" +" AZS Azores Standard Time" +" CND Canada Central Standard Time" +" CPV Cape Verde Standard Time" +" CCS Caucasus Standard Time" +" CNAS Cen. Australia Standard Time" +" CAMR Central America Standard Time" +" CAS Central Asia Standard Time" +" CER Central Europe Standard Time" +" CEPN Central European Standard Time" +" CPC Central Pacific Standard Time" +" CSTD Central Standard Time" +" CHN China Standard Time" +" DTLN Dateline Standard Time" +" EAFR E. Africa Standard Time" +" EAS E. Australia Standard Time" +" ERP E. Europe Standard Time" +" ESTH E. South America Standard Time" +" ESTM Eastern Standard Time" +" EGP Egypt Standard Time" +" EKT Ekaterinburg Standard Time" +" FST Fiji Standard Time" +" FLE FLE Standard Time" +" GMT GMT Standard Time" +" GRLD Greenland Standard Time" +" GRW Greenwich Standard Time" +" GTB GTB Standard Time" +" HWT Hawaiian Standard Time" +" INT India Standard Time" +" IRT Iran Standard Time" +" ISL Israel Standard Time" +" KRT Korea Standard Time" +" MXST Mexico Standard Time" +" MTL Mid-Atlantic Standard Time" +" MNT Mountain Standard Time" +" MNM Myanmar Standard Time" +" NCNA N. Central Asia Standard Time" +" MPL Nepal Standard Time" +" NWZ New Zealand Standard Time" +" NWF Newfoundland Standard Time" +" NTAE North Asia East Standard Time" +" NTAS North Asia Standard Time" +" HSAT Pacific SA Standard Time" +" PST Pacific Standard Time" +" RMC Romance Standard Time" +" MSK Russian Standard Time" +" SSS SA Eastern Standard Time" +" SPS SA Pacific Standard Time" +" SWS SA Western Standard Time" +" SMS Samoa Standard Time" +" SAS SE Asia Standard Time" +" SNG Singapore Standard Time" +" STAF South Africa Standard Time" +" SRLK Sri Lanka Standard Time" +" TPS Taipei Standard Time" +" TSM Tasmania Standard Time" +" JPN Tokyo Standard Time" +" TNG Tonga Standard Time" +" AEST US Eastern Standard Time" +" AMST US Mountain Standard Time" +" VLD Vladivostok Standard Time" +" AUSW W. Australia Standard Time" +" AFCW W. Central Africa Standard Time" +" EWS W. Europe Standard Time" +" ASW West Asia Standard Time" +" PWS West Pacific Standard Time" +" RKS Yakutsk Standard Time" +" " +" "; +#else +"" +" " +" " +" " +" ADT America/Halifax" +" AFT Asia/Kabul" +" AKDT America/Juneau" +" AKST America/Juneau" +" AST America/Halifax" +" CDT America/Chicago" +" CEST Europe/Rome" +" CET Europe/Rome" +" CST America/Chicago" +" EDT America/New_York" +" EEST Europe/Warsaw" +" EET Europe/Warsaw" +" EST America/New_York" +" GMT GMT" +" HKST Asia/Hong_Kong" +" HST Pacific/Honolulu" +" JST Asia/Tokyo" +" MDT America/Denver" +" MSD Europe/Moscow" +" MSK Europe/Moscow" +" MST America/Denver" +" NZDT Pacific/Auckland" +" NZST Pacific/Auckland" +" PDT America/Los_Angeles" +" PST America/Los_Angeles" +" UTC UTC" +" WEST Europe/Paris" +" WET Europe/Paris" +" YDT America/Yakutat" +" YST America/Yakutat" +" " +" "; +#endif + +CFDictionaryRef CFTimeZoneCopyAbbreviationDictionary(void) { + CFDictionaryRef dict; + __CFTimeZoneLockAbbreviations(); + if (NULL == __CFTimeZoneAbbreviationDict) { + CFDataRef data = CFDataCreate(kCFAllocatorDefault, __CFTimeZoneAbbreviationDefaults, strlen(__CFTimeZoneAbbreviationDefaults)); + __CFTimeZoneAbbreviationDict = CFPropertyListCreateFromXMLData(kCFAllocatorDefault, data, kCFPropertyListImmutable, NULL); + CFRelease(data); + } + if (NULL == __CFTimeZoneAbbreviationDict) { + __CFTimeZoneAbbreviationDict = CFDictionaryCreate(kCFAllocatorDefault, NULL, NULL, 0, NULL, NULL); + } + dict = __CFTimeZoneAbbreviationDict ? CFRetain(__CFTimeZoneAbbreviationDict) : NULL; + __CFTimeZoneUnlockAbbreviations(); + return dict; +} + +void CFTimeZoneSetAbbreviationDictionary(CFDictionaryRef dict) { + __CFGenericValidateType(dict, CFDictionaryGetTypeID()); + __CFTimeZoneLockGlobal(); + if (dict != __CFTimeZoneAbbreviationDict) { + if (dict) CFRetain(dict); + if (__CFTimeZoneAbbreviationDict) CFRelease(__CFTimeZoneAbbreviationDict); + __CFTimeZoneAbbreviationDict = dict; + } + __CFTimeZoneUnlockGlobal(); +} + +CFTimeZoneRef CFTimeZoneCreate(CFAllocatorRef allocator, CFStringRef name, CFDataRef data) { +// assert: (NULL != name && NULL != data); + CFTimeZoneRef memory; + uint32_t size; + CFTZPeriod *tzp; + CFIndex idx, cnt; + + if (allocator == NULL) allocator = __CFGetDefaultAllocator(); + __CFGenericValidateType(allocator, CFAllocatorGetTypeID()); + __CFGenericValidateType(name, CFStringGetTypeID()); + __CFGenericValidateType(data, CFDataGetTypeID()); + __CFTimeZoneLockGlobal(); + if (NULL != __CFTimeZoneCache && CFDictionaryGetValueIfPresent(__CFTimeZoneCache, name, (const void **)&memory)) { + __CFTimeZoneUnlockGlobal(); + return (CFTimeZoneRef)CFRetain(memory); + } + if (!__CFParseTimeZoneData(allocator, data, &tzp, &cnt)) { + __CFTimeZoneUnlockGlobal(); + return NULL; + } + size = sizeof(struct __CFTimeZone) - sizeof(CFRuntimeBase); + memory = _CFRuntimeCreateInstance(allocator, __kCFTimeZoneTypeID, size, NULL); + if (NULL == memory) { + __CFTimeZoneUnlockGlobal(); + for (idx = 0; idx < cnt; idx++) { + if (NULL != tzp[idx].abbrev) CFRelease(tzp[idx].abbrev); + } + if (NULL != tzp) CFAllocatorDeallocate(allocator, tzp); + return NULL; + } + ((struct __CFTimeZone *)memory)->_name = CFRetain(name); + ((struct __CFTimeZone *)memory)->_data = CFRetain(data); + ((struct __CFTimeZone *)memory)->_periods = tzp; + ((struct __CFTimeZone *)memory)->_periodCnt = cnt; + if (NULL == __CFTimeZoneCache) { + __CFTimeZoneCache = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + } + CFDictionaryAddValue(__CFTimeZoneCache, name, memory); + __CFTimeZoneUnlockGlobal(); + return memory; +} + +static CFTimeZoneRef __CFTimeZoneCreateFixed(CFAllocatorRef allocator, int32_t seconds, CFStringRef name, int isDST) { + CFTimeZoneRef result; + CFDataRef data; + int32_t nameLen = CFStringGetLength(name); +#if defined(__WIN32__) + unsigned char *dataBytes = CFAllocatorAllocate(allocator, 52 + nameLen + 1, 0); + if (!dataBytes) return NULL; + if (__CFOASafe) __CFSetLastAllocationEventName(*tzpp, "CFTimeZone (temp)"); +#else + unsigned char dataBytes[52 + nameLen + 1]; +#endif + memset(dataBytes, 0, sizeof(dataBytes)); + __CFEntzcode(1, dataBytes + 20); + __CFEntzcode(1, dataBytes + 24); + __CFEntzcode(1, dataBytes + 36); + __CFEntzcode(nameLen + 1, dataBytes + 40); + __CFEntzcode(seconds, dataBytes + 44); + dataBytes[48] = isDST ? 1 : 0; + CFStringGetCString(name, dataBytes + 50, nameLen + 1, kCFStringEncodingASCII); + data = CFDataCreate(allocator, dataBytes, 52 + nameLen + 1); + result = CFTimeZoneCreate(allocator, name, data); + CFRelease(data); +#if defined(__WIN32__) + CFAllocatorDeallocate(allocator, dataBytes); +#endif + return result; +} + +// rounds offset to nearest minute +CFTimeZoneRef CFTimeZoneCreateWithTimeIntervalFromGMT(CFAllocatorRef allocator, CFTimeInterval ti) { + CFTimeZoneRef result; + CFStringRef name; + int32_t seconds, minute, hour; + if (allocator == NULL) allocator = __CFGetDefaultAllocator(); + __CFGenericValidateType(allocator, CFAllocatorGetTypeID()); + if (ti < -18.0 * 3600 || 18.0 * 3600 < ti) return NULL; + ti = (ti < 0.0) ? ceil((ti / 60.0) - 0.5) * 60.0 : floor((ti / 60.0) + 0.5) * 60.0; + seconds = (int32_t)ti; + hour = (ti < 0) ? (-seconds / 3600) : (seconds / 3600); + seconds -= ((ti < 0) ? -hour : hour) * 3600; + minute = (ti < 0) ? (-seconds / 60) : (seconds / 60); + if (fabs(ti) < 1.0) { + name = CFRetain(CFSTR("GMT")); + } else { + name = CFStringCreateWithFormat(allocator, NULL, CFSTR("GMT%c%02d%02d"), (ti < 0.0 ? '-' : '+'), hour, minute); + } +#if !defined(__WIN32__) + result = __CFTimeZoneCreateFixed(allocator, (int32_t)ti, name, 0); +#else +/* CFTimeZoneRef->_data will contain TIME_ZONE_INFORMATION structure + * to find current timezone + * (Aleksey Dukhnyakov) + */ + { + TIME_ZONE_INFORMATION tzi; + CFDataRef data; + CFIndex length = CFStringGetLength(name); + + memset(&tzi,0,sizeof(tzi)); + tzi.Bias=(long)(-ti/60); + CFStringGetCharacters(name, CFRangeMake(0, length < 31 ? length : 31 ), tzi.StandardName); + data = CFDataCreate(allocator,(UInt8*)&tzi, sizeof(tzi)); + result = CFTimeZoneCreate(allocator, name, data); + CFRelease(data); + } +#endif + CFRelease(name); + return result; +} + +CFTimeZoneRef CFTimeZoneCreateWithName(CFAllocatorRef allocator, CFStringRef name, Boolean tryAbbrev) { + CFTimeZoneRef result = NULL; + CFStringRef tzName = NULL; + CFDataRef data = NULL; + CFURLRef baseURL, tempURL; + void *bytes; + CFIndex length; + + if (allocator == NULL) allocator = __CFGetDefaultAllocator(); + __CFGenericValidateType(allocator, CFAllocatorGetTypeID()); + __CFGenericValidateType(name, CFStringGetTypeID()); + if (CFEqual(CFSTR(""), name)) { + // empty string is not a time zone name, just abort now, + // following stuff will fail anyway + return NULL; + } + __CFTimeZoneLockGlobal(); + if (NULL != __CFTimeZoneCache && CFDictionaryGetValueIfPresent(__CFTimeZoneCache, name, (const void **)&result)) { + __CFTimeZoneUnlockGlobal(); + return (CFTimeZoneRef)CFRetain(result); + } + __CFTimeZoneUnlockGlobal(); +#if !defined(__WIN32__) + baseURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, CFSTR(TZZONEINFO), kCFURLPOSIXPathStyle, true); + if (tryAbbrev) { + CFDictionaryRef abbrevs = CFTimeZoneCopyAbbreviationDictionary(); + tzName = CFDictionaryGetValue(abbrevs, name); + if (NULL != tzName) { + tempURL = CFURLCreateCopyAppendingPathComponent(kCFAllocatorDefault, baseURL, tzName, false); + if (NULL != tempURL) { + if (_CFReadBytesFromFile(kCFAllocatorDefault, tempURL, &bytes, &length, 0)) { + data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, bytes, length, kCFAllocatorDefault); + } + CFRelease(tempURL); + } + } + CFRelease(abbrevs); + } + if (NULL == data) { + CFDictionaryRef dict = __CFTimeZoneCopyCompatibilityDictionary(); + CFStringRef mapping = CFDictionaryGetValue(dict, name); + if (mapping) { + name = mapping; + } else if (CFStringHasPrefix(name, CFSTR(TZZONEINFO))) { + CFMutableStringRef unprefixed = CFStringCreateMutableCopy(kCFAllocatorDefault, CFStringGetLength(name), name); + CFStringDelete(unprefixed, CFRangeMake(0, sizeof(TZZONEINFO))); + mapping = CFDictionaryGetValue(dict, unprefixed); + if (mapping) { + name = mapping; + } + CFRelease(unprefixed); + } + CFRelease(dict); + if (CFEqual(CFSTR(""), name)) { + return NULL; + } + } + if (NULL == data) { + tzName = name; + tempURL = CFURLCreateCopyAppendingPathComponent(kCFAllocatorDefault, baseURL, tzName, false); + if (NULL != tempURL) { + if (_CFReadBytesFromFile(kCFAllocatorDefault, tempURL, &bytes, &length, 0)) { + data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, bytes, length, kCFAllocatorDefault); + } + CFRelease(tempURL); + } + } + CFRelease(baseURL); + if (NULL == data) { + tzName = name; + tempURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, tzName, kCFURLPOSIXPathStyle, false); + if (NULL != tempURL) { + if (_CFReadBytesFromFile(kCFAllocatorDefault, tempURL, &bytes, &length, 0)) { + data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, bytes, length, kCFAllocatorDefault); + } + CFRelease(tempURL); + } + } + if (NULL != data) { + result = CFTimeZoneCreate(allocator, tzName, data); + CFRelease(data); + } +#else +/* Reading GMT offset and daylight flag from the registry + * for TimeZone name + * (Aleksey Dukhnyakov) + */ + { + CFStringRef safeName = name; + struct { + LONG Bias; + LONG StandardBias; + LONG DaylightBias; + SYSTEMTIME StandardDate; + SYSTEMTIME DaylightDate; + } tzi; + TIME_ZONE_INFORMATION tzi_system; + + HKEY hkResult; + DWORD dwType, dwSize=sizeof(tzi), + dwSize_name1=sizeof(tzi_system.StandardName), + dwSize_name2=sizeof(tzi_system.DaylightName); + + if (tryAbbrev) { + CFDictionaryRef abbrevs = CFTimeZoneCopyAbbreviationDictionary(); + tzName = CFDictionaryGetValue(abbrevs, name); + if (NULL == tzName) { + return NULL; + } + name = tzName; + CFRelease(abbrevs); + } + +/* Open regestry and move down to the TimeZone information + */ + if (RegOpenKey(HKEY_LOCAL_MACHINE,_T(TZZONEINFO),&hkResult) != + ERROR_SUCCESS ) { + return NULL; + } +/* Move down to specific TimeZone name + */ +#if defined(UNICODE) + if (RegOpenKey(hkResult,CFStringGetCharactersPtr(name) ,&hkResult) != + ERROR_SUCCESS ) { +#else + if (RegOpenKey(hkResult,CFStringGetCStringPtr(name, CFStringGetSystemEncoding()),&hkResult) != ERROR_SUCCESS ) { +#endif + return NULL; + } +/* TimeZone information(offsets, daylight flag, ...) assign to tzi structure + */ + if ( RegQueryValueEx(hkResult,_T("TZI"),NULL,&dwType,(LPBYTE)&tzi,&dwSize) != ERROR_SUCCESS && + RegQueryValueEx(hkResult,_T("Std"),NULL,&dwType,(LPBYTE)&tzi_system.StandardName,&dwSize_name1) != ERROR_SUCCESS && + RegQueryValueEx(hkResult,_T("Dlt"),NULL,&dwType,(LPBYTE)&tzi_system.DaylightName,&dwSize_name2) != ERROR_SUCCESS ) + { + return NULL; + } + + tzi_system.Bias=tzi.Bias; + tzi_system.StandardBias=tzi.StandardBias; + tzi_system.DaylightBias=tzi.DaylightBias; + tzi_system.StandardDate=tzi.StandardDate; + tzi_system.DaylightDate=tzi.DaylightDate; + +/* CFTimeZoneRef->_data will contain TIME_ZONE_INFORMATION structure + * to find current timezone + * (Aleksey Dukhnyakov) + */ + data = CFDataCreate(allocator,(UInt8*)&tzi_system, sizeof(tzi_system)); + + RegCloseKey(hkResult); + result = CFTimeZoneCreate(allocator, name, data); + if (result) { + if (tryAbbrev) + result->_periods->abbrev = CFStringCreateCopy(allocator,safeName); + else { + } + } + CFRelease(data); + } +#endif + return result; +} + +CFStringRef CFTimeZoneGetName(CFTimeZoneRef tz) { + CF_OBJC_FUNCDISPATCH0(__kCFTimeZoneTypeID, CFStringRef, tz, "name"); + __CFGenericValidateType(tz, __kCFTimeZoneTypeID); + return tz->_name; +} + +CFDataRef CFTimeZoneGetData(CFTimeZoneRef tz) { + CF_OBJC_FUNCDISPATCH0(__kCFTimeZoneTypeID, CFDataRef, tz, "data"); + __CFGenericValidateType(tz, __kCFTimeZoneTypeID); + return tz->_data; +} + +/* This function converts CFAbsoluteTime to (Win32) SYSTEMTIME + * (Aleksey Dukhnyakov) + */ +#if defined(__WIN32__) +BOOL __CFTimeZoneGetWin32SystemTime(SYSTEMTIME * sys_time, CFAbsoluteTime time) +{ + LONGLONG l; + FILETIME * ftime=(FILETIME*)&l; + + /* seconds between 1601 and 1970 : 11644473600, + * seconds between 1970 and 2001 : 978307200, + * FILETIME - number of 100-nanosecond intervals since January 1, 1601 + */ + l=(time+11644473600+978307200)*10000000; + if (FileTimeToSystemTime(ftime,sys_time)) + return TRUE; + else + return FALSE; +} +#endif + +CFTimeInterval CFTimeZoneGetSecondsFromGMT(CFTimeZoneRef tz, CFAbsoluteTime at) { +#if !defined(__WIN32__) + CFIndex idx; + CF_OBJC_FUNCDISPATCH1(__kCFTimeZoneTypeID, CFTimeInterval, tz, "_secondsFromGMTForAbsoluteTime:", at); + __CFGenericValidateType(tz, __kCFTimeZoneTypeID); + idx = __CFBSearchTZPeriods(tz, at); + return __CFTZPeriodGMTOffset(&(tz->_periods[idx])); +#else +/* To calculate seconds from GMT, calculate current timezone time and + * subtract GMT timnezone time + * (Aleksey Dukhnyakov) + */ + TIME_ZONE_INFORMATION tzi; + FILETIME ftime1,ftime2; + SYSTEMTIME stime0,stime1,stime2; + LONGLONG * l1= (LONGLONG*)&ftime1; + LONGLONG * l2= (LONGLONG*)&ftime2; + CFRange range={0,sizeof(TIME_ZONE_INFORMATION)}; + double result; + + CF_OBJC_FUNCDISPATCH1(__kCFTimeZoneTypeID, CFTimeInterval, tz, "_secondsFromGMTForAbsoluteTime:", at); + + CFDataGetBytes(tz->_data,range,(UInt8*)&tzi); + + if (!__CFTimeZoneGetWin32SystemTime(&stime0,at) || + !SystemTimeToTzSpecificLocalTime(&tzi,&stime0,&stime1) || + !SystemTimeToFileTime(&stime1,&ftime1) ) + { + CFAssert(0, __kCFLogAssertion, "Win32 system time/timezone failed !\n"); + return 0; + } + + tzi.DaylightDate.wMonth=0; + tzi.StandardDate.wMonth=0; + tzi.StandardBias=0; + tzi.DaylightBias=0; + tzi.Bias=0; + + if ( !SystemTimeToTzSpecificLocalTime(&tzi,&stime0,&stime2) || + !SystemTimeToFileTime(&stime2,&ftime2)) + { + CFAssert(0, __kCFLogAssertion, "Win32 system time/timezone failed !\n"); + return 0; + } + result=(double)((*l1-*l2)/10000000); + return result; +#endif +} + +#if defined(__WIN32__) +/* + * Get abbreviation for name for WIN32 platform + * (Aleksey Dukhnyakov) + */ + +typedef struct { + CFStringRef tzName; + CFStringRef tzAbbr; +} _CFAbbrFind; + +static void _CFFindKeyForValue(const void *key, const void *value, void *context) { + if ( ((_CFAbbrFind *)context)->tzAbbr != NULL ) { + if ( ((_CFAbbrFind *)context)->tzName == (CFStringRef) value ) { + ((_CFAbbrFind *)context)->tzAbbr = key ; + } + } +} + +CFIndex __CFTimeZoneInitAbbrev(CFTimeZoneRef tz) { + + if ( tz->_periods->abbrev == NULL ) { + _CFAbbrFind abbr = { NULL, NULL }; + CFDictionaryRef abbrevs = CFTimeZoneCopyAbbreviationDictionary(); + + CFDictionaryApplyFunction(abbrevs, _CFFindKeyForValue, &abbr); + + if ( abbr.tzAbbr != NULL) + tz->_periods->abbrev = CFStringCreateCopy(kCFAllocatorDefault, abbr.tzAbbr); + else + tz->_periods->abbrev = CFStringCreateCopy(kCFAllocatorDefault, tz->_name); +/* We should return name of TimeZone if couldn't find abbrevation. + * (Ala on MACOSX) + * + * old line : tz->_periods->abbrev = + * CFStringCreateWithCString(kCFAllocatorDefault,"UNKNOWN", + * CFStringGetSystemEncoding()); + * + * (Aleksey Dukhnyakov) +*/ + CFRelease( abbrevs ); + } + + return 0; +} +#endif + +CFStringRef CFTimeZoneCopyAbbreviation(CFTimeZoneRef tz, CFAbsoluteTime at) { + CFStringRef result; + CFIndex idx; + CF_OBJC_FUNCDISPATCH1(__kCFTimeZoneTypeID, CFStringRef, tz, "_abbreviationForAbsoluteTime:", at); + __CFGenericValidateType(tz, __kCFTimeZoneTypeID); +#if !defined(__WIN32__) + idx = __CFBSearchTZPeriods(tz, at); +#else +/* + * Initialize abbreviation for this TimeZone + * (Aleksey Dukhnyakov) + */ + idx = __CFTimeZoneInitAbbrev(tz); +#endif + result = __CFTZPeriodAbbreviation(&(tz->_periods[idx])); + return result ? CFRetain(result) : NULL; +} + +Boolean CFTimeZoneIsDaylightSavingTime(CFTimeZoneRef tz, CFAbsoluteTime at) { +#if !defined(__WIN32__) + CFIndex idx; + CF_OBJC_FUNCDISPATCH1(__kCFTimeZoneTypeID, Boolean, tz, "_isDaylightSavingTimeForAbsoluteTime:", at); + __CFGenericValidateType(tz, __kCFTimeZoneTypeID); + idx = __CFBSearchTZPeriods(tz, at); + return __CFTZPeriodIsDST(&(tz->_periods[idx])); +#else +/* Compare current timezone time and current timezone time without + * transition to day light saving time + * (Aleskey Dukhnyakov) + */ + TIME_ZONE_INFORMATION tzi; + SYSTEMTIME stime0,stime1,stime2; + CFRange range={0,sizeof(TIME_ZONE_INFORMATION)}; + + CF_OBJC_FUNCDISPATCH1(__kCFTimeZoneTypeID, Boolean, tz, "_isDaylightSavingTimeForAbsoluteTime:", at); + + CFDataGetBytes(tz->_data,range,(UInt8*)&tzi); + + if ( !__CFTimeZoneGetWin32SystemTime(&stime0,at) || + !SystemTimeToTzSpecificLocalTime(&tzi,&stime0,&stime1)) { + CFAssert(0, __kCFLogAssertion, "Win32 system time/timezone failed !\n"); + return FALSE; + } + + tzi.DaylightDate.wMonth=0; + tzi.StandardDate.wMonth=0; + + if ( !SystemTimeToTzSpecificLocalTime(&tzi,&stime0,&stime2)) { + CFAssert(0, __kCFLogAssertion, "Win32 system time/timezone failed !\n"); + return FALSE; + } + + if ( !memcmp(&stime1,&stime2,sizeof(stime1)) ) + return FALSE; + + return TRUE; +#endif +} + +CFTimeInterval _CFTimeZoneGetDSTDelta(CFTimeZoneRef tz, CFAbsoluteTime at) { + CFIndex idx; + __CFGenericValidateType(tz, __kCFTimeZoneTypeID); + idx = __CFBSearchTZPeriods(tz, at); + CFTimeInterval delta = __CFTZPeriodGMTOffset(&(tz->_periods[idx])); + if (idx + 1 < tz->_periodCnt) { + return fabs(delta - __CFTZPeriodGMTOffset(&(tz->_periods[idx + 1]))); + } else if (0 < idx) { + return fabs(delta - __CFTZPeriodGMTOffset(&(tz->_periods[idx - 1]))); + } + return 0.0; +} + +static const unsigned char *__CFTimeZoneCompatibilityMapping = +"" +" " +" " +" " + +// Empty string means delete/ignore these +" Factory " +" US/Pacific-New " +" Mideast/Riyadh87 " +" Mideast/Riyadh88 " +" Mideast/Riyadh89 " +" SystemV/AST4 " +" SystemV/AST4ADT " +" SystemV/CST6 " +" SystemV/CST6CDT " +" SystemV/EST5 " +" SystemV/EST5EDT " +" SystemV/HST10 " +" SystemV/MST7 " +" SystemV/MST7MDT " +" SystemV/PST8 " +" SystemV/PST8PDT " +" SystemV/YST9 " +" SystemV/YST9YDT " + +" America/Atka America/Adak" +" America/Ensenada America/Tijuana" +" America/Fort_Wayne America/Indianapolis" +" America/Indiana/Indianapolis America/Indianapolis" +" America/Kentucky/Louisville America/Louisville" +" America/Knox_IN America/Indiana/Knox" +" America/Porto_Acre America/Rio_Branco" +" America/Rosario America/Cordoba" +" America/Shiprock America/Denver" +" America/Virgin America/St_Thomas" +" Antarctica/South_Pole Antarctica/McMurdo" +" Asia/Ashkhabad Asia/Ashgabat" +" Asia/Chungking Asia/Chongqing" +//" Asia/Dacca Asia/Dhaka" +//" Asia/Istanbul Europe/Istanbul" +" Asia/Macao Asia/Macau" +" Asia/Tel_Aviv Asia/Jerusalem" +" Asia/Thimbu Asia/Thimphu" +" Asia/Ujung_Pandang Asia/Makassar" +" Asia/Ulan_Bator Asia/Ulaanbaatar" +" Australia/ACT Australia/Sydney" +//" Australia/Canberra Australia/Sydney" +" Australia/LHI Australia/Lord_Howe" +" Australia/NSW Australia/Sydney" +" Australia/North Australia/Darwin" +" Australia/Queensland Australia/Brisbane" +" Australia/South Australia/Adelaide" +" Australia/Tasmania Australia/Hobart" +" Australia/Victoria Australia/Melbourne" +" Australia/West Australia/Perth" +" Australia/Yancowinna Australia/Broken_Hill" +" Brazil/Acre America/Porto_Acre" +" Brazil/DeNoronha America/Noronha" +//" Brazil/East America/Sao_Paulo" +" Brazil/West America/Manaus" +" CST6CDT America/Chicago" +//" Canada/Atlantic America/Halifax" +" Canada/Central America/Winnipeg" +" Canada/East-Saskatchewan America/Regina" +//" Canada/Eastern America/Montreal" +//" Canada/Mountain America/Edmonton" +//" Canada/Newfoundland America/St_Johns" +" Canada/Pacific America/Vancouver" +//" Canada/Saskatchewan America/Regina" +" Canada/Yukon America/Whitehorse" +" Chile/Continental America/Santiago" +" Chile/EasterIsland Pacific/Easter" +" Cuba America/Havana" +" EST5EDT America/New_York" +" Egypt Africa/Cairo" +" Eire Europe/Dublin" +" Etc/GMT+0 GMT" +" Etc/GMT-0 GMT" +" Etc/GMT0 GMT" +" Etc/Greenwich GMT" +" Etc/Universal UTC" +" Etc/Zulu UTC" +" Europe/Nicosia Asia/Nicosia" +" Europe/Tiraspol Europe/Chisinau" +" GB-Eire Europe/London" +" GB Europe/London" +" GMT+0 GMT" +" GMT-0 GMT" +" GMT0 GMT" +" Greenwich GMT" +" Hongkong Asia/Hong_Kong" +" Iceland Atlantic/Reykjavik" +" Iran Asia/Tehran" +" Israel Asia/Jerusalem" +" Jamaica America/Jamaica" +//" Japan Asia/Tokyo" +" Kwajalein Pacific/Kwajalein" +" Libya Africa/Tripoli" +" MST7MDT America/Denver" +" Mexico/BajaNorte America/Tijuana" +" Mexico/BajaSur America/Mazatlan" +" Mexico/General America/Mexico_City" +" NZ-CHAT Pacific/Chatham" +" NZ Pacific/Auckland" +" Navajo America/Denver" +" PRC Asia/Shanghai" +" PST8PDT America/Los_Angeles" +" Pacific/Samoa Pacific/Pago_Pago" +" Poland Europe/Warsaw" +" Portugal Europe/Lisbon" +" ROC Asia/Taipei" +" ROK Asia/Seoul" +" Singapore Asia/Singapore" +" Turkey Europe/Istanbul" +" UCT UTC" +" US/Alaska America/Anchorage" +" US/Aleutian America/Adak" +" US/Arizona America/Phoenix" +//" US/Central America/Chicago" +" US/East-Indiana America/Indianapolis" +//" US/Eastern America/New_York" +" US/Hawaii Pacific/Honolulu" +" US/Indiana-Starke America/Indiana/Knox" +" US/Michigan America/Detroit" +//" US/Mountain America/Denver" +//" US/Pacific America/Los_Angeles" +" US/Samoa Pacific/Pago_Pago" +" Universal UTC" +" W-SU Europe/Moscow" +" Zulu UTC" +" " +" "; + +static CFDictionaryRef __CFTimeZoneCopyCompatibilityDictionary(void) { + CFDictionaryRef dict; + __CFTimeZoneLockCompatibilityMapping(); + if (NULL == __CFTimeZoneCompatibilityMappingDict) { + CFDataRef data = CFDataCreate(kCFAllocatorDefault, __CFTimeZoneCompatibilityMapping, strlen(__CFTimeZoneCompatibilityMapping)); + __CFTimeZoneCompatibilityMappingDict = CFPropertyListCreateFromXMLData(kCFAllocatorDefault, data, kCFPropertyListImmutable, NULL); + CFRelease(data); + } + if (NULL == __CFTimeZoneCompatibilityMappingDict) { + __CFTimeZoneCompatibilityMappingDict = CFDictionaryCreate(kCFAllocatorDefault, NULL, NULL, 0, NULL, NULL); + } + dict = __CFTimeZoneCompatibilityMappingDict ? CFRetain(__CFTimeZoneCompatibilityMappingDict) : NULL; + __CFTimeZoneUnlockCompatibilityMapping(); + return dict; +} + +static const unsigned char *__CFTimeZoneCompatibilityMapping2 = +"" +" " +" " +" " +" Asia/Dacca Asia/Dhaka" +" Asia/Istanbul Europe/Istanbul" +" Australia/Canberra Australia/Sydney" +" Brazil/East America/Sao_Paulo" +" Canada/Atlantic America/Halifax" +" Canada/Eastern America/Montreal" +" Canada/Mountain America/Edmonton" +" Canada/Newfoundland America/St_Johns" +" Canada/Saskatchewan America/Regina" +" Japan Asia/Tokyo" +" US/Central America/Chicago" +" US/Eastern America/New_York" +" US/Mountain America/Denver" +" US/Pacific America/Los_Angeles" +" " +" "; + +__private_extern__ CFDictionaryRef __CFTimeZoneCopyCompatibilityDictionary2(void) { + CFDictionaryRef dict; + __CFTimeZoneLockCompatibilityMapping(); + if (NULL == __CFTimeZoneCompatibilityMappingDict2) { + CFDataRef data = CFDataCreate(kCFAllocatorDefault, __CFTimeZoneCompatibilityMapping2, strlen(__CFTimeZoneCompatibilityMapping2)); + __CFTimeZoneCompatibilityMappingDict2 = CFPropertyListCreateFromXMLData(kCFAllocatorDefault, data, kCFPropertyListImmutable, NULL); + CFRelease(data); + } + if (NULL == __CFTimeZoneCompatibilityMappingDict2) { + __CFTimeZoneCompatibilityMappingDict2 = CFDictionaryCreate(kCFAllocatorDefault, NULL, NULL, 0, NULL, NULL); + } + dict = __CFTimeZoneCompatibilityMappingDict2 ? CFRetain(__CFTimeZoneCompatibilityMappingDict2) : NULL; + __CFTimeZoneUnlockCompatibilityMapping(); + return dict; +} + + diff --git a/NumberDate.subproj/CFTimeZone.h b/NumberDate.subproj/CFTimeZone.h new file mode 100644 index 0000000..603ad34 --- /dev/null +++ b/NumberDate.subproj/CFTimeZone.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFTimeZone.h + Copyright (c) 1998-2003, Apple, Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFTIMEZONE__) +#define __COREFOUNDATION_CFTIMEZONE__ 1 + +#include +#include +#include +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +CF_EXPORT +CFTypeID CFTimeZoneGetTypeID(void); + +CF_EXPORT +CFTimeZoneRef CFTimeZoneCopySystem(void); + +CF_EXPORT +void CFTimeZoneResetSystem(void); + +CF_EXPORT +CFTimeZoneRef CFTimeZoneCopyDefault(void); + +CF_EXPORT +void CFTimeZoneSetDefault(CFTimeZoneRef tz); + +CF_EXPORT +CFArrayRef CFTimeZoneCopyKnownNames(void); + +CF_EXPORT +CFDictionaryRef CFTimeZoneCopyAbbreviationDictionary(void); + +CF_EXPORT +void CFTimeZoneSetAbbreviationDictionary(CFDictionaryRef dict); + +CF_EXPORT +CFTimeZoneRef CFTimeZoneCreate(CFAllocatorRef allocator, CFStringRef name, CFDataRef data); + +CF_EXPORT +CFTimeZoneRef CFTimeZoneCreateWithTimeIntervalFromGMT(CFAllocatorRef allocator, CFTimeInterval ti); + +CF_EXPORT +CFTimeZoneRef CFTimeZoneCreateWithName(CFAllocatorRef allocator, CFStringRef name, Boolean tryAbbrev); + +CF_EXPORT +CFStringRef CFTimeZoneGetName(CFTimeZoneRef tz); + +CF_EXPORT +CFDataRef CFTimeZoneGetData(CFTimeZoneRef tz); + +CF_EXPORT +CFTimeInterval CFTimeZoneGetSecondsFromGMT(CFTimeZoneRef tz, CFAbsoluteTime at); + +CF_EXPORT +CFStringRef CFTimeZoneCopyAbbreviation(CFTimeZoneRef tz, CFAbsoluteTime at); + +CF_EXPORT +Boolean CFTimeZoneIsDaylightSavingTime(CFTimeZoneRef tz, CFAbsoluteTime at); + +#if defined(__cplusplus) +} +#endif + +#endif /* ! __COREFOUNDATION_CFTIMEZONE__ */ + diff --git a/Parsing.subproj/CFBinaryPList.c b/Parsing.subproj/CFBinaryPList.c new file mode 100644 index 0000000..9ada609 --- /dev/null +++ b/Parsing.subproj/CFBinaryPList.c @@ -0,0 +1,899 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFBinaryPList.c + Copyright 2000-2002, Apple, Inc. All rights reserved. + Responsibility: Christopher Kane +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ForFoundationOnly.h" +#include +#include +#include +#include "CFInternal.h" + + +struct __CFKeyedArchiverUID { + CFRuntimeBase _base; + uint32_t _value; +}; + +static CFStringRef __CFKeyedArchiverUIDCopyDescription(CFTypeRef cf) { + CFKeyedArchiverUIDRef uid = (CFKeyedArchiverUIDRef)cf; + return CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("{value = %u}"), cf, CFGetAllocator(cf), uid->_value); +} + +static CFStringRef __CFKeyedArchiverUIDCopyFormattingDescription(CFTypeRef cf, CFDictionaryRef formatOptions) { + CFKeyedArchiverUIDRef uid = (CFKeyedArchiverUIDRef)cf; + return CFStringCreateWithFormat(kCFAllocatorDefault, 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; + bool streamIsData; + uint64_t written; + int32_t used; + uint8_t buffer[8192 - 16]; +} __CFBinaryPlistWriteBuffer; + +static void bufferWrite(__CFBinaryPlistWriteBuffer *buf, const uint8_t *buffer, CFIndex count) { + CFIndex copyLen; + if ((CFIndex)sizeof(buf->buffer) <= count) { + if (buf->streamIsData) { + CFDataAppendBytes((CFMutableDataRef)buf->stream, buf->buffer, buf->used); + } else { + } + buf->written += buf->used; + buf->used = 0; + if (buf->streamIsData) { + CFDataAppendBytes((CFMutableDataRef)buf->stream, buffer, count); + } else { + } + buf->written += count; + return; + } + 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) { + if (buf->streamIsData) { + CFDataAppendBytes((CFMutableDataRef)buf->stream, buf->buffer, sizeof(buf->buffer)); + } else { + } + buf->written += sizeof(buf->buffer); + memmove(buf->buffer, buffer + copyLen, count - copyLen); + buf->used = count - copyLen; + } +} + +static void bufferFlush(__CFBinaryPlistWriteBuffer *buf) { + if (buf->streamIsData) { + CFDataAppendBytes((CFMutableDataRef)buf->stream, buf->buffer, buf->used); + } else { + } + buf->written += 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 shorts + 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 + 1100 xxxx // unused + 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 + +*/ + + +static CFTypeID stringtype = -1, datatype = -1, numbertype = -1, booltype = -1; +static CFTypeID datetype = -1, dicttype = -1, arraytype = -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 | (nbytes - 1); + bigint = CFSwapInt64HostToBig(bigint); + bytes = (uint8_t *)&bigint + sizeof(bigint) - nbytes; + bufferWrite(buf, &marker, 1); + bufferWrite(buf, bytes, nbytes); +} + +static Boolean __plistUniquingEqual(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 (CFGetTypeID(cf1) != CFGetTypeID(cf2)) return false; + if (CFGetTypeID(cf1) == CFNumberGetTypeID()) { + if (CFNumberIsFloatType(cf1) != CFNumberIsFloatType(cf2)) return false; + return CFEqual(cf1, cf2); + } + return CFEqual(cf1, cf2); +} + +static void _flattenPlist(CFPropertyListRef plist, CFMutableArrayRef objlist, CFMutableDictionaryRef objtable, CFMutableSetRef uniquingset) { + CFPropertyListRef unique; + uint32_t refnum; + CFTypeID type = CFGetTypeID(plist); + CFIndex idx, before, after; + CFPropertyListRef *list, buffer[256]; + + // Do not unique dictionaries, because: they are + // slow to compare, and produce poor hash codes. + // Same is true for arrays, but we still unique them; + // they aren't as slow. + if (dicttype != type) { + before = CFSetGetCount(uniquingset); + CFSetAddValue(uniquingset, plist); + after = CFSetGetCount(uniquingset); + if (after == before) { // already in set + unique = CFSetGetValue(uniquingset, plist); + if (unique != plist) { + refnum = (uint32_t)CFDictionaryGetValue(objtable, unique); + CFDictionaryAddValue(objtable, plist, (const void *)refnum); + } + return; + } + } + refnum = CFArrayGetCount(objlist); + CFArrayAppendValue(objlist, plist); + CFDictionaryAddValue(objtable, plist, (const void *)refnum); + if (dicttype == type) { + CFIndex count = CFDictionaryGetCount(plist); + list = (count <= 128) ? buffer : CFAllocatorAllocate(kCFAllocatorSystemDefault, 2 * count * sizeof(CFTypeRef), 0); + CFDictionaryGetKeysAndValues(plist, list, list + count); + for (idx = 0; idx < 2 * count; idx++) { + _flattenPlist(list[idx], objlist, objtable, uniquingset); + } + if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list); + } else if (arraytype == type) { + CFIndex count = CFArrayGetCount(plist); + list = (count <= 256) ? buffer : CFAllocatorAllocate(kCFAllocatorSystemDefault, count * sizeof(CFTypeRef), 0); + CFArrayGetValues(plist, CFRangeMake(0, count), list); + for (idx = 0; idx < count; idx++) { + _flattenPlist(list[idx], objlist, objtable, uniquingset); + } + if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list); + } +} + +// stream must be a CFMutableDataRef +CFIndex __CFBinaryPlistWriteToStream(CFPropertyListRef plist, CFTypeRef stream) { + CFMutableDictionaryRef objtable; + CFMutableSetRef uniquingset; + CFMutableArrayRef objlist; + CFBinaryPlistTrailer trailer; + uint64_t *offsets, length_so_far; + uint64_t mask, refnum; + int64_t idx, idx2, cnt; + __CFBinaryPlistWriteBuffer *buf; + CFSetCallBacks cb = kCFTypeSetCallBacks; + + if ((CFTypeID)-1 == stringtype) { + stringtype = CFStringGetTypeID(); + datatype = CFDataGetTypeID(); + numbertype = CFNumberGetTypeID(); + booltype = CFBooleanGetTypeID(); + datetype = CFDateGetTypeID(); + dicttype = CFDictionaryGetTypeID(); + arraytype = CFArrayGetTypeID(); + } + objtable = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, NULL); + _CFDictionarySetCapacity(objtable, 320); + objlist = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, NULL); + _CFArraySetCapacity(objlist, 320); + cb.equal = __plistUniquingEqual; + uniquingset = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &cb); + _CFSetSetCapacity(uniquingset, 320); + + _flattenPlist(plist, objlist, objtable, uniquingset); + + CFRelease(uniquingset); + + cnt = CFArrayGetCount(objlist); + offsets = CFAllocatorAllocate(kCFAllocatorSystemDefault, cnt * sizeof(*offsets), 0); + + buf = CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(__CFBinaryPlistWriteBuffer), 0); + buf->stream = stream; + buf->streamIsData = (CFGetTypeID(stream) == CFDataGetTypeID()); + buf->written = 0; + buf->used = 0; + bufferWrite(buf, "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, idx); + CFTypeID type = CFGetTypeID(obj); + offsets[idx] = buf->written + buf->used; + if (stringtype == type) { + CFIndex ret, count = CFStringGetLength(obj); + CFIndex needed; + uint8_t *bytes, buffer[1024]; + bytes = (count <= 1024) ? buffer : CFAllocatorAllocate(kCFAllocatorDefault, 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(obj, CFRangeMake(0, count), kCFStringEncodingASCII, 0, false, bytes, count, &needed); + if (ret == count) { + uint8_t marker = 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 = kCFBinaryPlistMarkerUnicode16String | (count < 15 ? count : 0xf); + bufferWrite(buf, &marker, 1); + if (15 <= count) { + _appendInt(buf, (uint64_t)count); + } + chars = CFAllocatorAllocate(kCFAllocatorDefault, count * sizeof(UniChar), 0); + CFStringGetCharacters(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(kCFAllocatorDefault, chars); + } + if (bytes != buffer) CFAllocatorDeallocate(kCFAllocatorDefault, bytes); + } else if (numbertype == type) { + uint8_t marker; + CFSwappedFloat64 swapped64; + CFSwappedFloat32 swapped32; + uint64_t bigint; + uint8_t *bytes; + CFIndex nbytes; + if (CFNumberIsFloatType(obj)) { + if (CFNumberGetByteSize(obj) <= (CFIndex)sizeof(float)) { + float v; + CFNumberGetValue(obj, kCFNumberFloat32Type, &v); + swapped32 = CFConvertFloat32HostToSwapped(v); + bytes = (uint8_t *)&swapped32; + nbytes = sizeof(float); + marker = kCFBinaryPlistMarkerReal | 2; + } else { + double v; + CFNumberGetValue(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 { + CFNumberGetValue(obj, kCFNumberSInt64Type, &bigint); + _appendInt(buf, bigint); + } + } else if (_CFKeyedArchiverUIDGetTypeID() == type) { + _appendUID(buf, (CFKeyedArchiverUIDRef)obj); + } else if (booltype == type) { + uint8_t marker = CFBooleanGetValue(obj) ? kCFBinaryPlistMarkerTrue : kCFBinaryPlistMarkerFalse; + bufferWrite(buf, &marker, 1); + } else if (datatype == type) { + CFIndex count = CFDataGetLength(obj); + uint8_t marker = kCFBinaryPlistMarkerData | (count < 15 ? count : 0xf); + bufferWrite(buf, &marker, 1); + if (15 <= count) { + _appendInt(buf, (uint64_t)count); + } + bufferWrite(buf, CFDataGetBytePtr(obj), count); + } else if (datetype == type) { + CFSwappedFloat64 swapped; + uint8_t marker = kCFBinaryPlistMarkerDate; + bufferWrite(buf, &marker, 1); + swapped = CFConvertFloat64HostToSwapped(CFDateGetAbsoluteTime(obj)); + bufferWrite(buf, (uint8_t *)&swapped, sizeof(swapped)); + } else if (dicttype == type) { + CFIndex count = CFDictionaryGetCount(obj); + CFPropertyListRef *list, buffer[512]; + uint8_t marker = kCFBinaryPlistMarkerDict | (count < 15 ? count : 0xf); + bufferWrite(buf, &marker, 1); + if (15 <= count) { + _appendInt(buf, (uint64_t)count); + } + list = (count <= 256) ? buffer : CFAllocatorAllocate(kCFAllocatorSystemDefault, 2 * count * sizeof(CFTypeRef), 0); + CFDictionaryGetKeysAndValues(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)CFDictionaryGetValue(objtable, value); + swapped = CFSwapInt32HostToBig(refnum); + bufferWrite(buf, source + sizeof(swapped) - trailer._objectRefSize, trailer._objectRefSize); + } + if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list); + } else if (arraytype == type) { + CFIndex count = CFArrayGetCount(obj); + CFPropertyListRef *list, buffer[256]; + uint8_t marker = kCFBinaryPlistMarkerArray | (count < 15 ? count : 0xf); + bufferWrite(buf, &marker, 1); + if (15 <= count) { + _appendInt(buf, (uint64_t)count); + } + list = (count <= 256) ? buffer : CFAllocatorAllocate(kCFAllocatorSystemDefault, count * sizeof(CFTypeRef), 0); + CFArrayGetValues(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)CFDictionaryGetValue(objtable, value); + swapped = CFSwapInt32HostToBig(refnum); + bufferWrite(buf, source + sizeof(swapped) - trailer._objectRefSize, trailer._objectRefSize); + } + if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list); + } else { + CFRelease(objtable); + CFRelease(objlist); + 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); + CFAllocatorDeallocate(kCFAllocatorSystemDefault, buf); + CFAllocatorDeallocate(kCFAllocatorSystemDefault, offsets); + return (CFIndex)length_so_far; +} + +bool __CFBinaryPlistGetTopLevelInfo(CFDataRef data, uint8_t *marker, uint64_t *offset, CFBinaryPlistTrailer *trailer) { + const uint8_t *databytes, *bytesptr; + uint64_t datalen; + CFBinaryPlistTrailer trail; + uint64_t off; + CFIndex idx; + + if ((CFTypeID)-1 == stringtype) { + stringtype = CFStringGetTypeID(); + datatype = CFDataGetTypeID(); + numbertype = CFNumberGetTypeID(); + booltype = CFBooleanGetTypeID(); + datetype = CFDateGetTypeID(); + dicttype = CFDictionaryGetTypeID(); + arraytype = CFArrayGetTypeID(); + } + databytes = CFDataGetBytePtr(data); + datalen = CFDataGetLength(data); + if (!databytes || datalen < 8 || 0 != memcmp("bplist00", databytes, 8)) return false; + if (datalen < sizeof(trail) + 8 + 1) return false; + memmove(&trail, databytes + datalen - sizeof(trail), sizeof(trail)); + trail._numObjects = CFSwapInt64BigToHost(trail._numObjects); + trail._topObject = CFSwapInt64BigToHost(trail._topObject); + if (trail._numObjects < trail._topObject) return false; + trail._offsetTableOffset = CFSwapInt64BigToHost(trail._offsetTableOffset); + if (datalen < trail._offsetTableOffset + trail._numObjects * trail._offsetIntSize + sizeof(trail)) return false; + bytesptr = databytes + trail._offsetTableOffset + trail._topObject * trail._offsetIntSize; + off = 0; + for (idx = 0; idx < trail._offsetIntSize; idx++) { + off = (off << 8) + bytesptr[idx]; + } + if (trail._offsetTableOffset <= off) return false; + if (trailer) *trailer = trail; + if (offset) *offset = off; + if (marker) *marker = *(databytes + off); + return true; +} + +static bool _readInt(const uint8_t *ptr, uint64_t *bigint, const uint8_t **newptr) { + uint8_t marker; + CFIndex idx, cnt; + marker = *ptr++; + if ((marker & 0xf0) != kCFBinaryPlistMarkerInt) return false; + cnt = 1 << (marker & 0xf); + *bigint = 0; + for (idx = 0; idx < cnt; idx++) { + *bigint = (*bigint << 8) + *ptr++; + } + if (newptr) *newptr = ptr; + return true; +} + +static uint64_t _getOffsetOfRefAt(const uint8_t *databytes, const uint8_t *bytesptr, const CFBinaryPlistTrailer *trailer) { + uint64_t ref = 0, off = 0; + CFIndex idx; + for (idx = 0; idx < trailer->_objectRefSize; idx++) { + ref = (ref << 8) + bytesptr[idx]; + } + bytesptr = databytes + trailer->_offsetTableOffset + ref * trailer->_offsetIntSize; + for (idx = 0; idx < trailer->_offsetIntSize; idx++) { + off = (off << 8) + bytesptr[idx]; + } + return off; +} + +bool __CFBinaryPlistGetOffsetForValueFromArray(CFDataRef data, uint64_t startOffset, const CFBinaryPlistTrailer *trailer, CFIndex idx, uint64_t *offset) { + const uint8_t *databytes, *bytesptr; + uint8_t marker; + CFIndex cnt; + uint64_t off; + databytes = CFDataGetBytePtr(data); + marker = *(databytes + startOffset); + if ((marker & 0xf0) != kCFBinaryPlistMarkerArray) return false; + cnt = (marker & 0x0f); + if (cnt < 15 && cnt <= idx) return false; + bytesptr = databytes + startOffset + 1; + if (0xf == cnt) { + uint64_t bigint; + if (!_readInt(bytesptr, &bigint, &bytesptr)) return false; + if (INT_MAX < bigint) return false; + cnt = (CFIndex)bigint; + } + if (cnt <= idx) return false; + off = _getOffsetOfRefAt(databytes, bytesptr + idx * trailer->_objectRefSize, trailer); + if ((uint64_t)CFDataGetLength(data) <= off) return false; + if (offset) *offset = off; + return true; +} + +bool __CFBinaryPlistGetOffsetForValueFromDictionary(CFDataRef data, uint64_t startOffset, const CFBinaryPlistTrailer *trailer, CFTypeRef key, uint64_t *koffset, uint64_t *voffset) { + const uint8_t *databytes, *refsptr, *bytesptr; + uint64_t off; + uint8_t marker; + CFTypeID keytype = CFGetTypeID(key); + CFIndex idx, keyn, cnt, cnt2; + + databytes = CFDataGetBytePtr(data); + marker = *(databytes + startOffset); + if ((marker & 0xf0) != kCFBinaryPlistMarkerDict) return false; + cnt = (marker & 0x0f); + refsptr = databytes + startOffset + 1 + 0; + if (0xf == cnt) { + uint64_t bigint; + if (!_readInt(refsptr, &bigint, &refsptr)) return false; + if (INT_MAX < bigint) return false; + cnt = (CFIndex)bigint; + } + for (keyn = 0; keyn < cnt; keyn++) { + off = _getOffsetOfRefAt(databytes, refsptr, trailer); + if ((uint64_t)CFDataGetLength(data) <= off) return false; + refsptr += trailer->_objectRefSize; + bytesptr = databytes + off; + marker = *bytesptr & 0xf0; + cnt2 = *bytesptr & 0x0f; + if (kCFBinaryPlistMarkerASCIIString == marker || kCFBinaryPlistMarkerUnicode16String == marker) { + CFStringInlineBuffer strbuf; + UniChar uchar; + if (keytype != stringtype) goto miss; + if (0xf == cnt2 && CFStringGetLength(key) < 15) goto miss; + bytesptr++; + if (0xf == cnt2) { + uint64_t bigint; + if (!_readInt(bytesptr, &bigint, &bytesptr)) return false; + if (INT_MAX < bigint) return false; + cnt2 = (CFIndex)bigint; + } + if (cnt2 != CFStringGetLength(key)) goto miss; + uchar = (kCFBinaryPlistMarkerASCIIString == marker) ? (UniChar)bytesptr[0] : (UniChar)(bytesptr[0] * 256 + bytesptr[1]); + if (uchar != CFStringGetCharacterAtIndex(key, 0)) goto miss; + bytesptr += (kCFBinaryPlistMarkerASCIIString == marker) ? 1 : 2; + CFStringInitInlineBuffer(key, &strbuf, CFRangeMake(0, cnt2)); + for (idx = 1; idx < cnt2; idx++) { + uchar = (kCFBinaryPlistMarkerASCIIString == marker) ? (UniChar)bytesptr[0] : (UniChar)(bytesptr[0] * 256 + bytesptr[1]); + if (uchar != __CFStringGetCharacterFromInlineBufferQuick(&strbuf, idx)) goto miss; + bytesptr += (kCFBinaryPlistMarkerASCIIString == marker) ? 1 : 2; + } + if (koffset) *koffset = off; + off = _getOffsetOfRefAt(databytes, refsptr + (cnt - 1) * trailer->_objectRefSize, trailer); + if ((uint64_t)CFDataGetLength(data) <= off) return false; + if (voffset) *voffset = off; + return true; + } else { +//#warning the other primitive types should be allowed as keys in a binary plist dictionary, I think + return false; + } + miss: ; + } + return false; +} + +extern CFArrayRef _CFArrayCreate_ex(CFAllocatorRef allocator, bool mutable, const void **values, CFIndex numValues); + +extern CFDictionaryRef _CFDictionaryCreate_ex(CFAllocatorRef allocator, bool mutable, const void **keys, const void **values, CFIndex numValues); + +#if 0 +static bool _getUIDFromData(const uint8_t *datap, uint64_t *vp) { + int32_t idx, cnt; + uint8_t marker = *datap; + uint64_t bigint; + if ((marker & 0xf0) != kCFBinaryPlistMarkerUID) return false; + cnt = (marker & 0x0f) + 1; + datap++; + bigint = 0; + for (idx = 0; idx < cnt; idx++) { + bigint = (bigint << 8) + *datap++; + } + *vp = bigint; + return true; +} +#endif + +static bool _getFloatFromData(const uint8_t *datap, float *vp) { + CFSwappedFloat32 swapped32; + if (*datap != (kCFBinaryPlistMarkerReal | 2)) return false; + datap++; + memmove(&swapped32, datap, sizeof(swapped32)); + *vp = CFConvertFloat32SwappedToHost(swapped32); + return true; +} + +static bool _getDoubleFromData(const uint8_t *datap, double *vp) { + CFSwappedFloat64 swapped64; + if (*datap != (kCFBinaryPlistMarkerReal | 3)) return false; + datap++; + memmove(&swapped64, datap, sizeof(swapped64)); + *vp = CFConvertFloat64SwappedToHost(swapped64); + return true; +} + +bool __CFBinaryPlistCreateObject(CFDataRef data, uint64_t startOffset, const CFBinaryPlistTrailer *trailer, CFAllocatorRef allocator, CFOptionFlags mutabilityOption, CFMutableDictionaryRef objects, CFPropertyListRef *plist) { + const uint8_t *databytes, *bytesptr; + uint64_t off; + uint8_t marker; + CFIndex idx, cnt; + uint64_t bigint; + UniChar *chars; + CFPropertyListRef *list, buffer[256]; + + if (objects) { + *plist = CFDictionaryGetValue(objects, (const void *)(intptr_t)startOffset); + if (*plist) { + CFRetain(*plist); + return true; + } + } + + databytes = CFDataGetBytePtr(data); + marker = *(databytes + startOffset); + switch (marker & 0xf0) { + case kCFBinaryPlistMarkerNull: + switch (marker) { + case kCFBinaryPlistMarkerNull: + *plist = NULL; + return true; + case kCFBinaryPlistMarkerFalse: + *plist = CFRetain(kCFBooleanFalse); + return true; + case kCFBinaryPlistMarkerTrue: + *plist = CFRetain(kCFBooleanTrue); + return true; + } + return false; + case kCFBinaryPlistMarkerInt: + if (!_readInt(databytes + startOffset, &bigint, NULL)) return false; + *plist = CFNumberCreate(allocator, kCFNumberSInt64Type, &bigint); + if (objects) CFDictionarySetValue(objects, (const void *)(intptr_t)startOffset, *plist); + return (*plist) ? true : false; + case kCFBinaryPlistMarkerReal: + cnt = marker & 0x0f; + if (2 == cnt) { + float f; + _getFloatFromData(databytes + startOffset, &f); + *plist = CFNumberCreate(allocator, kCFNumberFloat32Type, &f); + if (objects) CFDictionarySetValue(objects, (const void *)(intptr_t)startOffset, *plist); + return (*plist) ? true : false; + } else if (3 == cnt) { + double d; + _getDoubleFromData(databytes + startOffset, &d); + *plist = CFNumberCreate(allocator, kCFNumberFloat64Type, &d); + if (objects) CFDictionarySetValue(objects, (const void *)(intptr_t)startOffset, *plist); + return (*plist) ? true : false; + } + return false; + case kCFBinaryPlistMarkerDate & 0xf0: { + CFSwappedFloat64 swapped64; + double d; + cnt = marker & 0x0f; + if (3 != cnt) return false; + memmove(&swapped64, databytes + startOffset + 1, sizeof(swapped64)); + d = CFConvertFloat64SwappedToHost(swapped64); + *plist = CFDateCreate(allocator, d); + if (objects) CFDictionarySetValue(objects, (const void *)(intptr_t)startOffset, *plist); + return (*plist) ? true : false; + } + case kCFBinaryPlistMarkerData: + cnt = marker & 0x0f; + bytesptr = databytes + startOffset + 1; + if (0xf == cnt) { + if (!_readInt(bytesptr, &bigint, &bytesptr)) return false; + if (INT_MAX < bigint) return false; + cnt = (CFIndex)bigint; + } + if (mutabilityOption == kCFPropertyListMutableContainersAndLeaves) { + *plist = CFDataCreateMutable(allocator, 0); + CFDataAppendBytes((CFMutableDataRef)*plist, bytesptr, cnt); + } else { + *plist = CFDataCreate(allocator, bytesptr, cnt); + } + if (objects) CFDictionarySetValue(objects, (const void *)(intptr_t)startOffset, *plist); + return (*plist) ? true : false; + case kCFBinaryPlistMarkerASCIIString: + cnt = marker & 0x0f; + bytesptr = databytes + startOffset + 1; + if (0xf == cnt) { + if (!_readInt(bytesptr, &bigint, &bytesptr)) return false; + if (INT_MAX < bigint) return false; + cnt = (CFIndex)bigint; + } + if (mutabilityOption == kCFPropertyListMutableContainersAndLeaves) { + CFStringRef str = CFStringCreateWithBytes(allocator, bytesptr, cnt, kCFStringEncodingASCII, false); + *plist = CFStringCreateMutableCopy(allocator, 0, str); + CFRelease(str); + } else { + *plist = CFStringCreateWithBytes(allocator, bytesptr, cnt, kCFStringEncodingASCII, false); + } + if (objects) CFDictionarySetValue(objects, (const void *)(intptr_t)startOffset, *plist); + return (*plist) ? true : false; + case kCFBinaryPlistMarkerUnicode16String: + cnt = marker & 0x0f; + bytesptr = databytes + startOffset + 1; + if (0xf == cnt) { + if (!_readInt(bytesptr, &bigint, &bytesptr)) return false; + if (INT_MAX < bigint) return false; + cnt = (CFIndex)bigint; + } + chars = CFAllocatorAllocate(allocator, cnt * sizeof(UniChar), 0); + memmove(chars, bytesptr, cnt * sizeof(UniChar)); + for (idx = 0; idx < cnt; idx++) { + chars[idx] = CFSwapInt16BigToHost(chars[idx]); + } + if (mutabilityOption == kCFPropertyListMutableContainersAndLeaves) { + CFStringRef str = CFStringCreateWithCharactersNoCopy(allocator, chars, cnt, allocator); + *plist = CFStringCreateMutableCopy(allocator, 0, str); + CFRelease(str); + } else { + *plist = CFStringCreateWithCharactersNoCopy(allocator, chars, cnt, allocator); + } + if (objects) CFDictionarySetValue(objects, (const void *)(intptr_t)startOffset, *plist); + return (*plist) ? true : false; + case kCFBinaryPlistMarkerUID: + cnt = (marker & 0x0f) + 1; + bytesptr = databytes + startOffset + 1; + bigint = 0; + for (idx = 0; idx < cnt; idx++) { + bigint = (bigint << 8) + *bytesptr++; + } + if (UINT_MAX < bigint) return false; + *plist = _CFKeyedArchiverUIDCreate(allocator, (uint32_t)bigint); + if (objects) CFDictionarySetValue(objects, (const void *)(intptr_t)startOffset, *plist); + return (*plist) ? true : false; + case kCFBinaryPlistMarkerArray: + cnt = marker & 0x0f; + bytesptr = databytes + startOffset + 1; + if (0xf == cnt) { + if (!_readInt(bytesptr, &bigint, &bytesptr)) return false; + if (INT_MAX < bigint) return false; + cnt = (CFIndex)bigint; + } + list = (cnt <= 256) ? buffer : CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(CFPropertyListRef) * cnt, 0); + for (idx = 0; idx < cnt; idx++) { + CFPropertyListRef pl; + off = _getOffsetOfRefAt(databytes, bytesptr, trailer); + if ((uint64_t)CFDataGetLength(data) <= off) return false; + if (!__CFBinaryPlistCreateObject(data, off, trailer, allocator, mutabilityOption, objects, &pl)) { + while (idx--) { + CFRelease(list[idx]); + } + if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list); + return false; + } + list[idx] = pl; + bytesptr += trailer->_objectRefSize; + } + *plist = _CFArrayCreate_ex(allocator, (mutabilityOption != kCFPropertyListImmutable), list, cnt); + if (objects) CFDictionarySetValue(objects, (const void *)(intptr_t)startOffset, *plist); + if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list); + return (*plist) ? true : false; + case kCFBinaryPlistMarkerDict: + cnt = marker & 0x0f; + bytesptr = databytes + startOffset + 1; + if (0xf == cnt) { + if (!_readInt(bytesptr, &bigint, &bytesptr)) return false; + if (INT_MAX < bigint) return false; + cnt = (CFIndex)bigint; + } + cnt *= 2; + list = (cnt <= 256) ? buffer : CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(CFPropertyListRef) * cnt, 0); + for (idx = 0; idx < cnt; idx++) { + CFPropertyListRef pl; + off = _getOffsetOfRefAt(databytes, bytesptr, trailer); + if ((uint64_t)CFDataGetLength(data) <= off) return false; + if (!__CFBinaryPlistCreateObject(data, off, trailer, allocator, mutabilityOption, objects, &pl)) { + while (idx--) { + CFRelease(list[idx]); + } + if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list); + return false; + } + list[idx] = pl; + bytesptr += trailer->_objectRefSize; + } + *plist = _CFDictionaryCreate_ex(allocator, (mutabilityOption != kCFPropertyListImmutable), list, list + cnt / 2, cnt / 2); + if (objects) CFDictionarySetValue(objects, (const void *)(intptr_t)startOffset, *plist); + if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list); + return (*plist) ? true : false; + } + return false; +} + +__private_extern__ bool __CFTryParseBinaryPlist(CFAllocatorRef allocator, CFDataRef data, CFOptionFlags option, CFPropertyListRef *plist, CFStringRef *errorString) { + uint8_t marker; + CFBinaryPlistTrailer trailer; + uint64_t offset; + CFPropertyListRef pl; + + if (8 <= CFDataGetLength(data) && __CFBinaryPlistGetTopLevelInfo(data, &marker, &offset, &trailer)) { + if (__CFBinaryPlistCreateObject(data, offset, &trailer, allocator, option, NULL, &pl)) { + if (plist) *plist = pl; + } else { + if (plist) *plist = NULL; + if (errorString) *errorString = CFRetain(CFSTR("binary data is corrupt")); + } + return true; + } + return false; +} + diff --git a/Parsing.subproj/CFPropertyList.c b/Parsing.subproj/CFPropertyList.c new file mode 100644 index 0000000..37f01c1 --- /dev/null +++ b/Parsing.subproj/CFPropertyList.c @@ -0,0 +1,2520 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFPropertyList.c + Copyright 1999-2002, Apple, Inc. All rights reserved. + Responsibility: Christopher Kane +*/ + +#include +#include +#include +#include +#include "CFUtilities.h" +#include "CFStringEncodingConverter.h" +#include "CFInternal.h" +#include +#include +#include +#include +#include +#if defined(__MACH__) || defined(__WIN32__) +#include +#elif !defined(__WIN32__) +#define isspace(x) ((x)==' ' || (x)=='\n' || (x)=='\f' || (x)=='\r' || (x)=='\t' || (x)=='\v') +#define isdigit(x) ((x) <= '9' && (x) >= '0') +#define isxdigit(x) (((x) <= '9' && (x) >= '0') || ((x) >= 'a' && (x) <= 'f') || ((x) >= 'A' && (x) <= 'F')) +#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(): 0x%x not of a property list type", __PRETTY_FUNCTION__, (UInt32)cf); + +static bool __CFPropertyListIsValidAux(CFPropertyListRef plist, bool recursive, CFMutableSetRef set, CFPropertyListFormat format); + +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(0, 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(0, CFSTR("CFPropertyListIsValid(): property list dictionaries cannot contain NULL keys")); + if (!value) CFLog(0, CFSTR("CFPropertyListIsValid(): property list dictionaries cannot contain NULL values")); + if (CFStringGetTypeID() != CFGetTypeID(key)) { + CFStringRef desc = CFCopyTypeIDDescription(CFGetTypeID(key)); + CFLog(0, CFSTR("CFPropertyListIsValid(): property list dictionaries may only have keys which are CFStrings, not '%@'"), desc); + CFRelease(desc); + } +#endif + ctx->answer = key && value && (CFStringGetTypeID() == 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(0, CFSTR("CFPropertyListIsValid(): property lists cannot contain NULL")); +#endif + if (!plist) return false; + type = CFGetTypeID(plist); + if (CFStringGetTypeID() == type) return true; + if (CFDataGetTypeID() == type) return true; + if (kCFPropertyListOpenStepFormat != format) { + if (CFBooleanGetTypeID() == type) return true; + if (CFNumberGetTypeID() == type) return true; + if (CFDateGetTypeID() == type) return true; + if (_CFKeyedArchiverUIDGetTypeID() == type) return true; + } + if (!recursive && CFArrayGetTypeID() == type) return true; + if (!recursive && CFDictionaryGetTypeID() == 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(0, CFSTR("CFPropertyListIsValid(): property lists cannot contain recursive container references")); +#endif + if (CFSetContainsValue(set, plist)) return false; + if (CFArrayGetTypeID() == type) { + struct context ctx = {true, set, format}; + CFSetAddValue(set, plist); + CFArrayApplyFunction(plist, CFRangeMake(0, CFArrayGetCount(plist)), __CFPropertyListIsArrayPlistAux, &ctx); + CFSetRemoveValue(set, plist); + return ctx.answer; + } + if (CFDictionaryGetTypeID() == type) { + struct context ctx = {true, set, format}; + CFSetAddValue(set, plist); + CFDictionaryApplyFunction(plist, __CFPropertyListIsDictPlistAux, &ctx); + CFSetRemoveValue(set, plist); + return ctx.answer; + } +#if defined(DEBUG) + { + CFStringRef desc = CFCopyTypeIDDescription(type); + CFLog(0, CFSTR("CFPropertyListIsValid(): property lists cannot contain objects of type '%@'"), desc); + CFRelease(desc); + } +#endif + return false; +} + +Boolean CFPropertyListIsValid(CFPropertyListRef plist, CFPropertyListFormat format) { + CFMutableSetRef set; + bool result; + CFAssert1(plist != NULL, __kCFLogAssertion, "%s(): NULL is not a property list", __PRETTY_FUNCTION__); + set = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, NULL); + 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, 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(NULL, chars + curLoc, length - curLoc, kCFAllocatorNull))) { + if ((data = CFStringCreateExternalRepresentation(NULL, 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(NULL, 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(NULL, 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 == CFStringGetTypeID()) { + _plistAppendUTF8CString(xmlString, "<"); + _plistAppendCharacters(xmlString, CFXMLPlistTags[STRING_IX], STRING_TAG_LENGTH); + _plistAppendUTF8CString(xmlString, ">"); + _appendEscapedString(object, xmlString); + _plistAppendUTF8CString(xmlString, "\n"); + } else if (typeID == _CFKeyedArchiverUIDGetTypeID()) { + uint64_t v = _CFKeyedArchiverUIDGetValue(object); + CFNumberRef num = CFNumberCreate(kCFAllocatorSystemDefault, kCFNumberSInt64Type, &v); + _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, "\n"); + _CFAppendXML0(num, indentation+1, xmlString); + _appendIndents(indentation, xmlString); + _plistAppendUTF8CString(xmlString, "\n"); + } else if (typeID == CFArrayGetTypeID()) { + UInt32 i, count = CFArrayGetCount(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(object, i), indentation+1, xmlString); + } + _appendIndents(indentation, xmlString); + _plistAppendUTF8CString(xmlString, "\n"); + } else if (typeID == CFDictionaryGetTypeID()) { + UInt32 i, count = CFDictionaryGetCount(object); + CFAllocatorRef allocator = CFGetAllocator(xmlString); + 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(allocator, count * sizeof(CFTypeRef), 0); + CFDictionaryGetKeysAndValues(object, keys, NULL); + keyArray = CFArrayCreateMutable(allocator, 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(key, xmlString); + _plistAppendUTF8CString(xmlString, "\n"); + _CFAppendXML0(CFDictionaryGetValue(object, key), indentation+1, xmlString); + } + CFAllocatorDeallocate(allocator, keys); + _appendIndents(indentation, xmlString); + _plistAppendUTF8CString(xmlString, "\n"); + } else if (typeID == CFDataGetTypeID()) { + _plistAppendUTF8CString(xmlString, "<"); + _plistAppendCharacters(xmlString, CFXMLPlistTags[DATA_IX], DATA_TAG_LENGTH); + _plistAppendUTF8CString(xmlString, ">\n"); + _XMLPlistAppendDataUsingBase64(xmlString, object, indentation); + _appendIndents(indentation, xmlString); + _plistAppendUTF8CString(xmlString, "\n"); + } else if (typeID == CFDateGetTypeID()) { + // YYYY '-' MM '-' DD 'T' hh ':' mm ':' ss 'Z' + CFGregorianDate date = CFAbsoluteTimeGetGregorianDate(CFDateGetAbsoluteTime(object), NULL); + + _plistAppendUTF8CString(xmlString, "<"); + _plistAppendCharacters(xmlString, CFXMLPlistTags[DATE_IX], DATE_TAG_LENGTH); + _plistAppendUTF8CString(xmlString, ">"); + + _plistAppendFormat(xmlString, CFSTR("%04d-%02d-%02dT%02d:%02d:%02dZ"), date.year, date.month, date.day, date.hour, date.minute, (int)date.second); + + _plistAppendUTF8CString(xmlString, "\n"); + } else if (typeID == CFNumberGetTypeID()) { + if (CFNumberIsFloatType(object)) { + _plistAppendUTF8CString(xmlString, "<"); + _plistAppendCharacters(xmlString, CFXMLPlistTags[REAL_IX], REAL_TAG_LENGTH); + _plistAppendUTF8CString(xmlString, ">"); + + if (_CFExecutableLinkedOnOrAfter(CFSystemVersionJaguar)) { + CFStringRef s = __CFNumberCopyFormattingDescriptionAsFloat64(object); + _plistAppendString(xmlString, s); + CFRelease(s); + } else if (CFNumberGetType(object) == kCFNumberFloat64Type || CFNumberGetType(object) == kCFNumberDoubleType) { + double doubleVal; + static CFStringRef doubleFormatString = NULL; + CFNumberGetValue(object, kCFNumberDoubleType, &doubleVal); + if (!doubleFormatString) { + doubleFormatString = CFStringCreateWithFormat(NULL, NULL, CFSTR("%%.%de"), DBL_DIG); + } + _plistAppendFormat(xmlString, doubleFormatString, doubleVal); + } else { + float floatVal; + static CFStringRef floatFormatString = NULL; + CFNumberGetValue(object, kCFNumberFloatType, &floatVal); + if (!floatFormatString) { + floatFormatString = CFStringCreateWithFormat(NULL, NULL, CFSTR("%%.%de"), FLT_DIG); + } + _plistAppendFormat(xmlString, floatFormatString, floatVal); + } + + _plistAppendUTF8CString(xmlString, "\n"); + } else { + _plistAppendUTF8CString(xmlString, "<"); + _plistAppendCharacters(xmlString, CFXMLPlistTags[INTEGER_IX], INTEGER_TAG_LENGTH); + _plistAppendUTF8CString(xmlString, ">"); + + _plistAppendFormat(xmlString, CFSTR("%@"), object); + + _plistAppendUTF8CString(xmlString, "\n"); + } + } else if (typeID == CFBooleanGetTypeID()) { + if (CFBooleanGetValue(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, "\n\n<"); + _plistAppendCharacters(xml, CFXMLPlistTags[PLIST_IX], PLIST_TAG_LENGTH); + _plistAppendUTF8CString(xml, " version=\"1.0\">\n"); + + _CFAppendXML0(propertyList, 0, xml); + + _plistAppendUTF8CString(xml, "\n"); +} + +CFDataRef CFPropertyListCreateXMLData(CFAllocatorRef allocator, CFPropertyListRef propertyList) { + CFMutableDataRef xml; + CFAssert1(propertyList != NULL, __kCFLogAssertion, "%s(): Cannot be called with a NULL property list", __PRETTY_FUNCTION__); + __CFAssertIsPList(propertyList); + if (_CFExecutableLinkedOnOrAfter(CFSystemVersionJaguar)) { + if (!CFPropertyListIsValid(propertyList, kCFPropertyListXMLFormat_v1_0)) return NULL; + } + xml = CFDataCreateMutable(allocator, 0); + _CFGenerateXMLPropertyListToData(xml, propertyList); + return xml; +} + +CFDataRef _CFPropertyListCreateXMLDataWithExtras(CFAllocatorRef allocator, CFPropertyListRef propertyList) { + 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 ""), CFXMLNodeGetString(CFXMLTreeGetNode(tree))); + break; + case kCFXMLNodeTypeText: + CFStringAppend(str, CFXMLNodeGetString(CFXMLTreeGetNode(tree))); + break; + case kCFXMLNodeTypeCDATASection: + CFStringAppendFormat(str, NULL, CFSTR(""), CFXMLNodeGetString(CFXMLTreeGetNode(tree))); + break; + case kCFXMLNodeTypeDocumentFragment: + break; + case kCFXMLNodeTypeEntity: { + CFXMLEntityInfo *data = (CFXMLEntityInfo *)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree)); + CFStringAppendCString(str, "entityType == kCFXMLEntityTypeParameter) { + CFStringAppend(str, CFSTR("% ")); + } + CFStringAppend(str, CFXMLNodeGetString(CFXMLTreeGetNode(tree))); + CFStringAppend(str, CFSTR(" ")); + if (data->replacementText) { + appendQuotedString(str, data->replacementText); + CFStringAppendCString(str, ">", kCFStringEncodingASCII); + } else { + appendExternalID(str, &(data->entityID)); + if (data->notationName) { + CFStringAppendFormat(str, NULL, CFSTR(" NDATA %@"), data->notationName); + } + CFStringAppendCString(str, ">", kCFStringEncodingASCII); + } + break; + } + case kCFXMLNodeTypeEntityReference: + { + CFXMLEntityTypeCode entityType = ((CFXMLEntityReferenceInfo *)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree)))->entityType; + if (entityType == kCFXMLEntityTypeParameter) { + CFStringAppendFormat(str, NULL, CFSTR("%%%@;"), CFXMLNodeGetString(CFXMLTreeGetNode(tree))); + } else { + CFStringAppendFormat(str, NULL, CFSTR("&%@;"), CFXMLNodeGetString(CFXMLTreeGetNode(tree))); + } + break; + } + case kCFXMLNodeTypeDocumentType: + CFStringAppendCString(str, "externalID; + appendExternalID(str, extID); + } + CFStringAppendCString(str, " [", kCFStringEncodingASCII); + break; + case kCFXMLNodeTypeWhitespace: + CFStringAppend(str, CFXMLNodeGetString(CFXMLTreeGetNode(tree))); + break; + case kCFXMLNodeTypeNotation: { + CFXMLNotationInfo *data = (CFXMLNotationInfo *)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree)); + CFStringAppendFormat(str, NULL, CFSTR("externalID)); + CFStringAppendCString(str, ">", kCFStringEncodingASCII); + break; + } + case kCFXMLNodeTypeElementTypeDeclaration: + CFStringAppendFormat(str, NULL, CFSTR(""), CFXMLNodeGetString(CFXMLTreeGetNode(tree)), ((CFXMLElementTypeDeclarationInfo *)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree)))->contentDescription); + break; + case kCFXMLNodeTypeAttributeListDeclaration: { + CFXMLAttributeListDeclarationInfo *attListData = (CFXMLAttributeListDeclarationInfo *)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree)); + CFIndex idx; + CFStringAppendCString(str, "numberOfAttributes; idx ++) { + CFXMLAttributeDeclarationInfo *attr = &(attListData->attributes[idx]); + CFStringAppendFormat(str, NULL, CFSTR("\n\t%@ %@ %@"), attr->attributeName, attr->typeString, attr->defaultString); + } + CFStringAppendCString(str, ">", kCFStringEncodingASCII); + break; + } + default: + CFAssert1(false, __kCFLogAssertion, "Encountered unexpected XMLDataTypeID %d", CFXMLNodeGetTypeCode(CFXMLTreeGetNode(tree))); + } +} + +static void _CFAppendXMLEpilog(CFMutableStringRef str, CFXMLTreeRef tree) { + CFXMLNodeTypeCode typeID = CFXMLNodeGetTypeCode(CFXMLTreeGetNode(tree)); + if (typeID == kCFXMLNodeTypeElement) { + if (((CFXMLElementInfo *)CFXMLNodeGetInfoPtr(CFXMLTreeGetNode(tree)))->isEmpty) return; + CFStringAppendFormat(str, NULL, CFSTR(""), CFXMLNodeGetString(CFXMLTreeGetNode(tree))); + } else if (typeID == kCFXMLNodeTypeDocumentType) { + CFIndex len = CFStringGetLength(str); + if (CFStringHasSuffix(str, CFSTR(" ["))) { + // There were no in-line DTD elements + CFStringDelete(str, CFRangeMake(len-2, 2)); + } else { + CFStringAppendCString(str, "]", kCFStringEncodingASCII); + } + CFStringAppendCString(str, ">", kCFStringEncodingASCII); + } +} diff --git a/PlugIn.subproj/CFBundle.c b/PlugIn.subproj/CFBundle.c new file mode 100644 index 0000000..917b27d --- /dev/null +++ b/PlugIn.subproj/CFBundle.c @@ -0,0 +1,2969 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFBundle.c + Copyright 1999-2002, Apple, Inc. All rights reserved. + Responsibility: Doug Davidson +*/ + +#include "CFBundle_Internal.h" +#include +#include +#include +#include +#include +#include "CFInternal.h" +#include "CFPriv.h" +#include +#include "CFBundle_BinaryTypes.h" + +#if defined(BINARY_SUPPORT_DYLD) +// Import the mach-o headers that define the macho magic numbers +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#endif /* BINARY_SUPPORT_DYLD */ + +#if defined(__MACOS8__) +/* MacOS8 Headers */ +#include +#include +#else +/* Unixy & Windows Headers */ +#include +#include +#endif +#if defined(__LINUX__) +#include +#endif + +#if defined(__WIN32__) +#undef __STDC__ +#include +#include +#endif + +// 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(_kCFBundleNumericVersionKey, "CFBundleNumericVersion") +CONST_STRING_DECL(_kCFBundleExecutablePathKey, "CFBundleExecutablePath") +CONST_STRING_DECL(_kCFBundleResourcesFileMappedKey, "CSResourcesFileMapped") +CONST_STRING_DECL(_kCFBundleCFMLoadAsBundleKey, "CFBundleCFMLoadAsBundle") +CONST_STRING_DECL(_kCFBundleAllowMixedLocalizationsKey, "CFBundleAllowMixedLocalizations") + +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 */ + void *_imageCookie; + void *_moduleCookie; + + /* CFM<->DYLD glue */ + CFMutableDictionaryRef _glueDict; + + /* Resource fork goop */ + _CFResourceData _resourceData; + + _CFPlugInData _plugInData; + +#if defined(BINARY_SUPPORT_DLL) + HMODULE _hModule; +#endif + +}; + +static CFSpinLock_t CFBundleGlobalDataLock = 0; + +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; + +// Forward declares functions. +static CFBundleRef _CFBundleCreate(CFAllocatorRef allocator, CFURLRef bundleURL, Boolean alreadyLocked); +static CFStringRef _CFBundleCopyExecutableName(CFAllocatorRef alloc, CFBundleRef bundle, CFURLRef url, CFDictionaryRef infoDict); +static CFURLRef _CFBundleCopyExecutableURLIgnoringCache(CFBundleRef bundle); +static void _CFBundleEnsureBundleExistsForImagePath(CFStringRef imagePath); +static void _CFBundleEnsureBundlesExistForImagePaths(CFArrayRef imagePaths); +static void _CFBundleEnsureBundlesUpToDateWithHintAlreadyLocked(CFStringRef hint); +static void _CFBundleEnsureAllBundlesUpToDateAlreadyLocked(void); +static void _CFBundleCheckWorkarounds(CFBundleRef bundle); +#if defined(BINARY_SUPPORT_DYLD) +static CFDictionaryRef _CFBundleGrokInfoDictFromMainExecutable(void); +static CFStringRef _CFBundleDYLDCopyLoadedImagePathForPointer(void *p); +static void *_CFBundleDYLDGetSymbolByNameWithSearch(CFBundleRef bundle, CFStringRef symbolName, Boolean globalSearch); +#endif /* BINARY_SUPPORT_DYLD */ +#if defined(BINARY_SUPPORT_DYLD) && defined(BINARY_SUPPORT_CFM) && defined(__ppc__) +static void *_CFBundleFunctionPointerForTVector(CFAllocatorRef allocator, void *tvp); +static void *_CFBundleTVectorForFunctionPointer(CFAllocatorRef allocator, void *fp); +#endif /* BINARY_SUPPORT_DYLD && BINARY_SUPPORT_CFM && __ppc__ */ + +static void _CFBundleAddToTables(CFBundleRef bundle, Boolean alreadyLocked) { + CFStringRef bundleID = CFBundleGetIdentifier(bundle); + + if (!alreadyLocked) { + __CFSpinLock(&CFBundleGlobalDataLock); + } + + // Add to the _allBundles list + if (_allBundles == NULL) { + // Create this from the default allocator + CFArrayCallBacks nonRetainingArrayCallbacks = kCFTypeArrayCallBacks; + nonRetainingArrayCallbacks.retain = NULL; + nonRetainingArrayCallbacks.release = NULL; + _allBundles = CFArrayCreateMutable(NULL, 0, &nonRetainingArrayCallbacks); + } + CFArrayAppendValue(_allBundles, bundle); + + // Add to the table that maps urls to bundles + if (_bundlesByURL == NULL) { + // Create this from the default allocator + CFDictionaryValueCallBacks nonRetainingDictionaryValueCallbacks = kCFTypeDictionaryValueCallBacks; + nonRetainingDictionaryValueCallbacks.retain = NULL; + nonRetainingDictionaryValueCallbacks.release = NULL; + _bundlesByURL = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &nonRetainingDictionaryValueCallbacks); + } + CFDictionarySetValue(_bundlesByURL, bundle->_url, bundle); + + // Add to the table that maps identifiers to bundles + if (bundleID) { + CFBundleRef existingBundle = NULL; + Boolean addIt = true; + if (_bundlesByIdentifier == NULL) { + // Create this from the default allocator + CFDictionaryValueCallBacks nonRetainingDictionaryValueCallbacks = kCFTypeDictionaryValueCallBacks; + nonRetainingDictionaryValueCallbacks.retain = NULL; + nonRetainingDictionaryValueCallbacks.release = NULL; + _bundlesByIdentifier = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &nonRetainingDictionaryValueCallbacks); + } + existingBundle = (CFBundleRef)CFDictionaryGetValue(_bundlesByIdentifier, bundleID); + if (existingBundle) { + UInt32 existingVersion, newVersion; + existingVersion = CFBundleGetVersionNumber(existingBundle); + newVersion = CFBundleGetVersionNumber(bundle); + if (newVersion < existingVersion) { + // Less than to means that if you load two bundles with the same identifier and the same version, the last one wins. + addIt = false; + } + } + if (addIt) { + CFDictionarySetValue(_bundlesByIdentifier, bundleID, bundle); + } + } + if (!alreadyLocked) { + __CFSpinUnlock(&CFBundleGlobalDataLock); + } +} + +static void _CFBundleRemoveFromTables(CFBundleRef bundle) { + CFStringRef bundleID = CFBundleGetIdentifier(bundle); + + __CFSpinLock(&CFBundleGlobalDataLock); + + // Remove from the various lists + if (_allBundles != NULL) { + 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 != NULL) { + CFDictionaryRemoveValue(_bundlesByURL, bundle->_url); + } + + // Remove from the table that maps identifiers to bundles + if ((bundleID != NULL) && (_bundlesByIdentifier != NULL)) { + if (CFDictionaryGetValue(_bundlesByIdentifier, bundleID) == bundle) { + CFDictionaryRemoveValue(_bundlesByIdentifier, bundleID); + } + } + __CFSpinUnlock(&CFBundleGlobalDataLock); +} + +__private_extern__ CFBundleRef _CFBundleFindByURL(CFURLRef url, Boolean alreadyLocked) { + CFBundleRef result = NULL; + if (!alreadyLocked) { + __CFSpinLock(&CFBundleGlobalDataLock); + } + if (_bundlesByURL != NULL) { + 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); + CFStringGetCharacters(str, CFRangeMake(0, buffLen), buff); + 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(NULL, &(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(NULL, &(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(NULL, buff, buffLen, kCFAllocatorNull); + url = CFURLCreateWithFileSystemPath(NULL, 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; + CFStringGetCharacters(str, CFRangeMake(0, buffLen), buff); + len1 = _CFLengthAfterDeletingLastPathComponent(buff, buffLen); + if (len1 > 0 && len1 + 1 < buffLen) { + str1 = CFStringCreateWithCharacters(NULL, buff, len1); + str2 = CFStringCreateWithCharacters(NULL, buff + len1 + 1, buffLen - len1 - 1); + if (str1 && str2) { + url1 = CFURLCreateWithFileSystemPath(NULL, str1, PLATFORM_PATH_STYLE, true); + if (url1) { + url2 = CFURLCreateWithFileSystemPathRelativeToBase(NULL, 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 != NULL) { + 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; +} + +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, buff1, CFMaxPathSize) || !CFURLGetFileSystemRepresentation(executableURL, true, buff2, CFMaxPathSize) || 0 != strcmp(buff1, buff2)) { + CFRelease(bundle); + bundle = NULL; + } + if (executableURL) CFRelease(executableURL); + } + } + if (bundleURL) CFRelease(bundleURL); + if (resolvedURL) CFRelease(resolvedURL); + return bundle; +} + +static CFBundleRef _CFBundleGetMainBundleAlreadyLocked(void) { + if (!_initedMainBundle) { + const char *processPath; + CFStringRef str = NULL; + CFURLRef executableURL = NULL, bundleURL = NULL; +#if defined(BINARY_SUPPORT_CFM) + Boolean versRegionOverrides = false; +#endif /* BINARY_SUPPORT_CFM */ +#if defined(__MACOS8__) + // do not use Posix-styled _CFProcessPath() + ProcessSerialNumber gProcessID; + ProcessInfoRec processInfo; + FSSpec processAppSpec; + + processInfo.processInfoLength = sizeof(ProcessInfoRec); + processInfo.processAppSpec = &processAppSpec; + + if ((GetCurrentProcess(&gProcessID) == noErr) && (GetProcessInformation(&gProcessID, &processInfo) == noErr)) { + executableURL = _CFCreateURLFromFSSpec(NULL, (void *)(&processAppSpec), false); + } +#endif + _initedMainBundle = true; + processPath = _CFProcessPath(); + if (processPath) { + str = CFStringCreateWithCString(NULL, processPath, CFStringFileSystemEncoding()); + if (!executableURL) executableURL = CFURLCreateWithFileSystemPath(NULL, str, PLATFORM_PATH_STYLE, false); + } + if (executableURL) { + bundleURL = _CFBundleCopyBundleURLForExecutableURL(executableURL); + } + if (bundleURL != NULL) { + // make sure that main bundle has executable path + //??? what if we are not the main executable in the bundle? + _mainBundle = _CFBundleCreate(NULL, bundleURL, true); + if (_mainBundle != NULL) { + CFBundleGetInfoDictionary(_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 (_mainBundle->_infoDict == NULL || 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(NULL, _mainBundle, NULL, NULL); + if (!executableName || !CFStringHasSuffix(str, executableName)) _mainBundle->_version = 4; + if (executableName) CFRelease(executableName); + } +#if defined(BINARY_SUPPORT_DYLD) + if (_mainBundle->_binaryType == __CFBundleDYLDExecutableBinary) { + if (_mainBundle->_infoDict != NULL) 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->_infoDict != NULL) CFRelease(_mainBundle->_infoDict); + _mainBundle->_infoDict = _CFBundleCopyInfoDictionaryInResourceForkWithAllocator(CFGetAllocator(_mainBundle), executableURL); + if (_mainBundle->_binaryType == __CFBundleUnreadableBinary && _mainBundle->_infoDict != NULL && CFDictionaryGetValue(_mainBundle->_infoDict, kCFBundleDevelopmentRegionKey) != NULL) versRegionOverrides = true; + } +#endif /* BINARY_SUPPORT_CFM */ + } + if (_mainBundle->_infoDict == NULL) { + _mainBundle->_infoDict = CFDictionaryCreateMutable(CFGetAllocator(_mainBundle), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + } + if (NULL == CFDictionaryGetValue(_mainBundle->_infoDict, _kCFBundleExecutablePathKey)) { + CFDictionarySetValue((CFMutableDictionaryRef)(_mainBundle->_infoDict), _kCFBundleExecutablePathKey, str); + } +#if defined(BINARY_SUPPORT_DYLD) + // get cookie for already-loaded main bundle + if (_mainBundle->_binaryType == __CFBundleDYLDExecutableBinary && !_mainBundle->_imageCookie) { + _mainBundle->_imageCookie = (void *)_dyld_get_image_header(0); + } +#endif /* BINARY_SUPPORT_DYLD */ +#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 != NULL) { + CFDictionarySetValue((CFMutableDictionaryRef)(_mainBundle->_infoDict), kCFBundleDevelopmentRegionKey, devLang); + CFRelease(devLang); + } + } +#endif /* BINARY_SUPPORT_CFM */ + } + } + 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; +} + +#if defined(BINARY_SUPPORT_DYLD) + +static void *_CFBundleReturnAddressFromFrameAddress(void *addr) +{ + void *ret; +#if defined(__ppc__) + __asm__ volatile("lwz %0,0x0008(%1)" : "=r" (ret) : "b" (addr)); +#elif defined(__i386__) + __asm__ volatile("movl 0x4(%1),%0" : "=r" (ret) : "r" (addr)); +#elif defined(hppa) + __asm__ volatile("ldw 0x4(%1),%0" : "=r" (ret) : "r" (addr)); +#elif defined(sparc) + __asm__ volatile("ta 0x3"); + __asm__ volatile("ld [%1 + 60],%0" : "=r" (ret) : "r" (addr)); +#else +#warning Do not know how to define _CFBundleReturnAddressFromFrameAddress on this architecture + ret = NULL; +#endif + return ret; +} + +#endif + +CFBundleRef CFBundleGetBundleWithIdentifier(CFStringRef bundleID) { + CFBundleRef result = NULL; + if (bundleID) { + __CFSpinLock(&CFBundleGlobalDataLock); + (void)_CFBundleGetMainBundleAlreadyLocked(); + if (_bundlesByIdentifier != NULL) { + result = (CFBundleRef)CFDictionaryGetValue(_bundlesByIdentifier, bundleID); + } +#if defined(BINARY_SUPPORT_DYLD) + if (result == NULL) { + // Try to create the bundle for the caller and try again + void *p = _CFBundleReturnAddressFromFrameAddress(__builtin_frame_address(1)); + CFStringRef imagePath = _CFBundleDYLDCopyLoadedImagePathForPointer(p); + if (imagePath != NULL) { + _CFBundleEnsureBundleExistsForImagePath(imagePath); + CFRelease(imagePath); + } + if (_bundlesByIdentifier != NULL) { + result = (CFBundleRef)CFDictionaryGetValue(_bundlesByIdentifier, bundleID); + } + } +#endif + if (result == NULL) { + // Try to guess the bundle from the identifier and try again + _CFBundleEnsureBundlesUpToDateWithHintAlreadyLocked(bundleID); + if (_bundlesByIdentifier != NULL) { + result = (CFBundleRef)CFDictionaryGetValue(_bundlesByIdentifier, bundleID); + } + } + if (result == NULL) { + // Make sure all bundles have been created and try again. + _CFBundleEnsureAllBundlesUpToDateAlreadyLocked(); + if (_bundlesByIdentifier != NULL) { + result = (CFBundleRef)CFDictionaryGetValue(_bundlesByIdentifier, bundleID); + } + } + __CFSpinUnlock(&CFBundleGlobalDataLock); + } + return result; +} + +static CFStringRef __CFBundleCopyDescription(CFTypeRef cf) { + char buff[CFMaxPathSize]; + CFStringRef path = NULL, binaryType = NULL, retval = NULL; + if (((CFBundleRef)cf)->_url != NULL && CFURLGetFileSystemRepresentation(((CFBundleRef)cf)->_url, true, buff, CFMaxPathSize)) { + path = CFStringCreateWithCString(NULL, buff, CFStringFileSystemEncoding()); + } + 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(NULL, NULL, CFSTR("CFBundle/CFPlugIn 0x%x <%@> (%@%sloaded)"), cf, path, binaryType, ((CFBundleRef)cf)->_isLoaded ? "" : "not "); + } else { + retval = CFStringCreateWithFormat(NULL, NULL, CFSTR("CFBundle 0x%x <%@> (%@%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 != NULL) { + 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 != NULL) { + CFRelease(bundle->_url); + } + if (bundle->_infoDict != NULL) { + CFRelease(bundle->_infoDict); + } + if (bundle->_modDate != NULL) { + CFRelease(bundle->_modDate); + } + if (bundle->_localInfoDict != NULL) { + CFRelease(bundle->_localInfoDict); + } + if (bundle->_searchLanguages != NULL) { + CFRelease(bundle->_searchLanguages); + } + if (bundle->_glueDict != NULL) { + CFDictionaryApplyFunction(bundle->_glueDict, _CFBundleDeallocateGlue, (void *)allocator); + CFRelease(bundle->_glueDict); + } + if (bundle->_resourceData._stringTableCache != NULL) { + 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); +} + +CFTypeID CFBundleGetTypeID(void) { + return __kCFBundleTypeID; +} + +static CFBundleRef _CFBundleCreate(CFAllocatorRef allocator, CFURLRef bundleURL, Boolean alreadyLocked) { + 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, buff, CFMaxPathSize)) return NULL; + + newURL = CFURLCreateFromFileSystemRepresentation(allocator, buff, strlen(buff), true); + if (NULL == newURL) { + newURL = 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 (NULL != modDate) CFRelease(modDate); + CFRelease(newURL); + return NULL; + } + } else { + CFRelease(newURL); + return NULL; + } + } + + bundle = (CFBundleRef)_CFRuntimeCreateInstance(allocator, __kCFBundleTypeID, sizeof(struct __CFBundle) - sizeof(CFRuntimeBase), NULL); + if (NULL == 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 + + bundle->_isLoaded = false; + bundle->_sharesStringsFiles = false; + + /* ??? For testing purposes? Or for good? */ + if (!getenv("CFBundleDisableStringsSharing") && + (strncmp(buff, "/System/Library/Frameworks", 26) == 0) && + (strncmp(buff + strlen(buff) - 10, ".framework", 10) == 0)) bundle->_sharesStringsFiles = true; + + bundle->_connectionCookie = NULL; + bundle->_imageCookie = NULL; + bundle->_moduleCookie = NULL; + + bundle->_glueDict = NULL; + +#if defined(BINARY_SUPPORT_CFM) + bundle->_resourceData._executableLacksResourceFork = false; +#else + bundle->_resourceData._executableLacksResourceFork = true; +#endif + + 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); + + _CFBundleInitPlugIn(bundle); + + _CFBundleCheckWorkarounds(bundle); + + return bundle; +} + +CFBundleRef CFBundleCreate(CFAllocatorRef allocator, CFURLRef bundleURL) {return _CFBundleCreate(allocator, bundleURL, false);} + +CFArrayRef CFBundleCreateBundlesFromDirectory(CFAllocatorRef alloc, CFURLRef directoryURL, CFStringRef bundleType) { + CFMutableArrayRef bundles = CFArrayCreateMutable(alloc, 0, &kCFTypeArrayCallBacks); + CFArrayRef URLs = _CFContentsOfDirectory(alloc, NULL, NULL, directoryURL, bundleType); + if (URLs != NULL) { + CFIndex i, c = CFArrayGetCount(URLs); + CFURLRef curURL; + CFBundleRef curBundle; + + for (i=0; i_url) { + CFRetain(bundle->_url); + } + return bundle->_url; +} + +void _CFBundleSetDefaultLocalization(CFStringRef localizationName) { + CFStringRef newLocalization = localizationName ? CFStringCreateCopy(NULL, localizationName) : NULL; + if (_defaultLocalization) CFRelease(_defaultLocalization); + _defaultLocalization = newLocalization; +} + +__private_extern__ CFArrayRef _CFBundleGetLanguageSearchList(CFBundleRef bundle) { + if (bundle->_searchLanguages == NULL) { + CFMutableArrayRef langs = CFArrayCreateMutable(NULL, 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, CFArrayGetValueAtIndex(localizations, 0)); + } + CFRelease(localizations); + } + } + + if (devLang != NULL && !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 == NULL) { + // 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 != NULL) { + CFArrayAppendValue(langs, _defaultLocalization); + } else { + CFArrayAppendValue(langs, CFSTR("en")); + } + } + bundle->_searchLanguages = langs; + } + return bundle->_searchLanguages; +} + +CFDictionaryRef CFBundleCopyInfoDictionaryInDirectory(CFURLRef url) {return _CFBundleCopyInfoDictionaryInDirectory(NULL, url, NULL);} + +CFDictionaryRef CFBundleGetInfoDictionary(CFBundleRef bundle) { + if (bundle->_infoDict == NULL) { + 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 == NULL) { + 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 = CFPropertyListCreateFromXMLData(CFGetAllocator(bundle), data, kCFPropertyListImmutable, &errStr); + if (errStr) { + CFRelease(errStr); + } + 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!= NULL) && (key != NULL)) { + CFDictionaryRef dict = CFBundleGetLocalInfoDictionary(bundle); + if (dict != NULL) { + result = CFDictionaryGetValue(dict, key); + } + if (result == NULL) { + dict = CFBundleGetInfoDictionary(bundle); + if (dict != NULL) { + result = CFDictionaryGetValue(dict, key); + } + } + } + return result; +} + +CFStringRef CFBundleGetIdentifier(CFBundleRef bundle) { + CFStringRef bundleID = NULL; + CFDictionaryRef infoDict = CFBundleGetInfoDictionary(bundle); + if (infoDict) { + bundleID = 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 == NULL) { + unknownVersionValue = CFDictionaryGetValue(infoDict, kCFBundleVersionKey); + } + if (unknownVersionValue != NULL) { + 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 = CFDictionaryGetValue(infoDict, kCFBundleDevelopmentRegionKey); + if (devLang != NULL && (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 = 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 = 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 defined(__MACH__) + const uint8_t *image_suffix = getenv("DYLD_IMAGE_SUFFIX"); +#endif /* __MACH__ */ + + if (urlPath == NULL || exeName == NULL) return NULL; + +#if defined(__MACH__) + if (image_suffix != NULL) { + CFStringRef newExeName, imageSuffix; + imageSuffix = CFStringCreateWithCString(NULL, 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 != NULL && !_urlExists(alloc, executableURL)) { + CFRelease(executableURL); + executableURL = NULL; + } + CFRelease(newExeName); + CFRelease(imageSuffix); + } +#endif /* __MACH__ */ + if (executableURL == NULL) { + executableURL = CFURLCreateWithFileSystemPathRelativeToBase(alloc, exeName, kCFURLPOSIXPathStyle, false, urlPath); + if (executableURL != NULL && !_urlExists(alloc, executableURL)) { + CFRelease(executableURL); + executableURL = NULL; + } + } +#if defined(__WIN32__) + 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 == NULL && bundle != NULL) { + alloc = CFGetAllocator(bundle); + } + if (infoDict == NULL && bundle != NULL) { + infoDict = CFBundleGetInfoDictionary(bundle); + } + if (url == NULL && bundle != NULL) { + url = bundle->_url; + } + + if (infoDict != NULL) { + // Figure out the name of the executable. + // First try for the new key in the plist. + executableName = CFDictionaryGetValue(infoDict, kCFBundleExecutableKey); + if (executableName == NULL) { + // Second try for the old key in the plist. + executableName = CFDictionaryGetValue(infoDict, _kCFBundleOldExecutableKey); + } + if (executableName != NULL && CFGetTypeID(executableName) == CFStringGetTypeID() && CFStringGetLength(executableName) > 0) { + CFRetain(executableName); + } else { + executableName = NULL; + } + } + if (executableName == NULL && url != NULL) { + // 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); + 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(NULL, bundle, NULL, NULL); + CFURLRef resourceForkURL = NULL; + if (executableName != NULL) { + 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 isDir = false; + Boolean foundIt = false; + Boolean lookupMainExe = ((executableName == NULL) ? true : false); + + if (bundle != NULL) { + 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 != NULL) && (infoDict != NULL)) { + executablePath = CFDictionaryGetValue(infoDict, _kCFBundleExecutablePathKey); + if (executablePath != NULL) { + executableURL = CFURLCreateWithFileSystemPath(alloc, executablePath, kCFURLPOSIXPathStyle, false); + if (executableURL != NULL) foundIt = true; + if (!foundIt) { + executablePath = NULL; + CFDictionaryRemoveValue((CFMutableDictionaryRef)infoDict, _kCFBundleExecutablePathKey); + } + } + } + + if (!foundIt) { + if (lookupMainExe) { + executableName = _CFBundleCopyExecutableName(alloc, bundle, url, infoDict); + } + if (executableName != NULL) { + // Now, look for the executable inside the bundle. + if (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 = CFRetain(url); + } + exeSubdirURL = CFURLCreateWithFileSystemPathRelativeToBase(alloc, useOtherPlatform ? _CFBundleGetOtherPlatformExecutablesSubdirectoryName() : _CFBundleGetPlatformExecutablesSubdirectoryName(), kCFURLPOSIXPathStyle, true, exeDirURL); + executableURL = _CFBundleCopyExecutableURLRaw(alloc, exeSubdirURL, executableName); + if (executableURL == NULL) { + CFRelease(exeSubdirURL); + exeSubdirURL = CFURLCreateWithFileSystemPathRelativeToBase(alloc, useOtherPlatform ? _CFBundleGetOtherAlternatePlatformExecutablesSubdirectoryName() : _CFBundleGetAlternatePlatformExecutablesSubdirectoryName(), kCFURLPOSIXPathStyle, true, exeDirURL); + executableURL = _CFBundleCopyExecutableURLRaw(alloc, exeSubdirURL, executableName); + } + if (executableURL == NULL) { + CFRelease(exeSubdirURL); + exeSubdirURL = CFURLCreateWithFileSystemPathRelativeToBase(alloc, useOtherPlatform ? _CFBundleGetPlatformExecutablesSubdirectoryName() : _CFBundleGetOtherPlatformExecutablesSubdirectoryName(), kCFURLPOSIXPathStyle, true, exeDirURL); + executableURL = _CFBundleCopyExecutableURLRaw(alloc, exeSubdirURL, executableName); + } + if (executableURL == NULL) { + CFRelease(exeSubdirURL); + exeSubdirURL = CFURLCreateWithFileSystemPathRelativeToBase(alloc, useOtherPlatform ? _CFBundleGetAlternatePlatformExecutablesSubdirectoryName() : _CFBundleGetOtherAlternatePlatformExecutablesSubdirectoryName(), kCFURLPOSIXPathStyle, true, exeDirURL); + executableURL = _CFBundleCopyExecutableURLRaw(alloc, exeSubdirURL, executableName); + } + if (executableURL == NULL) { + executableURL = _CFBundleCopyExecutableURLRaw(alloc, exeDirURL, executableName); + } + + CFRelease(exeDirURL); + CFRelease(exeSubdirURL); + } + + // 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 == NULL) { + executableURL = _CFBundleCopyExecutableURLRaw(alloc, url, executableName); + } + +#if defined(__WIN32__) + // 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 (lookupMainExe && !ignoreCache && !useOtherPlatform && (bundle != NULL) && (infoDict != NULL) && (executableURL != NULL)) { + // We found it. Cache the path. + CFURLRef absURL = CFURLCopyAbsoluteURL(executableURL); + executablePath = CFURLCopyFileSystemPath(absURL, kCFURLPOSIXPathStyle); + CFRelease(absURL); + CFDictionarySetValue((CFMutableDictionaryRef)infoDict, _kCFBundleExecutablePathKey, executablePath); + CFRelease(executablePath); + } + if (lookupMainExe && !useOtherPlatform && (bundle != NULL) && (executableURL == NULL)) { + bundle->_binaryType = __CFBundleNoBinary; + } + if (lookupMainExe) { + CFRelease(executableName); + } + } + } + + if ((bundle == NULL) && (infoDict != NULL)) { + CFRelease(infoDict); + } + + return executableURL; +} + +CFURLRef _CFBundleCopyExecutableURLInDirectory(CFURLRef url) {return _CFBundleCopyExecutableURLInDirectoryWithAllocator(NULL, NULL, url, NULL, true, false);} + +CFURLRef _CFBundleCopyOtherExecutableURLInDirectory(CFURLRef url) {return _CFBundleCopyExecutableURLInDirectoryWithAllocator(NULL, 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;} + +#define UNKNOWN_FILETYPE 0x0 +#define PEF_FILETYPE 0x1000 +#define XLS_FILETYPE 0x10001 +#define DOC_FILETYPE 0x10002 +#define PPT_FILETYPE 0x10003 +#define XLS_NAME "Workbook" +#define DOC_NAME "WordDocument" +#define PPT_NAME "PowerPoint Document" +#define PEF_MAGIC 0x4a6f7921 +#define PEF_CIGAM 0x21796f4a +#define PLIST_SEGMENT "__TEXT" +#define PLIST_SECTION "__info_plist" +#define LIB_X11 "/usr/X11R6/lib/libX" + +static const uint32_t __CFBundleMagicNumbersArray[] = { + 0xcafebabe, 0xbebafeca, 0xfeedface, 0xcefaedfe, 0x4a6f7921, 0x21796f4a, 0xffd8ffe0, 0x4d4d002a, + 0x49492a00, 0x47494638, 0x89504e47, 0x69636e73, 0x00000100, 0x7b5c7274, 0x25504446, 0x2e7261fd, + 0x2e736e64, 0x2e736400, 0x464f524d, 0x52494646, 0x38425053, 0x000001b3, 0x000001ba, 0x4d546864, + 0x504b0304, 0x53495421, 0x53495432, 0x53495435, 0x53495444, 0x53747566, 0x3c212d2d, 0x25215053, + 0xd0cf11e0, 0x62656769, 0x6b6f6c79, 0x3026b275, 0x0000000c +}; + +// string, with groups of 5 characters being 1 element in the array +static const char * __CFBundleExtensionsArray = + "mach\0" "mach\0" "mach\0" "mach\0" "pef\0\0" "pef\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" + "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" "html\0" "ps\0\0\0" + "ole\0\0" "uu\0\0\0""dmg\0\0" "wmv\0\0" "jp2\0\0"; + +#define NUM_EXTENSIONS 37 +#define EXTENSION_LENGTH 5 +#define MAGIC_BYTES_TO_READ 512 + +#if defined(BINARY_SUPPORT_DYLD) + +CF_INLINE uint32_t _CFBundleSwapInt32Conditional(uint32_t arg, Boolean swap) {return swap ? CFSwapInt32(arg) : arg;} + +static CFDictionaryRef _CFBundleGrokInfoDictFromData(char *bytes, unsigned long length) { + CFMutableDictionaryRef result = NULL; + CFDataRef infoData = NULL; + if (NULL != bytes && 0 < length) { + infoData = CFDataCreateWithBytesNoCopy(NULL, bytes, length, kCFAllocatorNull); + if (infoData) { + +__CFSetNastyFile(CFSTR("")); + + result = (CFMutableDictionaryRef)CFPropertyListCreateFromXMLData(NULL, infoData, kCFPropertyListMutableContainers, NULL); + if (result && CFDictionaryGetTypeID() != CFGetTypeID(result)) { + CFRelease(result); + result = NULL; + } + CFRelease(infoData); + } + if (!result) { + result = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + } + } + return result; +} + +static CFDictionaryRef _CFBundleGrokInfoDictFromMainExecutable() { + unsigned long length = 0; + char *bytes = getsectdata(PLIST_SEGMENT, PLIST_SECTION, &length); + return _CFBundleGrokInfoDictFromData(bytes, length); +} + +static CFDictionaryRef _CFBundleGrokInfoDictFromFile(int fd, unsigned long offset, Boolean swapped) { + struct stat statBuf; + char *maploc; + unsigned i, j; + CFDictionaryRef result = NULL; + Boolean foundit = false; + if (fstat(fd, &statBuf) == 0 && (maploc = mmap(0, statBuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) != (void *)-1) { + unsigned long ncmds = _CFBundleSwapInt32Conditional(((struct mach_header *)(maploc + offset))->ncmds, swapped); + unsigned long sizeofcmds = _CFBundleSwapInt32Conditional(((struct mach_header *)(maploc + offset))->sizeofcmds, swapped); + char *startofcmds = maploc + offset + sizeof(struct mach_header); + char *endofcmds = startofcmds + sizeofcmds; + struct segment_command *sgp = (struct segment_command *)startofcmds; + if (endofcmds > maploc + statBuf.st_size) endofcmds = maploc + statBuf.st_size; + 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)); + unsigned long 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, PLIST_SEGMENT, sizeof(sp->segname))) { + unsigned long length = _CFBundleSwapInt32Conditional(sp->size, swapped); + unsigned long sectoffset = _CFBundleSwapInt32Conditional(sp->offset, swapped); + char *bytes = maploc + offset + sectoffset; + if (maploc <= bytes && bytes + length <= maploc + statBuf.st_size) result = _CFBundleGrokInfoDictFromData(bytes, length); + foundit = true; + } + sp = (struct section *)((char *)sp + sizeof(struct section)); + } + } + sgp = (struct segment_command *)((char *)sgp + _CFBundleSwapInt32Conditional(sgp->cmdsize, swapped)); + } + munmap(maploc, statBuf.st_size); + } + return result; +} + +static Boolean _CFBundleGrokX11(int fd, unsigned long offset, Boolean swapped) { + static const char libX11name[] = LIB_X11; + struct stat statBuf; + char *maploc; + unsigned i; + Boolean result = false; + if (fstat(fd, &statBuf) == 0 && (maploc = mmap(0, statBuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) != (void *)-1) { + unsigned long ncmds = _CFBundleSwapInt32Conditional(((struct mach_header *)(maploc + offset))->ncmds, swapped); + unsigned long sizeofcmds = _CFBundleSwapInt32Conditional(((struct mach_header *)(maploc + offset))->sizeofcmds, swapped); + char *startofcmds = maploc + offset + sizeof(struct mach_header); + char *endofcmds = startofcmds + sizeofcmds; + struct dylib_command *dlp = (struct dylib_command *)startofcmds; + if (endofcmds > maploc + statBuf.st_size) endofcmds = maploc + statBuf.st_size; + for (i = 0; !result && i < ncmds && startofcmds <= (char *)dlp && (char *)dlp < endofcmds; i++) { + if (LC_LOAD_DYLIB == _CFBundleSwapInt32Conditional(dlp->cmd, swapped)) { + unsigned long nameoffset = _CFBundleSwapInt32Conditional(dlp->dylib.name.offset, swapped); + if (0 == strncmp((char *)dlp + nameoffset, libX11name, sizeof(libX11name) - 1)) result = true; + } + dlp = (struct dylib_command *)((char *)dlp + _CFBundleSwapInt32Conditional(dlp->cmdsize, swapped)); + } + munmap(maploc, statBuf.st_size); + } + return result; +} + + +static UInt32 _CFBundleGrokMachTypeForFatFile(int fd, void *bytes, CFIndex length, Boolean *isX11, CFDictionaryRef *infodict) { + UInt32 machtype = UNKNOWN_FILETYPE, magic, numFatHeaders = ((struct fat_header *)bytes)->nfat_arch, maxFatHeaders = (length - sizeof(struct fat_header)) / sizeof(struct fat_arch); + unsigned char moreBytes[sizeof(struct mach_header)]; + const NXArchInfo *archInfo = NXGetLocalArchInfo(); + struct fat_arch *fat = NULL; + + if (isX11) *isX11 = false; + if (infodict) *infodict = NULL; + if (numFatHeaders > maxFatHeaders) numFatHeaders = maxFatHeaders; + if (numFatHeaders > 0) { + fat = NXFindBestFatArch(archInfo->cputype, archInfo->cpusubtype, (struct fat_arch *)(bytes + sizeof(struct fat_header)), numFatHeaders); + if (!fat) fat = (struct fat_arch *)(bytes + sizeof(struct fat_header)); + } + if (fat) { + if (lseek(fd, fat->offset, SEEK_SET) == (off_t)fat->offset && read(fd, moreBytes, sizeof(struct mach_header)) >= (int)sizeof(struct mach_header)) { + magic = *((UInt32 *)moreBytes); + if (MH_MAGIC == magic) { + machtype = ((struct mach_header *)moreBytes)->filetype; + if (infodict) *infodict = _CFBundleGrokInfoDictFromFile(fd, fat->offset, false); + if (isX11) *isX11 = _CFBundleGrokX11(fd, fat->offset, false); + } else if (MH_CIGAM == magic) { + machtype = CFSwapInt32(((struct mach_header *)moreBytes)->filetype); + if (infodict) *infodict = _CFBundleGrokInfoDictFromFile(fd, fat->offset, true); + if (isX11) *isX11 = _CFBundleGrokX11(fd, fat->offset, true); + } + } + } + return machtype; +} + +static UInt32 _CFBundleGrokMachType(int fd, void *bytes, CFIndex length, Boolean *isX11, CFDictionaryRef *infodict) { + unsigned int magic = *((UInt32 *)bytes), machtype = UNKNOWN_FILETYPE; + CFIndex i; + + if (isX11) *isX11 = false; + if (infodict) *infodict = NULL; + if (MH_MAGIC == magic) { + machtype = ((struct mach_header *)bytes)->filetype; + if (infodict) *infodict = _CFBundleGrokInfoDictFromFile(fd, 0, false); + if (isX11) *isX11 = _CFBundleGrokX11(fd, 0, false); + } else if (MH_CIGAM == magic) { + for (i = 0; i < length; i += 4) *(UInt32 *)(bytes + i) = CFSwapInt32(*(UInt32 *)(bytes + i)); + machtype = ((struct mach_header *)bytes)->filetype; + if (infodict) *infodict = _CFBundleGrokInfoDictFromFile(fd, 0, true); + if (isX11) *isX11 = _CFBundleGrokX11(fd, 0, true); + } else if (FAT_MAGIC == magic) { + machtype = _CFBundleGrokMachTypeForFatFile(fd, bytes, length, isX11, infodict); + } else if (FAT_CIGAM == magic) { + for (i = 0; i < length; i += 4) *(UInt32 *)(bytes + i) = CFSwapInt32(*(UInt32 *)(bytes + i)); + machtype = _CFBundleGrokMachTypeForFatFile(fd, bytes, length, isX11, infodict); + } else if (PEF_MAGIC == magic || PEF_CIGAM == magic) { + machtype = PEF_FILETYPE; + } + return machtype; +} + +#endif /* BINARY_SUPPORT_DYLD */ + +static UInt32 _CFBundleGrokFileTypeForOLEFile(int fd, unsigned long offset) { + UInt32 filetype = UNKNOWN_FILETYPE; + static const unsigned char xlsname[] = XLS_NAME, docname[] = DOC_NAME, pptname[] = PPT_NAME; + unsigned char moreBytes[512]; + + if (lseek(fd, offset, SEEK_SET) == (off_t)offset && read(fd, moreBytes, sizeof(moreBytes)) >= (int)sizeof(moreBytes)) { + CFIndex i, j; + Boolean foundit = false; + for (i = 0; !foundit && i < 4; i++) { + char namelength = moreBytes[128 * i + 64] / 2; + if (sizeof(xlsname) == namelength) { + for (j = 0, foundit = true; j + 1 < namelength; j++) if (moreBytes[128 * i + 2 * j] != xlsname[j]) foundit = false; + if (foundit) filetype = XLS_FILETYPE; + } else if (sizeof(docname) == namelength) { + for (j = 0, foundit = true; j + 1 < namelength; j++) if (moreBytes[128 * i + 2 * j] != docname[j]) foundit = false; + if (foundit) filetype = DOC_FILETYPE; + } else if (sizeof(pptname) == namelength) { + for (j = 0, foundit = true; j + 1 < namelength; j++) if (moreBytes[128 * i + 2 * j] != pptname[j]) foundit = false; + if (foundit) filetype = PPT_FILETYPE; + } + } + } + return filetype; +} + +static Boolean _CFBundleGrokFileType(CFURLRef url, CFStringRef *extension, UInt32 *machtype, CFDictionaryRef *infodict) { + struct stat statBuf; + int fd = -1; + char path[CFMaxPathSize]; + unsigned char bytes[MAGIC_BYTES_TO_READ]; + CFIndex i, length = 0; + const char *ext = NULL; + UInt32 mt = UNKNOWN_FILETYPE; + Boolean isX11 = false, isPlain = true, isZero = true; + // extensions returned: o, tool, x11app, pef, core, dylib, bundle, jpeg, jp2, tiff, gif, png, pict, icns, ico, rtf, pdf, ra, au, aiff, aifc, wav, avi, wmv, psd, mpeg, mid, zip, jar, sit, html, ps, mov, qtif, bmp, hqx, bin, class, tar, txt, gz, Z, uu, sh, pl, py, rb, dvi, sgi, mp3, xml, plist, xls, doc, ppt, mp4, m4a, m4b, m4p, dmg + if (url && CFURLGetFileSystemRepresentation(url, true, path, CFMaxPathSize) && (fd = open(path, O_RDONLY, 0777)) >= 0 && fstat(fd, &statBuf) == 0 && (statBuf.st_mode & S_IFMT) == S_IFREG) { + if ((length = read(fd, bytes, MAGIC_BYTES_TO_READ)) >= 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) <= length) { + mt = _CFBundleGrokMachType(fd, bytes, length, extension ? &isX11 : NULL, infodict); +#endif /* BINARY_SUPPORT_DYLD */ + } +#if defined(BINARY_SUPPORT_DYLD) + 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"; + } else +#endif /* BINARY_SUPPORT_DYLD */ + if (0x7b5c7274 == magic && (6 > length || 'f' != 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 (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))))) { + // ??? we do not distinguish between different wm types, returning wmv for any of wmv, wma, or asf + ext = NULL; + } else if (0x504b0304 == magic && 38 <= length && 0x4d455441 == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 30))) && 0x2d494e46 == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 34)))) { + ext = "jar"; + } 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 + ext = NULL; + if (52 <= length) { + UInt32 ft = _CFBundleGrokFileTypeForOLEFile(fd, 512 * (1 + CFSwapInt32HostToLittle(*((UInt32 *)(bytes + 48))))); + if (XLS_FILETYPE == ft) { + ext = "xls"; + } else if (DOC_FILETYPE == ft) { + ext = "doc"; + } else if (PPT_FILETYPE == ft) { + ext = "ppt"; + } + } + } 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 (8 <= length && 0x6d6f6f76 == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 4)))) { + ext = "mov"; + } else if (8 <= length && 0x69647363 == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 4)))) { + ext = "qtif"; + } 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 (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 == (statBuf.st_size % 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) == statBuf.st_size) { + 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 (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 == strncmp(bytes + lastSlash + 1, "perl", 4)) { + ext = "pl"; + } else if (0 == strncmp(bytes + lastSlash + 1, "python", 6)) { + ext = "py"; + } else if (0 == strncmp(bytes + lastSlash + 1, "ruby", 4)) { + ext = "rb"; + } else { + ext = "sh"; + } + } + } + } else if (0xffd8 == shortMagic && 0xff == bytes[2]) { + ext = "jpeg"; + } else if (0x4944 == shortMagic && '3' == bytes[2] && 0x20 > bytes[3]) { + ext = "mp3"; + } else if ('<' == bytes[0] && 14 <= length) { + if (0 == strncasecmp(bytes + 1, "!doctype html", 13) || 0 == strncasecmp(bytes + 1, "head", 4) || 0 == strncasecmp(bytes + 1, "title", 5) || 0 == strncasecmp(bytes + 1, "html", 4)) { + ext = "html"; + } else if (0 == strncasecmp(bytes + 1, "?xml", 4)) { + if (116 <= length && 0 == strncasecmp(bytes + 100, "PropertyList.dtd", 16)) { + ext = "plist"; + } else { + ext = "xml"; + } + } + } + } + } + if (extension && !ext) { + //??? what about MacOSRoman? + for (i = 0; (isPlain || isZero) && 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) { + ext = "txt"; + } else if (isZero && length >= MAGIC_BYTES_TO_READ && statBuf.st_size >= 526) { + if (lseek(fd, 512, SEEK_SET) == 512 && read(fd, bytes, 512) >= 14) { + if (0x001102ff == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 10)))) { + ext = "pict"; + } + } + } + } + if (extension && !ext && !isZero && length >= MAGIC_BYTES_TO_READ && statBuf.st_size >= 1024) { + off_t offset = statBuf.st_size - 512; + if (lseek(fd, offset, SEEK_SET) == offset && read(fd, bytes, 512) >= 512) { + if (0x6b6f6c79 == CFSwapInt32HostToBig(*((UInt32 *)bytes)) || (0x63647361 == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 504))) && 0x656e6372 == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 508))))) { + ext = "dmg"; + } + } + } + } + if (extension) *extension = ext ? CFStringCreateWithCStringNoCopy(NULL, ext, kCFStringEncodingASCII, kCFAllocatorNull) : NULL; + if (machtype) *machtype = mt; + close(fd); + return (ext != NULL); +} + +CFStringRef _CFBundleCopyFileTypeForFileURL(CFURLRef url) { + CFStringRef extension = NULL; + (void)_CFBundleGrokFileType(url, &extension, NULL, NULL); + return extension; +} + +__private_extern__ CFDictionaryRef _CFBundleCopyInfoDictionaryInExecutable(CFURLRef url) { + CFDictionaryRef result = NULL; + (void)_CFBundleGrokFileType(url, NULL, NULL, &result); + return result; +} + +#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, &machtype, 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; +} + +Boolean CFBundleLoadExecutable(CFBundleRef bundle) { + Boolean result = false; + CFURLRef executableURL = CFBundleCopyExecutableURL(bundle); + + if (!executableURL) { + bundle->_binaryType = __CFBundleNoBinary; + } +#if defined(BINARY_SUPPORT_DYLD) + // make sure we know whether bundle is already loaded or not + 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); + break; +#endif /* BINARY_SUPPORT_CFM */ +#if defined(BINARY_SUPPORT_DYLD) + case __CFBundleDYLDBundleBinary: + result = _CFBundleDYLDLoadBundle(bundle); + break; + case __CFBundleDYLDFrameworkBinary: + result = _CFBundleDYLDLoadFramework(bundle); + break; + case __CFBundleDYLDExecutableBinary: + CFLog(__kCFLogBundle, CFSTR("Attempt to load executable of a type that cannot be dynamically loaded for %@"), bundle); + break; +#endif /* BINARY_SUPPORT_DYLD */ +#if defined(BINARY_SUPPORT_DLL) + case __CFBundleDLLBinary: + result = _CFBundleDLLLoad(bundle); + break; +#endif /* BINARY_SUPPORT_DLL */ + case __CFBundleNoBinary: + CFLog(__kCFLogBundle, CFSTR("Cannot find executable for %@"), bundle); + break; + default: + CFLog(__kCFLogBundle, CFSTR("Cannot recognize type of executable for %@"), bundle); + break; + } + + return result; +} + +void CFBundleUnloadExecutable(CFBundleRef bundle) { + + if (!_scheduledBundlesAreUnloading) { + // First unload bundles scheduled for unloading (if that's not what we are already doing.) + _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: + _CFBundleDYLDUnloadBundle(bundle); + break; +#endif /* BINARY_SUPPORT_DYLD */ +#if defined(BINARY_SUPPORT_DLL) + case __CFBundleDLLBinary: + _CFBundleDLLUnload(bundle); + break; +#endif /* BINARY_SUPPORT_DLL */ + default: + break; + } + if (!bundle->_isLoaded && bundle->_glueDict != NULL) { + 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(NULL, 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 = CFAllocatorAllocate(NULL, sizeof(CFBundleRef) * c, 0); + CFSetGetValues(_bundlesToUnload, (const void **)unloadThese); + _scheduledBundlesAreUnloading = true; + for (i=0; i_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: + 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: + break; + } +#if defined(BINARY_SUPPORT_DYLD) && defined(BINARY_SUPPORT_CFM) && defined(__ppc__) + if (tvp != NULL) { + if (bundle->_glueDict == NULL) { + bundle->_glueDict = CFDictionaryCreateMutable(CFGetAllocator(bundle), 0, NULL, NULL); + } + void *fp = (void *)CFDictionaryGetValue(bundle->_glueDict, tvp); + if (fp == NULL) { + fp = _CFBundleFunctionPointerForTVector(CFGetAllocator(bundle), tvp); + CFDictionarySetValue(bundle->_glueDict, tvp, fp); + } + return fp; + } +#endif /* BINARY_SUPPORT_DYLD && BINARY_SUPPORT_CFM && __ppc__ */ + return tvp; +} + +void *_CFBundleGetCFMFunctionPointerForName(CFBundleRef bundle, CFStringRef funcName) { + void *fp = NULL; + // Load if necessary + if (!bundle->_isLoaded) { + if (!CFBundleLoadExecutable(bundle)) return NULL; + } + + 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: + fp = _CFBundleDYLDGetSymbolByNameWithSearch(bundle, funcName, true); + break; +#endif /* BINARY_SUPPORT_DYLD */ + default: + break; + } +#if defined(BINARY_SUPPORT_DYLD) && defined(BINARY_SUPPORT_CFM) && defined(__ppc__) + if (fp != NULL) { + if (bundle->_glueDict == NULL) { + bundle->_glueDict = CFDictionaryCreateMutable(CFGetAllocator(bundle), 0, NULL, NULL); + } + void *tvp = (void *)CFDictionaryGetValue(bundle->_glueDict, fp); + if (tvp == NULL) { + tvp = _CFBundleTVectorForFunctionPointer(CFGetAllocator(bundle), fp); + CFDictionarySetValue(bundle->_glueDict, fp, tvp); + } + return tvp; + } +#endif /* BINARY_SUPPORT_DYLD && BINARY_SUPPORT_CFM && __ppc__ */ + 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, 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, 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: + 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: + 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, 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(NULL, url, &exists, &mode, NULL, NULL, NULL, NULL) == 0) { + result = (exists && ((mode & S_IFMT) == S_IFDIR)); +#if !defined(__MACOS8__) + result = (result && ((mode & 0444) != 0)); +#endif + } + return result; +} + +__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 defined(__WIN32__) + 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); + 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 defined(__WIN32__) + // * (Windows-only) First check the "Executables" directory parallel to the "Frameworks" directory case. + _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; + _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 == NULL) { + // 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(NULL, imagePath); + + if (curURL != NULL) { + bundle = _CFBundleFindByURL(curURL, true); + if (bundle == NULL) { + bundle = _CFBundleCreate(NULL, curURL, true); + } + if (bundle != NULL && !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_DYLD) + if (bundle->_binaryType == __CFBundleUnknownBinary) { + bundle->_binaryType = __CFBundleDYLDFrameworkBinary; + } + if (bundle->_binaryType != __CFBundleCFMBinary && bundle->_binaryType != __CFBundleUnreadableBinary) { + bundle->_resourceData._executableLacksResourceFork = true; + } + if (!bundle->_imageCookie) _CFBundleDYLDCheckLoaded(bundle); +#endif /* BINARY_SUPPORT_DYLD */ + bundle->_isLoaded = true; + } + 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_version;} + +CF_EXPORT CFURLRef _CFBundleCopyInfoPlistURL(CFBundleRef bundle) { + CFDictionaryRef infoDict = CFBundleGetInfoDictionary(bundle); + CFStringRef path = CFDictionaryGetValue(infoDict, _kCFBundleInfoPlistURLKey); + return (path ? CFRetain(path) : 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 void *__CFBundleDYLDFindImage(char *buff) { + void *header = NULL; + unsigned long i, numImages = _dyld_image_count(), numMatches = 0; + 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); + + if (executableURL != NULL) { + char buff[CFMaxPathSize]; + + if (CFURLGetFileSystemRepresentation(executableURL, true, buff, CFMaxPathSize)) { + void *header = __CFBundleDYLDFindImage(buff); + if (header) { + if (bundle->_binaryType == __CFBundleUnknownBinary) { + bundle->_binaryType = __CFBundleDYLDFrameworkBinary; + } + if (!bundle->_imageCookie) bundle->_imageCookie = header; + bundle->_isLoaded = true; + } + } + CFRelease(executableURL); + } + } + return bundle->_isLoaded; +} + +__private_extern__ Boolean _CFBundleDYLDLoadBundle(CFBundleRef bundle) { + NSLinkEditErrors c = NSLinkEditUndefinedError; + int errorNumber = 0; + const char *fileName = NULL; + const char *errorString = NULL; + + if (!bundle->_isLoaded) { + CFURLRef executableURL = CFBundleCopyExecutableURL(bundle); + + if (executableURL) { + char buff[CFMaxPathSize]; + + if (CFURLGetFileSystemRepresentation(executableURL, true, buff, CFMaxPathSize)) { + NSObjectFileImage image; + NSObjectFileImageReturnCode retCode = NSCreateObjectFileImageFromFile(buff, &image); + if (retCode == NSObjectFileImageSuccess) { + NSModule module = NSLinkModule(image, buff, (NSLINKMODULE_OPTION_BINDNOW | NSLINKMODULE_OPTION_PRIVATE | NSLINKMODULE_OPTION_RETURN_ON_ERROR)); + 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 (!NSDestroyObjectFileImage(image)) { + /* MF:!!! Error destroying object file image */ + } + } + } else { + CFLog(__kCFLogBundle, CFSTR("dyld returns %d when trying to load %@"), retCode, executableURL); + } + } + CFRelease(executableURL); + } else { + CFLog(__kCFLogBundle, CFSTR("Cannot find executable for bundle %@"), bundle); + } + } + return bundle->_isLoaded; +} + +__private_extern__ Boolean _CFBundleDYLDLoadFramework(CFBundleRef bundle) { + // !!! Framework loading should be better. Can't unload frameworks. + NSLinkEditErrors c = NSLinkEditUndefinedError; + int errorNumber = 0; + const char *fileName = NULL; + const char *errorString = NULL; + + if (!bundle->_isLoaded) { + CFURLRef executableURL = CFBundleCopyExecutableURL(bundle); + + if (executableURL) { + char buff[CFMaxPathSize]; + + if (CFURLGetFileSystemRepresentation(executableURL, true, buff, CFMaxPathSize)) { + void *image = (void *)NSAddImage(buff, NSADDIMAGE_OPTION_RETURN_ON_ERROR); + 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); + } + } + CFRelease(executableURL); + } else { + CFLog(__kCFLogBundle, CFSTR("Cannot find executable for bundle %@"), bundle); + } + } + return bundle->_isLoaded; +} + +__private_extern__ void _CFBundleDYLDUnloadBundle(CFBundleRef bundle) { + if (bundle->_isLoaded) { + if (bundle->_moduleCookie && !NSUnLinkModule(bundle->_moduleCookie, NSUNLINKMODULE_OPTION_NONE)) { + CFLog(__kCFLogBundle, CFSTR("Internal error unloading bundle %@"), bundle); + } else { + if (bundle->_moduleCookie && bundle->_imageCookie && !NSDestroyObjectFileImage(bundle->_imageCookie)) { + /* MF:!!! Error destroying object file image */ + } + bundle->_connectionCookie = 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; + + // MF:!!! What if the factory was in C++ code (and is therefore mangled differently)? Huh, answer me that! + // MF: The current answer to the mangling question is that you cannot look up C++ functions with this API. Thus things like plugin factories must be extern "C" in plugin code. + buff[0] = '_'; + /* MF:??? ASCII appropriate here? */ + if (CFStringGetCString(symbolName, &(buff[1]), 1024, kCFStringEncodingASCII)) { + if (bundle->_moduleCookie) { + symbol = NSLookupSymbolInModule(bundle->_moduleCookie, buff); + } else if (bundle->_imageCookie) { + symbol = NSLookupSymbolInImage(bundle->_imageCookie, buff, NSLOOKUPSYMBOLINIMAGE_OPTION_BIND|NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR); + } + if (NULL == symbol && NULL == bundle->_moduleCookie && (NULL == bundle->_imageCookie || globalSearch)) { + char hintBuff[1026]; + CFStringRef executableName = _CFBundleCopyExecutableName(NULL, bundle, NULL, NULL); + hintBuff[0] = '\0'; + if (executableName) { + if (!CFStringGetCString(executableName, hintBuff, 1024, kCFStringEncodingUTF8)) hintBuff[0] = '\0'; + CFRelease(executableName); + } + if (NSIsSymbolNameDefinedWithHint(buff, hintBuff)) { + symbol = NSLookupAndBindSymbolWithHint(buff, hintBuff); + } + } + if (symbol) { + result = NSAddressOfSymbol(symbol); + } else { +#if defined(DEBUG) + CFLog(__kCFLogBundle, CFSTR("dyld cannot find symbol %s in %@"), buff, bundle); +#endif + } + } + return result; +} + +static CFStringRef _CFBundleDYLDCopyLoadedImagePathForPointer(void *p) { + unsigned long i, j, n = _dyld_image_count(); + Boolean foundit = false; + char *name; + CFStringRef result = NULL; + for (i = 0; !foundit && i < n; i++) { + struct mach_header *mh = _dyld_get_image_header(i); + unsigned long addr = (unsigned long)p - _dyld_get_image_vmaddr_slide(i); + if (mh) { + struct load_command *lc = (struct load_command *)((char *)mh + sizeof(struct mach_header)); + for (j = 0; !foundit && j < mh->ncmds; j++, lc = (struct load_command *)((char *)lc + lc->cmdsize)) { + if (LC_SEGMENT == lc->cmd && addr >= ((struct segment_command *)lc)->vmaddr && addr < ((struct segment_command *)lc)->vmaddr + ((struct segment_command *)lc)->vmsize) { + foundit = true; + name = _dyld_get_image_name(i); + if (name != NULL) { + result = CFStringCreateWithCString(NULL, name, CFStringFileSystemEncoding()); + } + } + } + } + } + return result; +} + +__private_extern__ CFArrayRef _CFBundleDYLDCopyLoadedImagePathsForHint(CFStringRef hint) { + unsigned long i, numImages = _dyld_image_count(); + CFMutableArrayRef result = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + CFRange range = CFRangeMake(0, CFStringGetLength(hint)); + + for (i=0; i_isLoaded) { + CFURLRef executableURL = CFBundleCopyExecutableURL(bundle); + + if (executableURL) { + char buff[CFMaxPathSize]; + + if (CFURLGetFileSystemRepresentation(executableURL, true, buff, CFMaxPathSize)) { + bundle->_hModule = LoadLibrary(buff); + if (bundle->_hModule == NULL) { + } else { + bundle->_isLoaded = true; + } + } + CFRelease(executableURL); + } else { + CFLog(__kCFLogBundle, CFSTR("Cannot find executable for bundle %@"), bundle); + } + } + + 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/PlugIn.subproj/CFBundle.h b/PlugIn.subproj/CFBundle.h new file mode 100644 index 0000000..23b481a --- /dev/null +++ b/PlugIn.subproj/CFBundle.h @@ -0,0 +1,321 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFBundle.h + Copyright (c) 1999-2003, Apple, Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFBUNDLE__) +#define __COREFOUNDATION_CFBUNDLE__ 1 + +#include +#include +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +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. Clients should use CFBundleGetVersionNumber() instead of accessing this key directly +since that function will properly convert a version number in string format into its interger 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. */ +#if MAC_OS_X_VERSION_10_2 <= MAC_OS_X_VERSION_MAX_ALLOWED +CF_EXPORT +const CFStringRef kCFBundleLocalizationsKey; + /* Allows an unbundled application that handles localization itself to specify which localizations it has available. */ +#endif + +/* ===================== 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 +UInt32 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) */ + +/* ==================== 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. */ + +#if MAC_OS_X_VERSION_10_2 <= MAC_OS_X_VERSION_MAX_ALLOWED +CF_EXPORT +CFArrayRef CFBundleCopyLocalizationsForPreferences(CFArrayRef locArray, CFArrayRef prefArray); + /* 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. */ +#endif + +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); + +#if MAC_OS_X_VERSION_10_2 <= MAC_OS_X_VERSION_MAX_ALLOWED +/* =================== 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); + /* For a directory URL, this is equivalent to CFBundleCopyInfoDictionaryInDirectory. */ + /* For a plain file URL representing an unbundled application, this will attempt to read */ + /* an info dictionary either from the (__TEXT, __info_plist) section (for a Mach-o file) */ + /* or from a 'plst' resource. */ + +CF_EXPORT +CFArrayRef CFBundleCopyLocalizationsForURL(CFURLRef url); + /* For a directory URL, this is equivalent to calling CFBundleCopyBundleLocalizations */ + /* on the corresponding bundle. For a plain file URL representing an unbundled application, */ + /* this will attempt to determine its localizations using the CFBundleLocalizations and */ + /* CFBundleDevelopmentRegion keys in the dictionary returned by CFBundleCopyInfoDictionaryForURL,*/ + /* or a 'vers' resource if those are not present. */ +#endif + +/* ==================== 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. */ +/* Note that Cocoa-based bundles containing Objective-C or Java code must */ +/* be loaded with NSBundle, not CFBundle. Once they are loaded, however, */ +/* either CFBundle or NSBundle can be used. */ + +CF_EXPORT +CFURLRef CFBundleCopyExecutableURL(CFBundleRef bundle); + +CF_EXPORT +Boolean CFBundleIsExecutableLoaded(CFBundleRef bundle); + +CF_EXPORT +Boolean CFBundleLoadExecutable(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 ==================== */ + +CF_EXPORT +short 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, short *refNum, short *localizedRefNum); + /* Similar to CFBundleOpenBundleResourceMap, except that it creates two */ + /* separate resource maps and returns reference numbers for both. */ + +CF_EXPORT +void CFBundleCloseBundleResourceMap(CFBundleRef bundle, short refNum); + +#if defined(__cplusplus) +} +#endif + +#endif /* ! __COREFOUNDATION_CFBUNDLE__ */ + diff --git a/PlugIn.subproj/CFBundlePriv.h b/PlugIn.subproj/CFBundlePriv.h new file mode 100644 index 0000000..882dd19 --- /dev/null +++ b/PlugIn.subproj/CFBundlePriv.h @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFBundlePriv.h + Copyright (c) 1999-2003, Apple, Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFBUNDLEPRIV__) +#define __COREFOUNDATION_CFBUNDLEPRIV__ 1 + +#include +#include +#include +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +/* 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 +CFBundleRef _CFBundleCreateWithExecutableURLIfLooksLikeBundle(CFAllocatorRef allocator, CFURLRef url); + + +/* 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 +Boolean _CFBundleGetHasChanged(CFBundleRef bundle); + +CF_EXPORT +void _CFBundleFlushCaches(void); + +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 +short _CFBundleOpenBundleResourceFork(CFBundleRef bundle); // deprecated in favor of CFBundleOpenBundleResourceMap + +CF_EXPORT +void _CFBundleCloseBundleResourceFork(CFBundleRef bundle); // deprecated in favor of CFBundleCloseBundleResourceMap + +#if defined(__cplusplus) +} +#endif + +#endif /* ! __COREFOUNDATION_CFBUNDLEPRIV__ */ + diff --git a/PlugIn.subproj/CFBundle_BinaryTypes.h b/PlugIn.subproj/CFBundle_BinaryTypes.h new file mode 100644 index 0000000..9550885 --- /dev/null +++ b/PlugIn.subproj/CFBundle_BinaryTypes.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFBundle_BinaryTypes.h + Copyright (c) 1999-2003, Apple, Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFBUNDLE_BINARYTYPES__) +#define __COREFOUNDATION_CFBUNDLE_BINARYTYPES__ 1 + +#if defined(__cplusplus) +extern "C" { +#endif + +/* We support CFM on OS8 and on PPC OSX */ + +/* We support DYLD on OSX only */ +#if defined(__MACH__) +#define BINARY_SUPPORT_DYLD 1 +#endif + +/* We support DLL on Windows only */ +#if defined(__WIN32__) +#define BINARY_SUPPORT_DLL 1 +#endif + +typedef enum { + __CFBundleUnknownBinary, + __CFBundleCFMBinary, + __CFBundleDYLDExecutableBinary, + __CFBundleDYLDBundleBinary, + __CFBundleDYLDFrameworkBinary, + __CFBundleDLLBinary, + __CFBundleUnreadableBinary, + __CFBundleNoBinary +} __CFPBinaryType; + +#if defined(__cplusplus) +} +#endif + +#endif /* ! __COREFOUNDATION_CFBUNDLE_BINARYTYPES__ */ + diff --git a/PlugIn.subproj/CFBundle_Internal.h b/PlugIn.subproj/CFBundle_Internal.h new file mode 100644 index 0000000..1c8fb09 --- /dev/null +++ b/PlugIn.subproj/CFBundle_Internal.h @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFBundle_Internal.h + Copyright (c) 1999-2003, Apple, Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFBUNDLE_INTERNAL__) +#define __COREFOUNDATION_CFBUNDLE_INTERNAL__ 1 + +#include +#include +#include +#include "CFInternal.h" +#include "CFPlugIn_Factory.h" +#include "CFBundle_BinaryTypes.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +#define __kCFLogBundle 21 +#define __kCFLogPlugIn 22 + +#if defined(__WIN32) +#define PLATFORM_PATH_STYLE kCFURLWindowsPathStyle +#elif defined (__MACOS8__) +#define PLATFORM_PATH_STYLE kCFURLHFSPathStyle +#else +#define PLATFORM_PATH_STYLE kCFURLPOSIXPathStyle +#endif + +typedef struct __CFResourceData { + CFMutableDictionaryRef _stringTableCache; + Boolean _executableLacksResourceFork; + char _padding[3]; +} _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 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); +extern Boolean _CFBundleDYLDLoadFramework(CFBundleRef bundle); +extern void _CFBundleDYLDUnloadBundle(CFBundleRef bundle); +extern void *_CFBundleDYLDGetSymbolByName(CFBundleRef bundle, CFStringRef symbolName); + +extern CFArrayRef _CFBundleDYLDCopyLoadedImagePathsIfChanged(void); +extern CFArrayRef _CFBundleDYLDCopyLoadedImagePathsForHint(CFStringRef hint); +#endif + +#if defined(BINARY_SUPPORT_CFM) +// CFM API +#if defined(__MACOS8__) +#include +#else +#include +#endif +extern Boolean _CFBundleCFMLoad(CFBundleRef bundle); +extern void _CFBundleCFMConnect(CFBundleRef bundle); +extern void _CFBundleCFMUnload(CFBundleRef bundle); +extern void *__CFBundleCFMGetSymbol(void *connID, ConstStr255Param name, CFragSymbolClass class); +extern void *_CFBundleCFMGetSymbolByName(CFBundleRef bundle, CFStringRef symbolName, CFragSymbolClass class); +#endif /* BINARY_SUPPORT_CFM */ + +#if defined(BINARY_SUPPORT_DLL) +extern Boolean _CFBundleDLLLoad(CFBundleRef bundle); +extern void _CFBundleDLLUnload(CFBundleRef bundle); +extern void *_CFBundleDLLGetSymbolByName(CFBundleRef bundle, CFStringRef symbolName); +#endif BINARY_SUPPORT_DLL + + +/* Private PlugIn-related CFBundle API */ + +extern void _CFBundleInitPlugIn(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 _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") + +/* 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") + +#if defined(__cplusplus) +} +#endif + +#endif /* ! __COREFOUNDATION_CFBUNDLE_INTERNAL__ */ + diff --git a/PlugIn.subproj/CFBundle_Resources.c b/PlugIn.subproj/CFBundle_Resources.c new file mode 100644 index 0000000..cd7b195 --- /dev/null +++ b/PlugIn.subproj/CFBundle_Resources.c @@ -0,0 +1,1977 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFBundle_Resources.c + Copyright 1999-2002, Apple, Inc. All rights reserved. + Responsibility: Doug Davidson +*/ + +#if defined(__MACOS8__) || defined(__WIN32__) +#define USE_GETDIRENTRIES 0 +#else +#define USE_GETDIRENTRIES 1 +#endif +#define GETDIRENTRIES_CACHE_CAPACITY 100 + +#include "CFBundle_Internal.h" +#include +#include +#include +#include +#include +#include "CFInternal.h" +#include "CFPriv.h" + +#if defined(__MACOS8__) +/* MacOS8 Headers */ +#include +#include +#include +#include +#include +#include +#include +#else +/* Unixy & Windows Headers */ +#include +#include +#include +#include +#if USE_GETDIRENTRIES +#include +#endif +#endif + + + +// All new-style bundles will have these extensions. +CF_INLINE CFStringRef _CFGetPlatformName(void) { + // MF:!!! This used to be based on NSInterfaceStyle, not hard-wired by compiler. +#if defined(__WIN32__) + return _CFBundleWindowsPlatformName; +#elif defined(__MACOS8__) + return _CFBundleMacOS8PlatformName; +#elif defined (__MACH__) + return _CFBundleMacOSXPlatformName; +#elif defined(__svr4__) + return _CFBundleSolarisPlatformName; +#elif defined(__hpux__) + return _CFBundleHPUXPlatformName; +#elif defined(__LINUX__) + return _CFBundleLinuxPlatformName; +#elif defined(__FREEBSD__) + return _CFBundleFreeBSDPlatformName; +#else + return CFSTR(""); +#endif +} + +CF_INLINE CFStringRef _CFGetAlternatePlatformName(void) { +#if defined (__MACH__) + return _CFBundleAlternateMacOSXPlatformName; +#elif defined(__MACOS8__) + return _CFBundleAlternateMacOS8PlatformName; +#else + return CFSTR(""); +#endif +} + +static CFSpinLock_t CFBundleResourceGlobalDataLock = 0; +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 = 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 == NULL) { + _CFBundleInitStaticUniCharBuffers(); + } + __CFSpinUnlock(&CFBundleResourceGlobalDataLock); +} + +#if USE_GETDIRENTRIES + +static CFMutableDictionaryRef contentsCache = NULL; +static CFMutableDictionaryRef directoryContentsCache = NULL; + +static CFArrayRef _CFBundleCopyDirectoryContentsAtPath(CFStringRef path, Boolean directoriesOnly) { + CFArrayRef result = NULL; + + __CFSpinLock(&CFBundleResourceGlobalDataLock); + if (directoriesOnly) { + if (directoryContentsCache) result = (CFMutableArrayRef)CFDictionaryGetValue(directoryContentsCache, path); + } else { + if (contentsCache) result = (CFMutableArrayRef)CFDictionaryGetValue(contentsCache, path); + } + if (result) CFRetain(result); + __CFSpinUnlock(&CFBundleResourceGlobalDataLock); + + if (!result) { + char cpathBuff[CFMaxPathSize], dirge[8192]; + CFIndex cpathLen = 0; + int fd = -1, numread; + long basep = 0; + CFMutableArrayRef contents = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks), directoryContents = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + CFStringRef name; + + if (_CFStringGetFileSystemRepresentation(path, cpathBuff, CFMaxPathSize)) { + cpathLen = strlen(cpathBuff); + fd = open(cpathBuff, O_RDONLY, 0777); + } + if (fd >= 0) { + while ((numread = getdirentries(fd, dirge, sizeof(dirge), &basep)) > 0) { + struct dirent *dent; + for (dent = (struct dirent *)dirge; dent < (struct dirent *)(dirge + numread); dent = (struct dirent *)((char *)dent + dent->d_reclen)) { + CFIndex nameLen = strlen(dent->d_name); + if (0 == dent->d_fileno || (dent->d_name[0] == '.' && (nameLen == 1 || (nameLen == 2 && dent->d_name[1] == '.')))) continue; + name = CFStringCreateWithCString(NULL, dent->d_name, CFStringFileSystemEncoding()); + if (NULL != name) { + CFArrayAppendValue(contents, name); + if (dent->d_type == DT_DIR) { + CFArrayAppendValue(directoryContents, name); + } else if (dent->d_type == DT_UNKNOWN) { + struct stat statBuf; + cpathBuff[cpathLen] = '/'; + strncpy(cpathBuff + cpathLen + 1, dent->d_name, nameLen); + cpathBuff[cpathLen + nameLen + 1] = '\0'; + if (stat(cpathBuff, &statBuf) == 0 && (statBuf.st_mode & S_IFMT) == S_IFDIR) CFArrayAppendValue(directoryContents, name); + cpathBuff[cpathLen] = '\0'; + } + CFRelease(name); + } + } + } + close(fd); + } + + __CFSpinLock(&CFBundleResourceGlobalDataLock); + if (!contentsCache) contentsCache = CFDictionaryCreateMutable(NULL, GETDIRENTRIES_CACHE_CAPACITY, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + if (GETDIRENTRIES_CACHE_CAPACITY == CFDictionaryGetCount(contentsCache)) CFDictionaryRemoveAllValues(contentsCache); + CFDictionaryAddValue(contentsCache, path, contents); + + if (!directoryContentsCache) directoryContentsCache = CFDictionaryCreateMutable(NULL, GETDIRENTRIES_CACHE_CAPACITY, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + if (GETDIRENTRIES_CACHE_CAPACITY == CFDictionaryGetCount(directoryContentsCache)) CFDictionaryRemoveAllValues(directoryContentsCache); + CFDictionaryAddValue(directoryContentsCache, path, directoryContents); + + result = CFRetain(directoriesOnly ? directoryContents : contents); + CFRelease(contents); + CFRelease(directoryContents); + __CFSpinUnlock(&CFBundleResourceGlobalDataLock); + } + + return result; +} + +static void _CFBundleFlushContentsCaches(void) { + __CFSpinLock(&CFBundleResourceGlobalDataLock); + if (contentsCache) CFDictionaryRemoveAllValues(contentsCache); + if (directoryContentsCache) CFDictionaryRemoveAllValues(directoryContentsCache); + __CFSpinUnlock(&CFBundleResourceGlobalDataLock); +} + +#endif /* USE_GETDIRENTRIES */ + +CF_EXPORT void _CFBundleFlushCaches(void) { +#if USE_GETDIRENTRIES + _CFBundleFlushContentsCaches(); +#endif /* USE_GETDIRENTRIES */ +} + +__private_extern__ Boolean _CFIsResourceAtURL(CFURLRef url, Boolean *isDir) { + Boolean exists; + SInt32 mode; + if (_CFGetFileProperties(NULL, url, &exists, &mode, NULL, NULL, NULL, NULL) == 0) { + if (isDir) { + *isDir = ((exists && ((mode & S_IFMT) == S_IFDIR)) ? true : false); + } +#if defined(__MACOS8__) + return (exists); +#else + return (exists && (mode & 0444)); +#endif /* __MACOS8__ */ + } 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 != NULL) { + result = _CFIsResourceAtURL(url, isDir); + CFRelease(url); + } + return result; +} + +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 platformGenericFound = false, platformSpecificFound = false; + Boolean platformGenericIsDir = false, platformSpecificIsDir = false; + CFStringRef platformGenericStr = NULL; + +#if USE_GETDIRENTRIES + CFIndex dirPathLen = pathLen; + CFArrayRef contents, directoryContents; + CFRange contentsRange, directoryContentsRange; + + CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, dirPathLen, dirPathLen); + CFStringReplaceAll(cheapStr, tmpString); + //fprintf(stderr, "looking in ");CFShow(cheapStr); + contents = _CFBundleCopyDirectoryContentsAtPath(cheapStr, false); + contentsRange = CFRangeMake(0, CFArrayGetCount(contents)); + directoryContents = _CFBundleCopyDirectoryContentsAtPath(cheapStr, true); + directoryContentsRange = CFRangeMake(0, CFArrayGetCount(directoryContents)); +#endif + + if (nameLen > 0) { + _CFAppendPathComponent(pathUniChars, &pathLen, CFMaxPathSize, nameUniChars, nameLen); + } + // Save length with just name appended. + savedPathLen = pathLen; + + // Check platform generic + if (typeLen > 0) { + _CFAppendPathExtension(pathUniChars, &pathLen, CFMaxPathSize, typeUniChars, typeLen); + } +#if USE_GETDIRENTRIES + CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars + dirPathLen + 1, pathLen - dirPathLen - 1, pathLen - dirPathLen - 1); + CFStringReplaceAll(cheapStr, tmpString); + platformGenericFound = CFArrayContainsValue(contents, contentsRange, cheapStr); + platformGenericIsDir = CFArrayContainsValue(directoryContents, directoryContentsRange, 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); +#else + CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, pathLen, pathLen); + CFStringReplaceAll(cheapStr, tmpString); + platformGenericFound = _CFIsResourceAtPath(cheapStr, &platformGenericIsDir); +#endif + + // Check for platform specific. + if (platformGenericFound) { + platformGenericStr = CFStringCreateCopy(alloc, cheapStr); + if (!platformSpecificFound && (_PlatformLen > 0)) { + pathLen = savedPathLen; + pathUniChars[pathLen++] = (UniChar)'-'; + memmove(pathUniChars + pathLen, _PlatformUniChars, _PlatformLen * sizeof(UniChar)); + pathLen += _PlatformLen; + if (typeLen > 0) { + _CFAppendPathExtension(pathUniChars, &pathLen, CFMaxPathSize, typeUniChars, typeLen); + } +#if USE_GETDIRENTRIES + CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars + dirPathLen + 1, pathLen - dirPathLen - 1, pathLen - dirPathLen - 1); + CFStringReplaceAll(cheapStr, tmpString); + platformSpecificFound = CFArrayContainsValue(contents, contentsRange, cheapStr); + platformSpecificIsDir = CFArrayContainsValue(directoryContents, directoryContentsRange, 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); +#else + CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, pathLen, pathLen); + CFStringReplaceAll(cheapStr, tmpString); + platformSpecificFound = _CFIsResourceAtPath(cheapStr, &platformSpecificIsDir); +#endif + } + } + if (platformSpecificFound) { + CFURLRef url = CFURLCreateWithFileSystemPath(alloc, cheapStr, PLATFORM_PATH_STYLE, platformSpecificIsDir); + CFArrayAppendValue(result, url); + CFRelease(url); + } else if (platformGenericFound) { + CFURLRef url = CFURLCreateWithFileSystemPath(alloc, ((platformGenericStr != NULL) ? platformGenericStr : cheapStr), PLATFORM_PATH_STYLE, platformGenericIsDir); + CFArrayAppendValue(result, url); + CFRelease(url); + } + if (platformGenericStr != NULL) { + CFRelease(platformGenericStr); + } +#if USE_GETDIRENTRIES + CFRelease(contents); + CFRelease(directoryContents); +#endif +} + +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 { + CFIndex i, c = CFArrayGetCount(resTypes); + for (i=0; i 0) { + _CFAppendPathComponent(workingUniChars, &workingLen, CFMaxPathSize, subDirUniChars, subDirLen); + } + _CFFindBundleResourcesInRawDir(alloc, workingUniChars, workingLen, nameUniChars, nameLen, resTypes, limit, version, cheapStr, tmpString, result); + + // Strip the non-localized resource directory. + workingLen = savedWorkingLen; + } + if (CFArrayGetCount(result) < limit) { + if (subDirLen > 0) { + _CFAppendPathComponent(workingUniChars, &workingLen, CFMaxPathSize, subDirUniChars, subDirLen); + } + _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 = CFArrayGetValueAtIndex(searchLanguages, langIndex); + curLangLen = CFStringGetLength(curLangStr); + 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 != NULL) ? CFGetAllocator(bundle) : 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 = 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 != NULL) ? 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 == NULL) { + CFRelease(alloc); + } + + return result; +} + +CF_EXPORT CFURLRef CFBundleCopyResourceURL(CFBundleRef bundle, CFStringRef resourceName, CFStringRef resourceType, CFStringRef subDirName) { + CFURLRef result = NULL; + CFArrayRef languages = _CFBundleGetLanguageSearchList(bundle); + CFMutableArrayRef types = NULL; + CFArrayRef array; + + if (resourceType) { + types = CFArrayCreateMutable(CFGetAllocator(bundle), 1, &kCFTypeArrayCallBacks); + CFArrayAppendValue(types, resourceType); + } + + array = _CFFindBundleResources(bundle, NULL, subDirName, languages, resourceName, types, 1, _CFBundleLayoutVersion(bundle)); + + if (types) { + CFRelease(types); + } + + if (array) { + if (CFArrayGetCount(array) > 0) { + result = CFRetain(CFArrayGetValueAtIndex(array, 0)); + } + CFRelease(array); + } + return result; +} + +CF_EXPORT CFArrayRef CFBundleCopyResourceURLsOfType(CFBundleRef bundle, CFStringRef resourceType, CFStringRef subDirName) { + CFArrayRef languages = _CFBundleGetLanguageSearchList(bundle); + CFMutableArrayRef types = NULL; + CFArrayRef array; + + if (resourceType) { + types = CFArrayCreateMutable(CFGetAllocator(bundle), 1, &kCFTypeArrayCallBacks); + CFArrayAppendValue(types, resourceType); + } + + // 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; + CFMutableArrayRef languages = CFArrayCreateMutable(CFGetAllocator(bundle), 1, &kCFTypeArrayCallBacks); + CFMutableArrayRef types = NULL; + CFArrayRef array; + + if (localizationName) CFArrayAppendValue(languages, localizationName); + + if (resourceType) { + types = CFArrayCreateMutable(CFGetAllocator(bundle), 1, &kCFTypeArrayCallBacks); + CFArrayAppendValue(types, resourceType); + } + + array = _CFFindBundleResources(bundle, NULL, subDirName, languages, resourceName, types, 1, _CFBundleLayoutVersion(bundle)); + + if (types) { + CFRelease(types); + } + + if (array) { + if (CFArrayGetCount(array) > 0) { + result = CFRetain(CFArrayGetValueAtIndex(array, 0)); + } + CFRelease(array); + } + + 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) { + CFMutableArrayRef languages = CFArrayCreateMutable(CFGetAllocator(bundle), 1, &kCFTypeArrayCallBacks); + CFMutableArrayRef types = NULL; + CFArrayRef array; + + if (localizationName) CFArrayAppendValue(languages, localizationName); + + if (resourceType) { + types = CFArrayCreateMutable(CFGetAllocator(bundle), 1, &kCFTypeArrayCallBacks); + CFArrayAppendValue(types, resourceType); + } + + // MF:!!! Better "limit" than 1,000,000? + array = _CFFindBundleResources(bundle, NULL, subDirName, languages, NULL, types, 1000000, _CFBundleLayoutVersion(bundle)); + + if (types) { + CFRelease(types); + } + + CFRelease(languages); + + return array; +} + +CF_EXPORT CFStringRef CFBundleCopyLocalizedString(CFBundleRef bundle, CFStringRef key, CFStringRef value, CFStringRef tableName) { + CFStringRef result = NULL; + CFDictionaryRef stringTable = NULL; + + if (key == NULL) return (value ? CFRetain(value) : CFRetain(CFSTR(""))); + + if ((tableName == NULL) || CFEqual(tableName, CFSTR(""))) { + tableName = _CFBundleDefaultStringTableName; + } + if (__CFBundleGetResourceData(bundle)->_stringTableCache != NULL) { + // See if we have the table cached. + stringTable = CFDictionaryGetValue(__CFBundleGetResourceData(bundle)->_stringTableCache, tableName); + } + if (stringTable == NULL) { + // Go load the table. + CFURLRef tableURL = CFBundleCopyResourceURL(bundle, tableName, _CFBundleStringTableType, NULL); + if (tableURL) { + CFStringRef nameForSharing = NULL; + if (stringTable == NULL) { + CFDataRef tableData = NULL; + SInt32 errCode; + CFStringRef errStr; + if (CFURLCreateDataAndPropertiesFromResource(CFGetAllocator(bundle), tableURL, &tableData, NULL, NULL, &errCode)) { + stringTable = CFPropertyListCreateFromXMLData(CFGetAllocator(bundle), tableData, kCFPropertyListImmutable, &errStr); + if (errStr != NULL) { + CFRelease(errStr); + errStr = NULL; + } + CFRelease(tableData); + } + } + if (nameForSharing) CFRelease(nameForSharing); + CFRelease(tableURL); + } + if (stringTable == NULL) { + stringTable = CFDictionaryCreate(CFGetAllocator(bundle), NULL, NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + } + if (__CFBundleGetResourceData(bundle)->_stringTableCache == NULL) { + __CFBundleGetResourceData(bundle)->_stringTableCache = CFDictionaryCreateMutable(CFGetAllocator(bundle), 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + } + CFDictionarySetValue(__CFBundleGetResourceData(bundle)->_stringTableCache, tableName, stringTable); + CFRelease(stringTable); + } + + result = CFDictionaryGetValue(stringTable, key); + if (result == NULL) { + if (value == NULL) { + result = CFRetain(key); + } else if (CFEqual(value, CFSTR(""))) { + result = CFRetain(key); + } else { + result = CFRetain(value); + } + } else { + CFRetain(result); + } + + return result; +} + +CF_EXPORT CFURLRef CFBundleCopyResourceURLInDirectory(CFURLRef bundleURL, CFStringRef resourceName, CFStringRef resourceType, CFStringRef subDirName) { + CFURLRef result = NULL; + char buff[CFMaxPathSize]; + CFURLRef newURL = NULL; + + if (!CFURLGetFileSystemRepresentation(bundleURL, true, buff, CFMaxPathSize)) return NULL; + + newURL = CFURLCreateFromFileSystemRepresentation(NULL, buff, strlen(buff), true); + if (NULL == newURL) { + newURL = CFRetain(bundleURL); + } + if (_CFBundleCouldBeBundle(newURL)) { + uint8_t version = 0; + CFArrayRef languages = _CFBundleCopyLanguageSearchListInDirectory(NULL, newURL, &version); + CFMutableArrayRef types = NULL; + CFArrayRef array; + + if (resourceType) { + types = CFArrayCreateMutable(NULL, 1, &kCFTypeArrayCallBacks); + CFArrayAppendValue(types, resourceType); + } + + array = _CFFindBundleResources(NULL, newURL, subDirName, languages, resourceName, types, 1, version); + + if (types) { + CFRelease(types); + } + + CFRelease(languages); + + if (array) { + if (CFArrayGetCount(array) > 0) { + result = 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; + char buff[CFMaxPathSize]; + CFURLRef newURL = NULL; + + if (!CFURLGetFileSystemRepresentation(bundleURL, true, buff, CFMaxPathSize)) return NULL; + + newURL = CFURLCreateFromFileSystemRepresentation(NULL, buff, strlen(buff), true); + if (NULL == newURL) { + newURL = CFRetain(bundleURL); + } + if (_CFBundleCouldBeBundle(newURL)) { + uint8_t version = 0; + CFArrayRef languages = _CFBundleCopyLanguageSearchListInDirectory(NULL, newURL, &version); + CFMutableArrayRef types = NULL; + + if (resourceType) { + types = CFArrayCreateMutable(NULL, 1, &kCFTypeArrayCallBacks); + CFArrayAppendValue(types, resourceType); + } + + // MF:!!! Better "limit" than 1,000,000? + array = _CFFindBundleResources(NULL, newURL, subDirName, languages, NULL, types, 1000000, version); + + if (types) { + CFRelease(types); + } + + 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" "no_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" "\0\0\0\0\0\0" + "tr_TR\0" "hr_HR\0" "nl_NL\0" "nl_BE\0" "en_CA\0" "en_CA\0" "pt_PT\0" "no_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" + "el_GR\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_YU\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\0\0\0\0" "es_ES\0" + "to_TO\0" "pl_PL\0" "ca_ES\0" "fr\0\0\0\0" "de_AT\0" "es\0\0\0\0" "gu_IN\0" "pa\0\0\0\0" + "ur_IN\0" "vi_VN\0" "fr_BE\0" "uz_UZ\0" "\0\0\0\0\0\0" "\0\0\0\0\0\0" "af_ZA\0" "eo\0\0\0\0" + "mr_IN\0" "bo\0\0\0\0" "ne_NP\0" "kl\0\0\0"; + +#define NUM_LOCALE_ABBREVIATIONS 108 +#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" +}; + +#define NUM_LANGUAGE_NAMES 151 +#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" "no\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"; + +#define NUM_LANGUAGE_ABBREVIATIONS 151 +#define LANGUAGE_ABBREVIATION_LENGTH 3 + +static const SInt32 __CFBundleScriptCodesArray[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 1, 4, 0, 0, 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 +}; + +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 +}; + +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_CN")) result = 33; // hack for mixed-up Chinese language codes + buff[LANGUAGE_ABBREVIATION_LENGTH - 1] = '\0'; + 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 != NULL && *languageAbbreviation != '\0') { + result = CFStringCreateWithCStringNoCopy(NULL, languageAbbreviation, kCFStringEncodingASCII, kCFAllocatorNull); + } + } + return result; +} + +static CFStringRef _CFBundleCopyLanguageNameForLanguageCode(SInt32 languageCode) { + CFStringRef result = NULL; + if (0 <= languageCode && languageCode < NUM_LANGUAGE_NAMES) { + const char *languageName = __CFBundleLanguageNamesArray[languageCode]; + if (languageName != NULL && *languageName != '\0') { + result = CFStringCreateWithCStringNoCopy(NULL, languageName, kCFStringEncodingASCII, kCFAllocatorNull); + } + } + return result; +} + +static 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(NULL, localizationName, CFRangeMake(0, LANGUAGE_ABBREVIATION_LENGTH - 1)); + } + } + return result; +} + +static CFStringRef _CFBundleCopyLanguageNameForLocalization(CFStringRef localizationName) { + CFStringRef result = NULL; + SInt32 languageCode = _CFBundleGetLanguageCodeForLocalization(localizationName); + if (languageCode >= 0) { + result = _CFBundleCopyLanguageNameForLanguageCode(languageCode); + } else { + result = CFStringCreateCopy(NULL, 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 != NULL && *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 != NULL && *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; + } + } + } + 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 (-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 != NULL && *localeAbbreviation != '\0') { + result = CFStringCreateWithCStringNoCopy(NULL, localeAbbreviation, kCFStringEncodingASCII, kCFAllocatorNull); + } + } + return result; +} + +Boolean CFBundleGetLocalizationInfoForLocalization(CFStringRef localizationName, SInt32 *languageCode, SInt32 *regionCode, SInt32 *scriptCode, CFStringEncoding *stringEncoding) { + SInt32 language = -1, region = -1, script = 0; + CFStringEncoding encoding = kCFStringEncodingMacRoman; + 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]; + } + if (languageCode) *languageCode = language; + if (regionCode) *regionCode = region; + if (scriptCode) *scriptCode = script; + if (stringEncoding) *stringEncoding = encoding; + return (language != -1 || region != -1); +} + +CFStringRef CFBundleCopyLocalizationForLocalizationInfo(SInt32 languageCode, SInt32 regionCode, SInt32 scriptCode, CFStringEncoding stringEncoding) { + CFStringRef localizationName = NULL; + if (!localizationName) { + localizationName = _CFBundleCopyLocaleAbbreviationForRegionCode(regionCode); + } + 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 = NULL; + // This is a temporary solution, until the argument domain is moved down into CFPreferences + __CFSpinLock(&CFBundleResourceGlobalDataLock); + if (!didit) { + if (__CFAppleLanguages) { + CFDataRef data; + CFIndex length = strlen(__CFAppleLanguages); + if (length > 0) { + data = CFDataCreateWithBytesNoCopy(NULL, __CFAppleLanguages, length, kCFAllocatorNull); + if (data) { +__CFSetNastyFile(CFSTR("")); + userLanguages = CFPropertyListCreateFromXMLData(NULL, data, kCFPropertyListImmutable, NULL); + CFRelease(data); + } + } + } + if (!userLanguages && preferencesArray) userLanguages = CFRetain(preferencesArray); + { // could perhaps read out of LANG environment variable + CFStringRef english = CFSTR("English"); + if (!userLanguages) userLanguages = CFArrayCreate(kCFAllocatorDefault, (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 = 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; + CFStringRef localizationName = NULL; + if (mainBundle) { + languages = _CFBundleGetLanguageSearchList(mainBundle); + if (languages) CFRetain(languages); + } + if (!languages) languages = _CFBundleCopyUserLanguages(false); + if (languages && (CFArrayGetCount(languages) > 0)) { + localizationName = CFArrayGetValueAtIndex(languages, 0); + 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); + UniChar curLangUniChars[255]; + CFIndex savedPathLen; + CFStringRef languageAbbreviation = _CFBundleCopyLanguageAbbreviationForLocalization(curLangStr), languageName = _CFBundleCopyLanguageNameForLocalization(curLangStr); + Boolean foundOne = false; + CFArrayRef predefinedLocalizations = NULL; + CFRange predefinedLocalizationsRange; + CFMutableStringRef cheapStr, tmpString; +#if USE_GETDIRENTRIES + CFArrayRef directoryContents; + CFRange directoryContentsRange; +#else + Boolean isDir = false; +#endif + + // 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 USE_GETDIRENTRIES + CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, pathLen, pathLen); + CFStringReplaceAll(cheapStr, tmpString); + directoryContents = _CFBundleCopyDirectoryContentsAtPath(cheapStr, true); + directoryContentsRange = CFRangeMake(0, CFArrayGetCount(directoryContents)); +#endif + + if (infoDict) { + predefinedLocalizations = CFDictionaryGetValue(infoDict, kCFBundleLocalizationsKey); + if (predefinedLocalizations != NULL && CFGetTypeID(predefinedLocalizations) != CFArrayGetTypeID()) { + predefinedLocalizations = NULL; + CFDictionaryRemoveValue((CFMutableDictionaryRef)infoDict, kCFBundleLocalizationsKey); + } + } + predefinedLocalizationsRange = CFRangeMake(0, predefinedLocalizations ? CFArrayGetCount(predefinedLocalizations) : 0); + + CFStringGetCharacters(curLangStr, CFRangeMake(0, curLangLen), curLangUniChars); + savedPathLen = pathLen; + _CFAppendPathComponent(pathUniChars, &pathLen, CFMaxPathSize, curLangUniChars, curLangLen); + _CFAppendPathExtension(pathUniChars, &pathLen, CFMaxPathSize, _LprojUniChars, _LprojLen); +#if USE_GETDIRENTRIES + CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars + savedPathLen + 1, pathLen - savedPathLen - 1, pathLen - savedPathLen - 1); + CFStringReplaceAll(cheapStr, tmpString); + if ((predefinedLocalizations && CFArrayContainsValue(predefinedLocalizations, predefinedLocalizationsRange, curLangStr)) || (version != 4 && CFArrayContainsValue(directoryContents, directoryContentsRange, cheapStr))) { +#else + CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, pathLen, pathLen); + CFStringReplaceAll(cheapStr, tmpString); + if ((predefinedLocalizations && CFArrayContainsValue(predefinedLocalizations, predefinedLocalizationsRange, curLangStr)) || (version != 4 && _CFIsResourceAtPath(cheapStr, &isDir) && isDir)) { +#endif + // We found one. + CFArrayAppendValue(lprojNames, curLangStr); + foundOne = true; + } + // Now, if the curLangStr was a region name, and we can map it to a language name, try that too. + if (languageAbbreviation && !CFEqual(curLangStr, languageAbbreviation)) { + curLangLen = CFStringGetLength(languageAbbreviation); + CFStringGetCharacters(languageAbbreviation, CFRangeMake(0, curLangLen), curLangUniChars); + pathLen = savedPathLen; + _CFAppendPathComponent(pathUniChars, &pathLen, CFMaxPathSize, curLangUniChars, curLangLen); + _CFAppendPathExtension(pathUniChars, &pathLen, CFMaxPathSize, _LprojUniChars, _LprojLen); +#if USE_GETDIRENTRIES + CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars + savedPathLen + 1, pathLen - savedPathLen - 1, pathLen - savedPathLen - 1); + CFStringReplaceAll(cheapStr, tmpString); + if ((predefinedLocalizations && CFArrayContainsValue(predefinedLocalizations, predefinedLocalizationsRange, languageAbbreviation)) || (version != 4 && CFArrayContainsValue(directoryContents, directoryContentsRange, cheapStr))) { +#else + CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, pathLen, pathLen); + CFStringReplaceAll(cheapStr, tmpString); + if ((predefinedLocalizations && CFArrayContainsValue(predefinedLocalizations, predefinedLocalizationsRange, languageAbbreviation)) || (version != 4 && _CFIsResourceAtPath(cheapStr, &isDir) && isDir)) { +#endif + // We found one. + CFArrayAppendValue(lprojNames, languageAbbreviation); + foundOne = true; + } + } + if (languageName && !CFEqual(curLangStr, languageName)) { + curLangLen = CFStringGetLength(languageName); + CFStringGetCharacters(languageName, CFRangeMake(0, curLangLen), curLangUniChars); + pathLen = savedPathLen; + _CFAppendPathComponent(pathUniChars, &pathLen, CFMaxPathSize, curLangUniChars, curLangLen); + _CFAppendPathExtension(pathUniChars, &pathLen, CFMaxPathSize, _LprojUniChars, _LprojLen); +#if USE_GETDIRENTRIES + CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars + savedPathLen + 1, pathLen - savedPathLen - 1, pathLen - savedPathLen - 1); + CFStringReplaceAll(cheapStr, tmpString); + if ((predefinedLocalizations && CFArrayContainsValue(predefinedLocalizations, predefinedLocalizationsRange, languageName)) || (version != 4 && CFArrayContainsValue(directoryContents, directoryContentsRange, cheapStr))) { +#else + CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, pathLen, pathLen); + CFStringReplaceAll(cheapStr, tmpString); + if ((predefinedLocalizations && CFArrayContainsValue(predefinedLocalizations, predefinedLocalizationsRange, languageName)) || (version != 4 && _CFIsResourceAtPath(cheapStr, &isDir) && isDir)) { +#endif + // We found one. + CFArrayAppendValue(lprojNames, languageName); + foundOne = true; + } + } + + CFRelease(cheapStr); + CFRelease(tmpString); + if (languageAbbreviation) CFRelease(languageAbbreviation); + if (languageName) CFRelease(languageName); +#if USE_GETDIRENTRIES + CFRelease(directoryContents); +#endif + + 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); + 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 = 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 = CFArrayGetValueAtIndex(userLanguages, idx); + foundOne = _CFBundleTryOnePreferredLprojNameInDirectory(alloc, pathUniChars, pathLen, version, infoDict, curLangStr, lprojNames); + } + // use development region and U.S. English as backstops + if (!foundOne && devLang != NULL) { + foundOne = _CFBundleTryOnePreferredLprojNameInDirectory(alloc, pathUniChars, pathLen, version, infoDict, devLang, lprojNames); + } + if (!foundOne) { + foundOne = _CFBundleTryOnePreferredLprojNameInDirectory(alloc, pathUniChars, pathLen, version, infoDict, CFSTR("en_US"), lprojNames); + } + if (userLanguages != NULL) { + CFRelease(userLanguages); + } + } +} + +static Boolean _CFBundleTryOnePreferredLprojNameInArray(CFArrayRef array, CFStringRef curLangStr, CFMutableArrayRef lprojNames) { + Boolean foundOne = false; + CFRange range = CFRangeMake(0, CFArrayGetCount(array)); + CFStringRef languageAbbreviation = _CFBundleCopyLanguageAbbreviationForLocalization(curLangStr), languageName = _CFBundleCopyLanguageNameForLocalization(curLangStr); + + if (CFArrayContainsValue(array, range, curLangStr)) { + // We found one. + CFArrayAppendValue(lprojNames, curLangStr); + foundOne = true; + } + if (languageAbbreviation && !CFEqual(curLangStr, languageAbbreviation)) { + if (CFArrayContainsValue(array, range, languageAbbreviation)) { + // We found one. + CFArrayAppendValue(lprojNames, languageAbbreviation); + foundOne = true; + } + } + if (languageName && !CFEqual(curLangStr, languageName)) { + if (CFArrayContainsValue(array, range, languageName)) { + // We found one. + CFArrayAppendValue(lprojNames, languageName); + foundOne = true; + } + } + + if (languageAbbreviation) CFRelease(languageAbbreviation); + if (languageName) CFRelease(languageName); + + return foundOne; +} + +static CFArrayRef _CFBundleCopyLocalizationsForPreferences(CFArrayRef locArray, CFArrayRef prefArray, Boolean considerMain) { + CFMutableArrayRef lprojNames = CFArrayCreateMutable(NULL, 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, 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, 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, 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 != NULL) { + devLang = CFDictionaryGetValue(infoDict, kCFBundleDevelopmentRegionKey); + } + if (devLang != NULL && (CFGetTypeID(devLang) != CFStringGetTypeID() || CFStringGetLength(devLang) == 0)) devLang = NULL; + + _CFBundleAddPreferredLprojNamesInDirectory(alloc, url, localVersion, infoDict, langs, devLang); + + if (devLang != NULL && CFArrayGetFirstIndexOfValue(langs, CFRangeMake(0, CFArrayGetCount(langs)), devLang) < 0) { + CFArrayAppendValue(langs, devLang); + } + if (CFArrayGetCount(langs) == 0) { + // Total backstop behavior to avoid having an empty array. + CFArrayAppendValue(langs, CFSTR("en")); + } + if (infoDict != NULL) { + CFRelease(infoDict); + } + if (version) { + *version = localVersion; + } + return langs; +} + +CF_EXPORT Boolean _CFBundleURLLooksLikeBundle(CFURLRef url) { + Boolean result = false; + CFBundleRef bundle = _CFBundleCreateIfLooksLikeBundle(NULL, 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, result = false; + + dirURL = CFURLCreateWithString(NULL, subDirName, url); + if (dirURL != NULL) { + if (_CFIsResourceAtURL(dirURL, &isDir) && isDir) { + result = true; + } + CFRelease(dirURL); + } + return result; +} + +__private_extern__ Boolean _CFBundleURLLooksLikeBundleVersion(CFURLRef url, uint8_t *version) { + Boolean result = false; + uint8_t localVersion = 0; + + // 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) + if (CFStringHasSuffix(CFURLGetString(url), CFSTR(".framework/"))) { + if (_CFBundleURLHasSubDir(url, _CFBundleResourcesURLFromBase0)) { + result = true; + localVersion = 0; + } else if (_CFBundleURLHasSubDir(url, _CFBundleSupportFilesURLFromBase2)) { + result = true; + localVersion = 2; + } else if (_CFBundleURLHasSubDir(url, _CFBundleSupportFilesURLFromBase1)) { + result = true; + localVersion = 1; + } + } else { + if (_CFBundleURLHasSubDir(url, _CFBundleSupportFilesURLFromBase2)) { + result = true; + localVersion = 2; + } else if (_CFBundleURLHasSubDir(url, _CFBundleResourcesURLFromBase0)) { + result = true; + localVersion = 0; + } else if (_CFBundleURLHasSubDir(url, _CFBundleSupportFilesURLFromBase1)) { + result = true; + localVersion = 1; + } + } + if (result && version) *version = localVersion; + return result; +} + +__private_extern__ CFDictionaryRef _CFBundleCopyInfoDictionaryInDirectory(CFAllocatorRef alloc, CFURLRef url, uint8_t *version) { + CFDictionaryRef dict = NULL; + char buff[CFMaxPathSize]; + uint8_t localVersion = 0; + + if (CFURLGetFileSystemRepresentation(url, true, buff, CFMaxPathSize)) { + CFURLRef newURL = CFURLCreateFromFileSystemRepresentation(alloc, buff, strlen(buff), true); + if (NULL == newURL) newURL = CFRetain(url); + + if (!_CFBundleURLLooksLikeBundleVersion(newURL, &localVersion)) { + // version 3 is for flattened pseudo-bundles with no Contents, Support Files, or Resources directories + 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 != NULL) { + CFURLRef infoURL = NULL; + CFDataRef infoData = NULL; + UniChar buff[CFMaxPathSize]; + CFIndex len; + CFMutableStringRef cheapStr; + CFStringRef infoURLFromBaseNoExtension = _CFBundleInfoURLFromBaseNoExtension0, infoURLFromBase = _CFBundleInfoURLFromBase0; + Boolean tryPlatformSpecific = true, tryGlobal = true; +#if USE_GETDIRENTRIES + CFArrayRef directoryContents = NULL; + CFRange directoryContentsRange = CFRangeMake(0, 0); +#endif + + _CFEnsureStaticBuffersInited(); + + if (0 == version) { +#if USE_GETDIRENTRIES + // we want to read the Resources directory anyway, so we might as well do it now + CFURLRef resourcesURL = _CFBundleCopyResourcesDirectoryURLInDirectory(alloc, url, version); + CFURLRef absoluteURL = CFURLCopyAbsoluteURL(resourcesURL); + CFStringRef resourcesPath = CFURLCopyFileSystemPath(absoluteURL, PLATFORM_PATH_STYLE); + directoryContents = _CFBundleCopyDirectoryContentsAtPath(resourcesPath, false); + directoryContentsRange = CFRangeMake(0, CFArrayGetCount(directoryContents)); + CFRelease(resourcesPath); + CFRelease(absoluteURL); + CFRelease(resourcesURL); +#endif + infoURLFromBaseNoExtension = _CFBundleInfoURLFromBaseNoExtension0; + infoURLFromBase = _CFBundleInfoURLFromBase0; + } else if (1 == version) { + infoURLFromBaseNoExtension = _CFBundleInfoURLFromBaseNoExtension1; + infoURLFromBase = _CFBundleInfoURLFromBase1; + } else if (2 == version) { + infoURLFromBaseNoExtension = _CFBundleInfoURLFromBaseNoExtension2; + infoURLFromBase = _CFBundleInfoURLFromBase2; + } else if (3 == version) { + 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))) { + infoURLFromBaseNoExtension = _CFBundleInfoURLFromBaseNoExtension3; + infoURLFromBase = _CFBundleInfoURLFromBase3; + } + CFRelease(posixPath); + } + } + + 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 USE_GETDIRENTRIES + if (directoryContents) { + CFIndex resourcesLen = CFStringGetLength(_CFBundleResourcesURLFromBase0); + CFStringDelete(cheapStr, CFRangeMake(0, CFStringGetLength(cheapStr))); + CFStringAppendCharacters(cheapStr, buff + resourcesLen, len - resourcesLen); + tryPlatformSpecific = CFArrayContainsValue(directoryContents, directoryContentsRange, cheapStr); + } +#endif + if (tryPlatformSpecific) CFURLCreateDataAndPropertiesFromResource(alloc, infoURL, &infoData, NULL, NULL, NULL); + //fprintf(stderr, "looking for ");CFShow(infoURL);if (infoData) fprintf(stderr, "found it\n"); + CFRelease(cheapStr); + if (!infoData) { + // Check for global Info.plist + CFRelease(infoURL); + infoURL = CFURLCreateWithString(alloc, infoURLFromBase, url); +#if USE_GETDIRENTRIES + if (directoryContents) tryGlobal = CFArrayContainsValue(directoryContents, directoryContentsRange, _CFBundleInfoFileName); +#endif + if (tryGlobal) CFURLCreateDataAndPropertiesFromResource(alloc, infoURL, &infoData, NULL, NULL, NULL); + //fprintf(stderr, "looking for ");CFShow(infoURL);if (infoData) fprintf(stderr, "found it\n"); + } + + if (infoData) { + result = CFPropertyListCreateFromXMLData(alloc, infoData, kCFPropertyListMutableContainers, NULL); + if (result) { + if (CFDictionaryGetTypeID() == CFGetTypeID(result)) { + CFDictionarySetValue((CFMutableDictionaryRef)result, _kCFBundleInfoPlistURLKey, infoURL); + } else { + CFRelease(result); + result = NULL; + } + } + CFRelease(infoData); + } + if (!result) { + result = CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + } + + CFRelease(infoURL); +#if USE_GETDIRENTRIES + if (directoryContents) CFRelease(directoryContents); +#endif + } + 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 == NULL) { + tempURL = CFURLCreateWithString(alloc, _CFBundlePkgInfoURLFromBase1, url); + CFURLCreateDataAndPropertiesFromResource(alloc, tempURL, &pkgInfoData, NULL, NULL, NULL); + CFRelease(tempURL); + } + if (pkgInfoData == NULL) { + // 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 != NULL) && (CFDataGetLength(pkgInfoData) >= (int)(sizeof(UInt32) * 2))) { + UInt32 *pkgInfo = (UInt32 *)CFDataGetBytePtr(pkgInfoData); + + if (packageType != NULL) { + *packageType = CFSwapInt32BigToHost(pkgInfo[0]); + } + if (packageCreator != NULL) { + *packageCreator = CFSwapInt32BigToHost(pkgInfo[1]); + } + retVal = hasType = hasCreator = true; + } + if (pkgInfoData != NULL) CFRelease(pkgInfoData); + if (!retVal) { + if (!infoDict) { + infoDict = _CFBundleCopyInfoDictionaryInDirectory(alloc, url, NULL); + releaseInfoDict = true; + } + if (infoDict) { + CFStringRef typeString = CFDictionaryGetValue(infoDict, _kCFBundlePackageTypeKey), creatorString = 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 != NULL) { + *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 != NULL) { + *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 != NULL && !hasCreator) { + *packageCreator = 0x3f3f3f3f; // '????' + } + if (packageType != NULL && !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); + 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 = CFSwapInt32BigToHost(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 = CFSwapInt32BigToHost(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 = CFSwapInt32BigToHost(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 = CFSwapInt32BigToHost(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 = CFSwapInt32BigToHost(0x464d574b); // 'FMWK' + } else { + // Default to BNDL for generic bundle + *packageType = CFSwapInt32BigToHost(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 != NULL) { + *packageType = CFSwapInt32BigToHost(0x424e444C); // 'BNDL' + } + if (packageCreator != NULL) { + *packageCreator = 0x3f3f3f3f; // '????' + } + } + if (bundleURL) CFRelease(bundleURL); +} + +CF_EXPORT Boolean CFBundleGetPackageInfoInDirectory(CFURLRef url, UInt32 *packageType, UInt32 *packageCreator) {return _CFBundleGetPackageInfoInDirectory(NULL, url, packageType, packageCreator);} + +__private_extern__ CFStringRef _CFBundleGetPlatformExecutablesSubdirectoryName(void) { +#if defined(__MACOS8__) + return CFSTR("MacOSClassic"); +#elif defined(__WIN32__) + return CFSTR("Windows"); +#elif defined(__MACH__) + return CFSTR("MacOS"); +#elif defined(__hpux__) + return CFSTR("HPUX"); +#elif defined(__svr4__) + return CFSTR("Solaris"); +#elif defined(__LINUX__) + return CFSTR("Linux"); +#elif defined(__FREEBSD__) + return CFSTR("FreeBSD"); +#else +#warning CFBundle: Unknown architecture + return CFSTR("Other"); +#endif +} + +__private_extern__ CFStringRef _CFBundleGetAlternatePlatformExecutablesSubdirectoryName(void) { +#if defined(__MACOS8__) + return CFSTR("Mac OS 8"); +#elif defined(__WIN32__) + return CFSTR("WinNT"); +#elif defined(__MACH__) + return CFSTR("Mac OS X"); +#elif defined(__hpux__) + return CFSTR("HP-UX"); +#elif defined(__svr4__) + return CFSTR("Solaris"); +#elif defined(__LINUX__) + return CFSTR("Linux"); +#elif defined(__FREEBSD__) + return CFSTR("FreeBSD"); +#else +#warning CFBundle: Unknown architecture + return CFSTR("Other"); +#endif +} + +__private_extern__ CFStringRef _CFBundleGetOtherPlatformExecutablesSubdirectoryName(void) { +#if defined(__MACOS8__) + return CFSTR("MacOS"); +#elif defined(__WIN32__) + return CFSTR("Other"); +#elif defined(__MACH__) + return CFSTR("MacOSClassic"); +#elif defined(__hpux__) + return CFSTR("Other"); +#elif defined(__svr4__) + return CFSTR("Other"); +#elif defined(__LINUX__) + return CFSTR("Other"); +#elif defined(__FREEBSD__) + return CFSTR("Other"); +#else +#warning CFBundle: Unknown architecture + return CFSTR("Other"); +#endif +} + +__private_extern__ CFStringRef _CFBundleGetOtherAlternatePlatformExecutablesSubdirectoryName(void) { +#if defined(__MACOS8__) + return CFSTR("Mac OS X"); +#elif defined(__WIN32__) + return CFSTR("Other"); +#elif defined(__MACH__) + return CFSTR("Mac OS 8"); +#elif defined(__hpux__) + return CFSTR("Other"); +#elif defined(__svr4__) + return CFSTR("Other"); +#elif defined(__LINUX__) + return CFSTR("Other"); +#elif defined(__FREEBSD__) + return CFSTR("Other"); +#else +#warning CFBundle: Unknown architecture + return CFSTR("Other"); +#endif +} + +__private_extern__ CFArrayRef _CFBundleCopyBundleRegionsArray(CFBundleRef bundle) {return CFBundleCopyBundleLocalizations(bundle);} + +CF_EXPORT CFArrayRef CFBundleCopyBundleLocalizations(CFBundleRef bundle) { + CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(bundle); + uint8_t version = _CFBundleLayoutVersion(bundle); + CFArrayRef urls = ((version != 4) ? _CFContentsOfDirectory(CFGetAllocator(bundle), NULL, NULL, resourcesURL, CFSTR("lproj")) : NULL); + CFArrayRef predefinedLocalizations = NULL; + CFDictionaryRef infoDict = CFBundleGetInfoDictionary(bundle); + CFMutableArrayRef result = NULL; + + if (infoDict) { + predefinedLocalizations = CFDictionaryGetValue(infoDict, kCFBundleLocalizationsKey); + if (predefinedLocalizations != NULL && CFGetTypeID(predefinedLocalizations) != CFArrayGetTypeID()) { + predefinedLocalizations = NULL; + CFDictionaryRemoveValue((CFMutableDictionaryRef)infoDict, kCFBundleLocalizationsKey); + } + if (predefinedLocalizations != NULL) { + CFIndex i, c = CFArrayGetCount(predefinedLocalizations); + if (c > 0 && !result) { + result = CFArrayCreateMutable(CFGetAllocator(bundle), 0, &kCFTypeArrayCallBacks); + } + for (i=0; i 0 && !result) { + result = CFArrayCreateMutable(CFGetAllocator(bundle), 0, &kCFTypeArrayCallBacks); + } + for (i = 0; i < c; i++) { + curURL = CFArrayGetValueAtIndex(urls, i); + curAbsoluteURL = CFURLCopyAbsoluteURL(curURL); + curStr = CFURLCopyFileSystemPath(curAbsoluteURL, PLATFORM_PATH_STYLE); + CFRelease(curAbsoluteURL); + strLen = CFStringGetLength(curStr); + 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); + } + 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; + if (_CFIsResourceAtURL(url, &isDir)) { + if (isDir) { + result = _CFBundleCopyInfoDictionaryInDirectory(NULL, url, NULL); + } else { + result = _CFBundleCopyInfoDictionaryInExecutable(url); + } + } + return result; +} + +CFArrayRef CFBundleCopyLocalizationsForURL(CFURLRef url) { + CFArrayRef result = NULL; + CFBundleRef bundle = CFBundleCreate(NULL, url); + CFStringRef devLang = NULL; + if (bundle) { + result = CFBundleCopyBundleLocalizations(bundle); + CFRelease(bundle); + } else { + CFDictionaryRef infoDict = _CFBundleCopyInfoDictionaryInExecutable(url); + if (infoDict) { + CFArrayRef predefinedLocalizations = CFDictionaryGetValue(infoDict, kCFBundleLocalizationsKey); + if (predefinedLocalizations != NULL && CFGetTypeID(predefinedLocalizations) == CFArrayGetTypeID()) { + result = CFRetain(predefinedLocalizations); + } + if (!result) { + devLang = CFDictionaryGetValue(infoDict, kCFBundleDevelopmentRegionKey); + if (devLang != NULL && (CFGetTypeID(devLang) == CFStringGetTypeID() && CFStringGetLength(devLang) > 0)) { + result = CFArrayCreate(NULL, (const void **)&devLang, 1, &kCFTypeArrayCallBacks); + } + } + CFRelease(infoDict); + } + } + return result; +} diff --git a/PlugIn.subproj/CFPlugIn.c b/PlugIn.subproj/CFPlugIn.c new file mode 100644 index 0000000..4cbab51 --- /dev/null +++ b/PlugIn.subproj/CFPlugIn.c @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFPlugIn.c + Copyright 1999-2002, 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(NULL, 0, &kCFTypeArrayCallBacks); + + for (i=0; i +#include +#include +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +/* ================ 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 +UInt32 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 +UInt32 CFPlugInInstanceGetTypeID(void); +CF_EXPORT +CFPlugInInstanceRef CFPlugInInstanceCreateWithInstanceDataSize(CFAllocatorRef allocator, CFIndex instanceDataSize, CFPlugInInstanceDeallocateInstanceDataFunction deallocateInstanceFunction, CFStringRef factoryName, CFPlugInInstanceGetInterfaceFunction getInterfaceFunction); + +#if defined(__cplusplus) +} +#endif + +#if !COREFOUNDATION_CFPLUGINCOM_SEPARATE +#include +#endif + +#endif /* ! __COREFOUNDATION_CFPLUGIN__ */ + diff --git a/PlugIn.subproj/CFPlugInCOM.h b/PlugIn.subproj/CFPlugInCOM.h new file mode 100644 index 0000000..671de8b --- /dev/null +++ b/PlugIn.subproj/CFPlugInCOM.h @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFPlugInCOM.h + Copyright (c) 1999-2003, Apple, Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFPLUGINCOM__) +#define __COREFOUNDATION_CFPLUGINCOM__ 1 + +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +/* ================= 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(NULL, 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; + +/* End of extern "C" stuff */ +#if defined(__cplusplus) +} +#endif + + +/* 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 +{ + public: + virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, LPVOID *ppv) = 0; + virtual ULONG STDMETHODCALLTYPE AddRef(void) = 0; + virtual ULONG STDMETHODCALLTYPE Release(void) = 0; +}; + +#endif + +#endif /* ! __COREFOUNDATION_CFPLUGINCOM__ */ + diff --git a/PlugIn.subproj/CFPlugIn_Factory.c b/PlugIn.subproj/CFPlugIn_Factory.c new file mode 100644 index 0000000..530556d --- /dev/null +++ b/PlugIn.subproj/CFPlugIn_Factory.c @@ -0,0 +1,289 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFPlugIn_Factory.c + Copyright 1999-2002, Apple, Inc. All rights reserved. + Responsibility: Doug Davidson +*/ + +#include "CFBundle_Internal.h" +#include "CFInternal.h" + +static CFSpinLock_t CFPlugInGlobalDataLock = 0; +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(NULL, 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, 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) ? CFRetain(__CFGetDefaultAllocator()) : CFRetain(allocator)); + factory = CFAllocatorAllocate(allocator, size, 0); + if (NULL == factory) { + CFRelease(allocator); + return NULL; + } + + factory->_allocator = allocator; + + factory->_uuid = 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 ? 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 = 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 defined(__MACH__) && defined(__ppc__) + 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 *)((unsigned long)(factory->_func) | 0x1); + } +#endif + } + if (factory->_func) { +#if 1 + // UPPGOOP + FAULT_CALLBACK((void **)&(factory->_func)); + result = (void *)INVOKE_CALLBACK2(factory->_func, allocator, typeID); +#else + result = factory->_func(allocator, typeID); +#endif + } + } 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(NULL, 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(NULL, 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 = 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/PlugIn.subproj/CFPlugIn_Factory.h b/PlugIn.subproj/CFPlugIn_Factory.h new file mode 100644 index 0000000..84c0d3d --- /dev/null +++ b/PlugIn.subproj/CFPlugIn_Factory.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFPlugIn_Factory.h + Copyright (c) 1999-2003, Apple, Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFPLUGIN_FACTORY__) +#define __COREFOUNDATION_CFPLUGIN_FACTORY__ 1 + +#include "CFBundle_Internal.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +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); + +#if defined(__cplusplus) +} +#endif + +#endif /* ! __COREFOUNDATION_CFPLUGIN_FACTORY__ */ + diff --git a/PlugIn.subproj/CFPlugIn_Instance.c b/PlugIn.subproj/CFPlugIn_Instance.c new file mode 100644 index 0000000..89790f1 --- /dev/null +++ b/PlugIn.subproj/CFPlugIn_Instance.c @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFPlugIn_Instance.c + Copyright 1999-2002, 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; + + uint8_t _instanceData[0]; +}; + +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/PlugIn.subproj/CFPlugIn_PlugIn.c b/PlugIn.subproj/CFPlugIn_PlugIn.c new file mode 100644 index 0000000..6122003 --- /dev/null +++ b/PlugIn.subproj/CFPlugIn_PlugIn.c @@ -0,0 +1,240 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFPlugIn_PlugIn.c + Copyright 1999-2002, 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(NULL, factoryIDStr) : NULL; + if (NULL == factoryID) factoryID = 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(NULL, typeIDStr) : NULL; + CFUUIDRef curFactoryID; + if (NULL == typeID) typeID = CFRetain(typeIDStr); + if (0 == c && (CFGetTypeID(factoryIDStrArray) != CFArrayGetTypeID())) { + curFactoryIDStr = (CFStringRef)val; + curFactoryID = (CFGetTypeID(curFactoryIDStr) == CFStringGetTypeID()) ? CFUUIDCreateFromString(CFGetAllocator(bundle), curFactoryIDStr) : NULL; + if (NULL == curFactoryID) curFactoryID = CFRetain(curFactoryIDStr); + CFPlugInRegisterPlugInType(curFactoryID, typeID); + if (NULL != curFactoryID) CFRelease(curFactoryID); + } else for (i=0; i_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 = CFDictionaryGetValue(infoDict, kCFPlugInTypesKey); + if (typeDict != NULL && CFGetTypeID(typeDict) != CFDictionaryGetTypeID()) typeDict = NULL; + if (typeDict != NULL) { + CFDictionaryApplyFunction(typeDict, _registerType, bundle); + } + + /* Now do dynamic registration if necessary */ + if (doDynamicReg) { + tempStr = CFDictionaryGetValue(infoDict, kCFPlugInDynamicRegisterFunctionKey); + if (tempStr == NULL || CFGetTypeID(tempStr) != CFStringGetTypeID() || CFStringGetLength(tempStr) <= 0) { + tempStr = CFSTR("CFPlugInDynamicRegister"); + } + __CFBundleGetPlugInData(bundle)->_loadOnDemand = false; + + if (CFBundleLoadExecutable(bundle)) { + CFPlugInDynamicRegisterFunction func = NULL; + + __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); + } + } + } +} + +__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; + } +} + +UInt32 CFPlugInGetTypeID(void) { + return CFBundleGetTypeID(); +} + +CFPlugInRef CFPlugInCreate(CFAllocatorRef allocator, CFURLRef plugInURL) { + return (CFPlugInRef)CFBundleCreate(allocator, plugInURL); +} + +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/PropertyList.dtd b/PropertyList.dtd new file mode 100644 index 0000000..4e42bb0 --- /dev/null +++ b/PropertyList.dtd @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/RunLoop.subproj/CFMachPort.c b/RunLoop.subproj/CFMachPort.c new file mode 100644 index 0000000..1353b1e --- /dev/null +++ b/RunLoop.subproj/CFMachPort.c @@ -0,0 +1,666 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @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. + + Finally, one tricky bit mostly specific to CFMachPort + deserves mention: use of __CFMachPortCurrentPID. Mach + ports are not inherited by a child process created with + fork(), unlike most other things. Thus, on fork(), in + the child, all cached CFMachPorts should be invalidated. + However, because the ports were never in the new task's + port space, the Mach part of the kernel doesn't send + dead-name notifications (which is used to autoinvalidate + CFMachPorts) to the new task, which is reasonable. There + is also no other notification that a fork() has occurred, + though, so how is CFMachPort to deal with this? An + atfork() function, similar to atexit(), would be nice, + but is not available. So CFMachPort does the best it + can, which is to compare the current process's PID with + the last PID recorded and if different, invalidate all + CFMachPorts, whenever various CFMachPort functions are + called. To avoid going insane, I've assumed that clients + aren't going to fork() as a result of callouts from this + code, which in a couple places might actually cause trouble. + It also isn't completely thread-safe. + + 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 defined(__MACH__) + +#include +#include +#include +#include +#include +#include +#include "CFInternal.h" + +static CFSpinLock_t __CFAllMachPortsLock = 0; +static CFMutableDictionaryRef __CFAllMachPorts = NULL; +static mach_port_t __CFNotifyRawMachPort = MACH_PORT_NULL; +static CFMachPortRef __CFNotifyMachPort = NULL; +static int __CFMachPortCurrentPID = 0; + +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)->_info, 0, 0); +} + +CF_INLINE void __CFMachPortSetValid(CFMachPortRef mp) { + __CFBitfieldSetValue(((CFRuntimeBase *)mp)->_info, 0, 0, 1); +} + +CF_INLINE void __CFMachPortUnsetValid(CFMachPortRef mp) { + __CFBitfieldSetValue(((CFRuntimeBase *)mp)->_info, 0, 0, 0); +} + +CF_INLINE Boolean __CFMachPortHasReceive(CFMachPortRef mp) { + return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)mp)->_info, 1, 1); +} + +CF_INLINE void __CFMachPortSetHasReceive(CFMachPortRef mp) { + __CFBitfieldSetValue(((CFRuntimeBase *)mp)->_info, 1, 1, 1); +} + +CF_INLINE Boolean __CFMachPortHasSend(CFMachPortRef mp) { + return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)mp)->_info, 2, 2); +} + +CF_INLINE void __CFMachPortSetHasSend(CFMachPortRef mp) { + __CFBitfieldSetValue(((CFRuntimeBase *)mp)->_info, 2, 2, 1); +} + +CF_INLINE Boolean __CFMachPortIsDeallocing(CFMachPortRef mp) { + return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)mp)->_info, 3, 3); +} + +CF_INLINE void __CFMachPortSetIsDeallocing(CFMachPortRef mp) { + __CFBitfieldSetValue(((CFRuntimeBase *)mp)->_info, 3, 3, 1); +} + +CF_INLINE void __CFMachPortLock(CFMachPortRef mp) { + __CFSpinLock(&(mp->_lock)); +} + +CF_INLINE void __CFMachPortUnlock(CFMachPortRef mp) { + __CFSpinUnlock(&(mp->_lock)); +} + +// The only CFMachPort this releases is the notify port, which +// no one else should have gotten their grubby hands on. Set the +// new PID first, to avoid reentrant invocations of this function. +static void __CFMachPortDidFork(void) { + CFMachPortRef oldNotify; + __CFMachPortCurrentPID = getpid(); + __CFSpinLock(&__CFAllMachPortsLock); + oldNotify = __CFNotifyMachPort; + __CFNotifyMachPort = NULL; + __CFNotifyRawMachPort = MACH_PORT_NULL; + if (NULL != __CFAllMachPorts) { + CFIndex idx, cnt; + CFMachPortRef *mps, buffer[128]; + cnt = CFDictionaryGetCount(__CFAllMachPorts); + mps = (cnt <= 128) ? buffer : CFAllocatorAllocate(kCFAllocatorDefault, cnt * sizeof(CFMachPortRef), 0); + CFDictionaryGetKeysAndValues(__CFAllMachPorts, NULL, (const void **)mps); + CFRelease(__CFAllMachPorts); + __CFAllMachPorts = NULL; + __CFSpinUnlock(&__CFAllMachPortsLock); + for (idx = 0; idx < cnt; idx++) { + // invalidation must be outside the lock + CFMachPortInvalidate(mps[idx]); + } + if (mps != buffer) CFAllocatorDeallocate(kCFAllocatorDefault, mps); + } else { + __CFSpinUnlock(&__CFAllMachPortsLock); + } + if (NULL != oldNotify) { + // The global is NULL'd before this, just in case. + // __CFNotifyMachPort was in the cache and was + // invalidated above, but that's harmless. + CFRelease(oldNotify); + } +} + +CF_INLINE void __CFMachPortCheckForFork(void) { + if (getpid() != __CFMachPortCurrentPID) { + __CFMachPortDidFork(); + } +} + +void _CFMachPortInstallNotifyPort(CFRunLoopRef rl, CFStringRef mode) { + CFRunLoopSourceRef source; + if (NULL == __CFNotifyMachPort) return; + source = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, __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; + if (header && header->msgh_id == MACH_NOTIFY_DEAD_NAME) { + mach_port_t dead_port = ((mach_dead_name_notification_t *)msg)->not_port; + CFMachPortRef existing; + /* If the CFMachPort has already been invalidated, it won't be found here. */ + __CFSpinLock(&__CFAllMachPortsLock); + if (NULL != __CFAllMachPorts && CFDictionaryGetValueIfPresent(__CFAllMachPorts, (void *)dead_port, (const void **)&existing)) { + CFDictionaryRemoveValue(__CFAllMachPorts, (void *)dead_port); + CFRetain(existing); + __CFSpinUnlock(&__CFAllMachPortsLock); + CFMachPortInvalidate(existing); + CFRelease(existing); + } else { + __CFSpinUnlock(&__CFAllMachPortsLock); + } + /* Delete port reference we got for this notification */ + mach_port_deallocate(mach_task_self(), dead_port); + } else if (header && header->msgh_id == MACH_NOTIFY_PORT_DELETED) { + mach_port_t dead_port = ((mach_port_deleted_notification_t *)msg)->not_port; + CFMachPortRef existing; + /* If the CFMachPort has already been invalidated, it won't be found here. */ + __CFSpinLock(&__CFAllMachPortsLock); + if (NULL != __CFAllMachPorts && CFDictionaryGetValueIfPresent(__CFAllMachPorts, (void *)dead_port, (const void **)&existing)) { + CFDictionaryRemoveValue(__CFAllMachPorts, (void *)dead_port); + CFRetain(existing); + __CFSpinUnlock(&__CFAllMachPortsLock); + CFMachPortInvalidate(existing); + CFRelease(existing); + } else { + __CFSpinUnlock(&__CFAllMachPortsLock); + } + /* Delete port reference we got for this notification */ +// Don't do this, since this always fails, and could cause trouble +// mach_port_deallocate(mach_task_self(), dead_port); + } +} + +static Boolean __CFMachPortEqual(CFTypeRef cf1, CFTypeRef cf2) { + CFMachPortRef mp1 = (CFMachPortRef)cf1; + CFMachPortRef mp2 = (CFMachPortRef)cf2; + __CFMachPortCheckForFork(); + return (mp1->_port == mp2->_port); +} + +static CFHashCode __CFMachPortHash(CFTypeRef cf) { + CFMachPortRef mp = (CFMachPortRef)cf; + __CFMachPortCheckForFork(); + 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(""), mp->_context.info); + } + result = CFStringCreateWithFormat(CFGetAllocator(mp), NULL, CFSTR("{locked = %s, valid = %s, port = %p, source = %p, callout = %p, context = %@}"), cf, CFGetAllocator(mp), locked, (__CFMachPortIsValid(mp) ? "Yes" : "No"), mp->_port, mp->_source, mp->_callout, (NULL != contextDesc ? contextDesc : CFSTR(""))); + if (NULL != contextDesc) { + CFRelease(contextDesc); + } + return result; +} + +static void __CFMachPortDeallocate(CFTypeRef cf) { + 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); + } + __CFMachPortCheckForFork(); +} + +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); + __CFMachPortCurrentPID = getpid(); +} + +CFTypeID CFMachPortGetTypeID(void) { + __CFMachPortCheckForFork(); + return __kCFMachPortTypeID; +} + +CFMachPortRef CFMachPortCreate(CFAllocatorRef allocator, CFMachPortCallBack callout, CFMachPortContext *context, Boolean *shouldFreeInfo) { + CFMachPortRef result; + mach_port_t port; + kern_return_t ret; + __CFMachPortCheckForFork(); + 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) { + CFMachPortRef memory; + SInt32 size; + Boolean didCreateNotifyPort = false; + CFRunLoopSourceRef source; + __CFMachPortCheckForFork(); + if (shouldFreeInfo) *shouldFreeInfo = true; + __CFSpinLock(&__CFAllMachPortsLock); + if (NULL != __CFAllMachPorts && CFDictionaryGetValueIfPresent(__CFAllMachPorts, (void *)port, (const void **)&memory)) { + __CFSpinUnlock(&__CFAllMachPortsLock); + return (CFMachPortRef)CFRetain(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 = 0; + 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) { + 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 *)port, memory); + __CFSpinUnlock(&__CFAllMachPortsLock); + if (didCreateNotifyPort) { + // __CFNotifyMachPort ends up in cache + CFMachPortRef mp = CFMachPortCreateWithPort(kCFAllocatorDefault, __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(kCFAllocatorDefault, __CFNotifyMachPort, -1000); + CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopCommonModes); + CFRelease(source); + } + if (shouldFreeInfo) *shouldFreeInfo = false; + return memory; +} + +mach_port_t CFMachPortGetPort(CFMachPortRef mp) { + CF_OBJC_FUNCDISPATCH0(__kCFMachPortTypeID, mach_port_t, mp, "machPort"); + __CFGenericValidateType(mp, __kCFMachPortTypeID); + __CFMachPortCheckForFork(); + 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__); + __CFMachPortCheckForFork(); + memmove(context, &mp->_context, sizeof(CFMachPortContext)); +} + +void CFMachPortInvalidate(CFMachPortRef mp) { + CF_OBJC_FUNCDISPATCH0(__kCFMachPortTypeID, void, mp, "invalidate"); + __CFGenericValidateType(mp, __kCFMachPortTypeID); + __CFMachPortCheckForFork(); + if (!__CFMachPortIsDeallocing(mp)) { + CFRetain(mp); + } + __CFSpinLock(&__CFAllMachPortsLock); + if (NULL != __CFAllMachPorts) { + CFDictionaryRemoveValue(__CFAllMachPorts, (void *)(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); + } + // here we get rid of the previous notify port + // [cjk - somehow it is the right thing to do to + // hold this until this point, then deallocate it, + // though I don't understand what that triggers + // with respect to the send-once right, and I + // doubt people are doing the right thing about + // handling the "death" (CFMachPort included) of + // the send-once right.] + 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); + __CFMachPortCheckForFork(); + return __CFMachPortIsValid(mp); +} + +CFMachPortInvalidationCallBack CFMachPortGetInvalidationCallBack(CFMachPortRef mp) { + __CFGenericValidateType(mp, __kCFMachPortTypeID); + __CFMachPortCheckForFork(); + return mp->_icallout; +} + +void CFMachPortSetInvalidationCallBack(CFMachPortRef mp, CFMachPortInvalidationCallBack callout) { + __CFGenericValidateType(mp, __kCFMachPortTypeID); + __CFMachPortCheckForFork(); + 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) { + 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; +} + +void CFMachPortInvalidateAll(void) { +// This function has been removed from the public API; +// it was a very bad idea to call it. +} + +static mach_port_t __CFMachPortGetPort(void *info) { + CFMachPortRef mp = info; + __CFMachPortCheckForFork(); + return mp->_port; +} + +static void *__CFMachPortPerform(void *msg, CFIndex size, CFAllocatorRef allocator, void *info) { + CFMachPortRef mp = info; + void *context_info; + void (*context_release)(const void *); + __CFMachPortCheckForFork(); + __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); + if (context_release) { + context_release(context_info); + } + return NULL; +} + +CFRunLoopSourceRef CFMachPortCreateRunLoopSource(CFAllocatorRef allocator, CFMachPortRef mp, CFIndex order) { + CFRunLoopSourceRef result = NULL; + __CFGenericValidateType(mp, __kCFMachPortTypeID); + __CFMachPortCheckForFork(); + __CFMachPortLock(mp); +#if 0 +#warning CF: adding ref to receive right is disabled for now -- doesnt work in 1F + if (!__CFMachPortHasReceive(mp)) { + kern_return_t ret; + +// this fails in 1F with KERN_INVALID_VALUE -- only 0 and -1 are valid for delta + ret = mach_port_mod_refs(mach_task_self(), mp->_port, MACH_PORT_RIGHT_RECEIVE, +1); + if (KERN_SUCCESS != ret) { + __CFMachPortUnlock(mp); + return NULL; + } + __CFMachPortSetHasReceive(mp); + } +#endif + 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 /* __MACH__ */ + diff --git a/RunLoop.subproj/CFMachPort.h b/RunLoop.subproj/CFMachPort.h new file mode 100644 index 0000000..510e408 --- /dev/null +++ b/RunLoop.subproj/CFMachPort.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFMachPort.h + Copyright (c) 1998-2003, Apple, Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFMACHPORT__) +#define __COREFOUNDATION_CFMACHPORT__ 1 + +#if defined(__MACH__) + +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +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); + +#if defined(__cplusplus) +} +#endif + +#endif /* __MACH__ */ + +#endif /* ! __COREFOUNDATION_CFMACHPORT__ */ + diff --git a/RunLoop.subproj/CFMessagePort.c b/RunLoop.subproj/CFMessagePort.c new file mode 100644 index 0000000..5112eed --- /dev/null +++ b/RunLoop.subproj/CFMessagePort.c @@ -0,0 +1,836 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFMessagePort.c + Copyright 1998-2002, Apple, Inc. All rights reserved. + Responsibility: Christopher Kane +*/ + +#if !defined(__WIN32__) + +#include +#include +#include +#include +#include +#include +#include "CFInternal.h" +#include +#include +#include +#include +#include +#include + +#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 = 0; +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; + 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 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)->_info, 0, 0); +} + +CF_INLINE void __CFMessagePortSetValid(CFMessagePortRef ms) { + __CFBitfieldSetValue(((CFRuntimeBase *)ms)->_info, 0, 0, 1); +} + +CF_INLINE void __CFMessagePortUnsetValid(CFMessagePortRef ms) { + __CFBitfieldSetValue(((CFRuntimeBase *)ms)->_info, 0, 0, 0); +} + +CF_INLINE Boolean __CFMessagePortIsRemote(CFMessagePortRef ms) { + return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)ms)->_info, 2, 2); +} + +CF_INLINE void __CFMessagePortSetRemote(CFMessagePortRef ms) { + __CFBitfieldSetValue(((CFRuntimeBase *)ms)->_info, 2, 2, 1); +} + +CF_INLINE void __CFMessagePortUnsetRemote(CFMessagePortRef ms) { + __CFBitfieldSetValue(((CFRuntimeBase *)ms)->_info, 2, 2, 0); +} + +CF_INLINE Boolean __CFMessagePortIsDeallocing(CFMessagePortRef ms) { + return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)ms)->_info, 3, 3); +} + +CF_INLINE void __CFMessagePortSetIsDeallocing(CFMessagePortRef ms) { + __CFBitfieldSetValue(((CFRuntimeBase *)ms)->_info, 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 = 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 Boolean __CFMessagePortNativeSetNameLocal(CFMachPortRef port, uint8_t *portname) { + mach_port_t bp; + kern_return_t ret; + task_get_bootstrap_port(mach_task_self(), &bp); + ret = bootstrap_register(bp, portname, CFMachPortGetPort(port)); + return (ret == KERN_SUCCESS) ? true : false; +} + +static Boolean __CFMessagePortEqual(CFTypeRef cf1, CFTypeRef cf2) { + CFMessagePortRef ms1 = (CFMessagePortRef)cf1; + CFMessagePortRef ms2 = (CFMessagePortRef)cf2; + return CFEqual(ms1->_port, ms2->_port); +} + +static CFHashCode __CFMessagePortHash(CFTypeRef cf) { + CFMessagePortRef ms = (CFMessagePortRef)cf; + return CFHash(ms->_port); +} + +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(CFGetAllocator(ms), NULL, CFSTR(""), ms->_context.info); + } + result = CFStringCreateWithFormat(CFGetAllocator(ms), NULL, CFSTR("{locked = %s, valid = %s, remote = %s, name = %@}"), cf, CFGetAllocator(ms), locked, (__CFMessagePortIsValid(ms) ? "Yes" : "No"), (__CFMessagePortIsRemote(ms) ? "Yes" : "No"), ms->_name); + } else { + result = CFStringCreateWithFormat(CFGetAllocator(ms), NULL, CFSTR("{locked = %s, valid = %s, remote = %s, name = %@, source = %p, callout = %p, context = %@}"), cf, CFGetAllocator(ms), locked, (__CFMessagePortIsValid(ms) ? "Yes" : "No"), (__CFMessagePortIsRemote(ms) ? "Yes" : "No"), ms->_name, ms->_source, ms->_callout, (NULL != contextDesc ? contextDesc : CFSTR(""))); + } + 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) { + CFMachPortInvalidate(ms->_port); + CFRelease(ms->_port); + } +} + +static CFTypeID __kCFMessagePortTypeID = _kCFRuntimeNotATypeID; + +static const CFRuntimeClass __CFMessagePortClass = { + 0, + "CFMessagePort", + NULL, // init + NULL, // copy + __CFMessagePortDeallocate, + __CFMessagePortEqual, + __CFMessagePortHash, + 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 = 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(info); +} + +CFMessagePortRef CFMessagePortCreateLocal(CFAllocatorRef allocator, CFStringRef name, CFMessagePortCallBack callout, CFMessagePortContext *context, Boolean *shouldFreeInfo) { + CFMessagePortRef memory; + CFMachPortRef native; + CFMachPortContext ctx; + uint8_t *utfname = NULL; + CFIndex size; + + if (shouldFreeInfo) *shouldFreeInfo = true; + if (NULL != name) { + name = __CFMessagePortSanitizeStringName(allocator, name, &utfname, NULL); + } + __CFSpinLock(&__CFAllMessagePortsLock); + if (NULL != name) { + CFMessagePortRef existing; + if (NULL != __CFAllLocalMessagePorts && CFDictionaryGetValueIfPresent(__CFAllLocalMessagePorts, name, (const void **)&existing)) { + __CFSpinUnlock(&__CFAllMessagePortsLock); + CFRelease(name); + CFAllocatorDeallocate(allocator, utfname); + return (CFMessagePortRef)CFRetain(existing); + } + } + size = sizeof(struct __CFMessagePort) - sizeof(CFRuntimeBase); + memory = (CFMessagePortRef)_CFRuntimeCreateInstance(allocator, __kCFMessagePortTypeID, size, NULL); + if (NULL == memory) { + __CFSpinUnlock(&__CFAllMessagePortsLock); + if (NULL != name) { + CFRelease(name); + } + CFAllocatorDeallocate(allocator, utfname); + return NULL; + } + __CFMessagePortUnsetValid(memory); + __CFMessagePortUnsetRemote(memory); + memory->_lock = 0; + memory->_name = name; + memory->_port = NULL; + memory->_replies = NULL; + memory->_convCounter = 0; + 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; + ctx.version = 0; + ctx.info = memory; + ctx.retain = NULL; + ctx.release = NULL; + ctx.copyDescription = NULL; + native = CFMachPortCreate(allocator, __CFMessagePortDummyCallback, &ctx, NULL); + if (NULL != native && NULL != name && !__CFMessagePortNativeSetNameLocal(native, utfname)) { + CFMachPortInvalidate(native); + CFRelease(native); + native = NULL; + } + CFAllocatorDeallocate(allocator, utfname); + if (NULL == native) { + __CFSpinUnlock(&__CFAllMessagePortsLock); + // name is released by deallocation + CFRelease(memory); + return NULL; + } + memory->_port = native; + CFMachPortSetInvalidationCallBack(native, __CFMessagePortInvalidationCallBack); + __CFMessagePortSetValid(memory); + if (NULL != context) { + memmove(&memory->_context, context, sizeof(CFMessagePortContext)); + memory->_context.info = context->retain ? (void *)context->retain(context->info) : context->info; + } + if (NULL != name) { + if (NULL == __CFAllLocalMessagePorts) { + __CFAllLocalMessagePorts = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeDictionaryKeyCallBacks, NULL); + } + CFDictionaryAddValue(__CFAllLocalMessagePorts, name, memory); + } + __CFSpinUnlock(&__CFAllMessagePortsLock); + if (shouldFreeInfo) *shouldFreeInfo = false; + return memory; +} + +CFMessagePortRef CFMessagePortCreateRemote(CFAllocatorRef allocator, CFStringRef name) { + 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 (NULL != name) { + CFMessagePortRef existing; + if (NULL != __CFAllRemoteMessagePorts && CFDictionaryGetValueIfPresent(__CFAllRemoteMessagePorts, name, (const void **)&existing)) { + __CFSpinUnlock(&__CFAllMessagePortsLock); + CFRelease(name); + CFAllocatorDeallocate(allocator, utfname); + return (CFMessagePortRef)CFRetain(existing); + } + } + size = sizeof(struct __CFMessagePort) - sizeof(CFMessagePortContext) - sizeof(CFRuntimeBase); + memory = (CFMessagePortRef)_CFRuntimeCreateInstance(allocator, __kCFMessagePortTypeID, size, NULL); + if (NULL == memory) { + __CFSpinUnlock(&__CFAllMessagePortsLock); + if (NULL != name) { + CFRelease(name); + } + CFAllocatorDeallocate(allocator, utfname); + return NULL; + } + __CFMessagePortUnsetValid(memory); + __CFMessagePortSetRemote(memory); + memory->_lock = 0; + memory->_name = name; + memory->_port = NULL; + memory->_replies = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, NULL, NULL); + memory->_convCounter = 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_up(bp, utfname, &port); + native = (KERN_SUCCESS == ret) ? CFMachPortCreateWithPort(allocator, port, __CFMessagePortDummyCallback, &ctx, NULL) : NULL; + CFAllocatorDeallocate(allocator, utfname); + if (NULL == native) { + __CFSpinUnlock(&__CFAllMessagePortsLock); + // name is released by deallocation + CFRelease(memory); + return NULL; + } + memory->_port = native; + CFMachPortSetInvalidationCallBack(native, __CFMessagePortInvalidationCallBack); + __CFMessagePortSetValid(memory); + if (NULL != name) { + if (NULL == __CFAllRemoteMessagePorts) { + __CFAllRemoteMessagePorts = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeDictionaryKeyCallBacks, NULL); + } + CFDictionaryAddValue(__CFAllRemoteMessagePorts, name, memory); + } + __CFSpinUnlock(&__CFAllMessagePortsLock); + return (CFMessagePortRef)memory; +} + +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 (__CFMessagePortIsRemote(ms)) return false; +//#warning CF: make this an assertion +// and assert than newName is non-NULL + 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; + } + } + if (NULL != name && (NULL == ms->_name || !CFEqual(ms->_name, name))) { + if (!__CFMessagePortNativeSetNameLocal(ms->_port, utfname)) { + __CFSpinUnlock(&__CFAllMessagePortsLock); + CFRelease(name); + CFAllocatorDeallocate(allocator, utfname); + return false; + } + 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 (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 = info; + struct __CFMessagePortMachMessage *msgp = 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 *)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 = (void *)0xffffffff; // 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, 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 *)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; + CFDictionarySetValue(remote->_replies, (void *)desiredReply, NULL); + sendmsg = __CFMessagePortCreateMessage(CFGetAllocator(remote), 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) { + 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); + CFAllocatorDeallocate(CFGetAllocator(remote), sendmsg); + if (KERN_SUCCESS != ret) { + if (didRegister) { + CFRunLoopRemoveSource(currentRL, source, replyMode); + CFRelease(source); + } + if (MACH_SEND_TIMED_OUT == ret) return kCFMessagePortSendTimeout; + return kCFMessagePortTransportError; + } + 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 = CFDictionaryGetValue(remote->_replies, (void *)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); + CFRelease(source); + } + if (NULL == reply) { + CFDictionaryRemoveValue(remote->_replies, (void *)desiredReply); + CFRelease(remote); + return CFMessagePortIsValid(remote) ? kCFMessagePortReceiveTimeout : -5; + } + if (NULL != returnDatap) { + *returnDatap = ((void *)0xffffffff == reply) ? NULL : reply; + } else if ((void *)0xffffffff != reply) { + CFRelease(reply); + } + CFDictionaryRemoveValue(remote->_replies, (void *)desiredReply); + CFRelease(remote); + return kCFMessagePortSuccess; +} + +static mach_port_t __CFMessagePortGetPort(void *info) { + CFMessagePortRef ms = info; + return CFMachPortGetPort(ms->_port); +} + +static void *__CFMessagePortPerform(void *msg, CFIndex size, CFAllocatorRef allocator, void *info) { + CFMessagePortRef ms = info; + struct __CFMessagePortMachMessage *msgp = 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, 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, 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 = (Boolean (*)(const void *, const void *))__CFMessagePortEqual; + context.hash = (CFHashCode (*)(const void *))__CFMessagePortHash; + 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 /* !__WIN32__ */ + diff --git a/RunLoop.subproj/CFMessagePort.h b/RunLoop.subproj/CFMessagePort.h new file mode 100644 index 0000000..51ce252 --- /dev/null +++ b/RunLoop.subproj/CFMessagePort.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFMessagePort.h + Copyright (c) 1998-2003, Apple, Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFMESSAGEPORT__) +#define __COREFOUNDATION_CFMESSAGEPORT__ 1 + +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +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); + +#if defined(__cplusplus) +} +#endif + +#endif /* ! __COREFOUNDATION_CFMESSAGEPORT__ */ + diff --git a/RunLoop.subproj/CFRunLoop.c b/RunLoop.subproj/CFRunLoop.c new file mode 100644 index 0000000..ec5a976 --- /dev/null +++ b/RunLoop.subproj/CFRunLoop.c @@ -0,0 +1,2593 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFRunLoop.c + Copyright 1998-2002, Apple, Inc. All rights reserved. + Responsibility: Christopher Kane +*/ + +#include +#include +#include +#include "CFInternal.h" +#include +#include +#if defined(__MACH__) +#include +#include +#include +#else +#include +#endif + +extern bool CFDictionaryGetKeyIfPresent(CFDictionaryRef dict, const void *key, const void **actualkey); + +#if defined(__MACH__) +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; +} +#endif + +#if defined(__MACH__) +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); + return result; +} + +static kern_return_t __CFClearPortSet(mach_port_t task, mach_port_t portSet) { + kern_return_t ret; + mach_port_name_array_t array; + mach_msg_type_number_t idx, number; + + ret = mach_port_get_set_status(task, portSet, &array, &number); + if (KERN_SUCCESS != ret) return ret; + for (idx = 0; idx < number; idx++) { + ret = mach_port_extract_member(task, array[idx], portSet); + if (KERN_SUCCESS != ret) { + vm_deallocate(task, (vm_address_t)array, number * sizeof(mach_port_name_t)); + return ret; + } + } + vm_deallocate(task, (vm_address_t)array, number * sizeof(mach_port_name_t)); + return KERN_SUCCESS; +} +#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 +#if defined(__MACH__) + mach_port_t _portSet; +#endif +#if defined(__WIN32__) + DWORD _msgQMask; +#endif +}; + +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("{name = %@, locked = %s, "), rlm, CFGetAllocator(rlm), rlm->_name, rlm->_lock ? "true" : "false"); +#if defined(__MACH__) + CFStringAppendFormat(result, NULL, CFSTR("port set = %p,"), rlm->_portSet); +#endif +#if defined(__WIN32__) + 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); +#if defined(__MACH__) + __CFClearPortSet(mach_task_self(), rlm->_portSet); + mach_port_destroy(mach_task_self(), rlm->_portSet); +#endif +} + +struct __CFRunLoop { + CFRuntimeBase _base; + CFSpinLock_t _lock; /* locked for accessing mode list */ +#if defined(__MACH__) + mach_port_t _waitPort; +#endif +#if defined(__WIN32__) + HANDLE _waitPort; +#endif + volatile CFIndex *_stopped; + CFMutableSetRef _commonModes; + CFMutableSetRef _commonModeItems; + CFRunLoopModeRef _currentMode; + CFMutableSetRef _modes; +}; + +/* 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)->_info, 1, 1); +} + +CF_INLINE void __CFRunLoopSetSleeping(CFRunLoopRef rl) { + __CFBitfieldSetValue(((CFRuntimeBase *)rl)->_info, 1, 1, 1); +} + +CF_INLINE void __CFRunLoopUnsetSleeping(CFRunLoopRef rl) { + __CFBitfieldSetValue(((CFRuntimeBase *)rl)->_info, 1, 1, 0); +} + +CF_INLINE Boolean __CFRunLoopIsDeallocating(CFRunLoopRef rl) { + return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)rl)->_info, 2, 2); +} + +CF_INLINE void __CFRunLoopSetDeallocating(CFRunLoopRef rl) { + __CFBitfieldSetValue(((CFRuntimeBase *)rl)->_info, 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("{locked = %s, wait port = 0x%x, stopped = %s,\ncurrent mode = %@,\n"), cf, CFGetAllocator(cf), rl->_lock ? "true" : "false", rl->_waitPort, (rl->_stopped && *(rl->_stopped)) ? "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) { + CFRunLoopModeRef rlm; + struct __CFRunLoopMode srlm; + srlm._base._isa = __CFISAForTypeID(__kCFRunLoopModeTypeID); + srlm._base._info = 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; + } + rlm->_lock = 0; + rlm->_name = CFStringCreateCopy(CFGetAllocator(rlm), modeName); + rlm->_stopped = false; + rlm->_sources = NULL; + rlm->_observers = NULL; + rlm->_timers = NULL; + rlm->_submodes = NULL; +#if defined(__MACH__) + { + kern_return_t ret; + ret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_PORT_SET, &(rlm->_portSet)); + if (KERN_SUCCESS == ret) { + ret = mach_port_insert_member(mach_task_self(), rl->_waitPort, rlm->_portSet); + } + if (KERN_SUCCESS != ret) HALT; + } +#endif +#if defined(__WIN32__) + rlm->_msgQMask = 0; +#endif + CFSetAddValue(rl->_modes, rlm); + CFRelease(rlm); + __CFRunLoopModeLock(rlm); /* return mode locked */ + return rlm; +} + +#if defined(__WIN32__) + +// expects rl and rlm locked +static Boolean __CFRunLoopModeIsEmpty(CFRunLoopRef rl, CFRunLoopModeRef rlm) { + if (NULL == rlm) return true; + if (0 != rlm->_msgQMask) return false; + 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; +} + +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); +} + +#else + +// expects rl and rlm locked +static Boolean __CFRunLoopModeIsEmpty(CFRunLoopRef rl, CFRunLoopModeRef rlm) { + if (NULL == rlm) return true; + 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; +} + +#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)->_info, 3, 3); +} + +CF_INLINE void __CFSetValid(void *cf) { + __CFBitfieldSetValue(((CFRuntimeBase *)cf)->_info, 3, 3, 1); +} + +CF_INLINE void __CFUnsetValid(void *cf) { + __CFBitfieldSetValue(((CFRuntimeBase *)cf)->_info, 3, 3, 0); +} + +struct __CFRunLoopSource { + CFRuntimeBase _base; + uint32_t _bits; + CFSpinLock_t _lock; + CFIndex _order; /* immutable */ + CFMutableBagRef _runLoops; + union { + CFRunLoopSourceContext version0; /* immutable, except invalidation */ +#if defined(__MACH__) + CFRunLoopSourceContext1 version1; /* immutable, except invalidation */ +#endif + } _context; +}; + +/* Bit 1 of the base reserved bits is used for signaled 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); + } +#if defined(__MACH__) + } else if (1 == rls->_context.version0.version) { + mach_port_t port; + port = rls->_context.version1.getPort(rls->_context.version1.info); + if (MACH_PORT_NULL != port) { + mach_port_insert_member(mach_task_self(), port, rlm->_portSet); + } +#endif + } +} + +/* 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 */ + } +#if defined(__MACH__) + } else if (1 == rls->_context.version0.version) { + mach_port_t port; + port = rls->_context.version1.getPort(rls->_context.version1.info); /* CALLOUT */ + if (MACH_PORT_NULL != port) { + mach_port_extract_member(mach_task_self(), port, rlm->_portSet); + } +#endif + } + __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)->_info, 0, 0); +} + +CF_INLINE void __CFRunLoopObserverSetFiring(CFRunLoopObserverRef rlo) { + __CFBitfieldSetValue(((CFRuntimeBase *)rlo)->_info, 0, 0, 1); +} + +CF_INLINE void __CFRunLoopObserverUnsetFiring(CFRunLoopObserverRef rlo) { + __CFBitfieldSetValue(((CFRuntimeBase *)rlo)->_info, 0, 0, 0); +} + +CF_INLINE Boolean __CFRunLoopObserverRepeats(CFRunLoopObserverRef rlo) { + return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)rlo)->_info, 1, 1); +} + +CF_INLINE void __CFRunLoopObserverSetRepeats(CFRunLoopObserverRef rlo) { + __CFBitfieldSetValue(((CFRuntimeBase *)rlo)->_info, 1, 1, 1); +} + +CF_INLINE void __CFRunLoopObserverUnsetRepeats(CFRunLoopObserverRef rlo) { + __CFBitfieldSetValue(((CFRuntimeBase *)rlo)->_info, 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 defined(__MACH__) + 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 has-reset state */ + +CF_INLINE Boolean __CFRunLoopTimerIsFiring(CFRunLoopTimerRef rlt) { + return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)rlt)->_info, 0, 0); +} + +CF_INLINE void __CFRunLoopTimerSetFiring(CFRunLoopTimerRef rlt) { + __CFBitfieldSetValue(((CFRuntimeBase *)rlt)->_info, 0, 0, 1); +} + +CF_INLINE void __CFRunLoopTimerUnsetFiring(CFRunLoopTimerRef rlt) { + __CFBitfieldSetValue(((CFRuntimeBase *)rlt)->_info, 0, 0, 0); +} + +CF_INLINE void __CFRunLoopTimerLock(CFRunLoopTimerRef rlt) { + __CFSpinLock(&(rlt->_lock)); +} + +CF_INLINE void __CFRunLoopTimerUnlock(CFRunLoopTimerRef rlt) { + __CFSpinUnlock(&(rlt->_lock)); +} + +static CFSpinLock_t __CFRLTFireTSRLock = 0; + +CF_INLINE void __CFRunLoopTimerFireTSRLock(void) { + __CFSpinLock(&__CFRLTFireTSRLock); +} + +CF_INLINE void __CFRunLoopTimerFireTSRUnlock(void) { + __CFSpinUnlock(&__CFRLTFireTSRLock); +} + +static CFMutableDictionaryRef __CFRLTPortMap = NULL; +static CFSpinLock_t __CFRLTPortMapLock = 0; + +CF_INLINE void __CFRunLoopTimerPortMapLock(void) { + __CFSpinLock(&__CFRLTPortMapLock); +} + +CF_INLINE void __CFRunLoopTimerPortMapUnlock(void) { + __CFSpinUnlock(&__CFRLTPortMapLock); +} + +static void __CFRunLoopTimerSchedule(CFRunLoopTimerRef rlt, CFRunLoopRef rl, CFRunLoopModeRef rlm) { +#if defined(__MACH__) + __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 *)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 defined(__MACH__) + __CFRunLoopTimerLock(rlt); + mach_port_extract_member(mach_task_self(), rlt->_port, rlm->_portSet); + rlt->_rlCount--; + if (0 == rlt->_rlCount) { + __CFRunLoopTimerPortMapLock(); + if (NULL != __CFRLTPortMap) { + CFDictionaryRemoveValue(__CFRLTPortMap, (void *)rlt->_port); + } + __CFRunLoopTimerPortMapUnlock(); + rlt->_runLoop = NULL; + mk_timer_cancel(rlt->_port, NULL); + } + __CFRunLoopTimerUnlock(rlt); +#endif +} + +static void __CFRunLoopTimerRescheduleWithAllModes(CFRunLoopTimerRef rlt, CFRunLoopRef rl) { +#if defined(__MACH__) + mk_timer_arm(rlt->_port, __CFUInt64ToAbsoluteTime(rlt->_fireTSR)); +#endif +} + + +/* CFRunLoop */ + +CONST_STRING_DECL(kCFRunLoopDefaultMode, "kCFRunLoopDefaultMode") +CONST_STRING_DECL(kCFRunLoopCommonModes, "kCFRunLoopCommonModes") + +#if defined(__MACH__) + +struct _findsource { + mach_port_t port; + CFRunLoopSourceRef result; +}; + +static void __CFRunLoopFindSource(const void *value, void *ctx) { + CFRunLoopSourceRef rls = (CFRunLoopSourceRef)value; + struct _findsource *context = (struct _findsource *)ctx; + mach_port_t 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, mach_port_t port) { /* DOES CALLOUT */ + 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; +} + +// call with rl and rlm locked +static CFRunLoopTimerRef __CFRunLoopModeFindTimerForMachPort(CFRunLoopModeRef rlm, mach_port_name_t port) { + CFRunLoopTimerRef result = NULL; + __CFRunLoopTimerPortMapLock(); + if (NULL != __CFRLTPortMap) { + result = (CFRunLoopTimerRef)CFDictionaryGetValue(__CFRLTPortMap, (void *)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 : 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 : 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 : 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); + } +#if defined(__MACH__) + mach_port_destroy(mach_task_self(), rl->_waitPort); + rl->_waitPort = 0; +#endif +#if defined(__WIN32__) + CloseHandle(rl->_waitPort); + rl->_waitPort = 0; +#endif + __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 +}; + +__private_extern__ void __CFRunLoopInitialize(void) { + __kCFRunLoopTypeID = _CFRuntimeRegisterClass(&__CFRunLoopClass); + __kCFRunLoopModeTypeID = _CFRuntimeRegisterClass(&__CFRunLoopModeClass); +} + +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; + loop->_lock = 0; +#if defined(__MACH__) + { + kern_return_t ret; + ret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &(loop->_waitPort)); + if (KERN_SUCCESS == ret) { + ret = mach_port_insert_right(mach_task_self(), loop->_waitPort, loop->_waitPort, 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(), loop->_waitPort, MACH_PORT_LIMITS_INFO, (mach_port_info_t)&limits, MACH_PORT_LIMITS_INFO_COUNT); + } + if (KERN_SUCCESS != ret) HALT; + } +#elif defined(__WIN32__) + loop->_waitPort = CreateEvent(NULL, true, false, NULL); +#endif + 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); + rlm = __CFRunLoopFindMode(loop, kCFRunLoopDefaultMode, true); + if (NULL != rlm) __CFRunLoopModeUnlock(rlm); + return loop; +} + +static CFRunLoopRef mainLoop = NULL; +static int mainLoopPid = 0; +static CFSpinLock_t mainLoopLock = 0; + +CFRunLoopRef CFRunLoopGetMain(void) { + __CFSpinLock(&mainLoopLock); + if (mainLoopPid != getpid()) { + // intentionally leak mainLoop so we don't kill any ports in the child + mainLoop = NULL; + } + if (!mainLoop) { + mainLoop = __CFRunLoopCreate(); + mainLoopPid = getpid(); + } + __CFSpinUnlock(&mainLoopLock); + return mainLoop; +} + +static void _CFRunLoopSetMain(CFRunLoopRef rl) { + if (rl != mainLoop) { + if (rl) CFRetain(rl); +// intentionally leak the old main run loop +// if (mainLoop) CFRelease(mainLoop); + mainLoop = rl; + } +} + +CFRunLoopRef CFRunLoopGetCurrent(void) { + if (pthread_main_np()) { + return CFRunLoopGetMain(); + } + CFRunLoopRef currentLoop = __CFGetThreadSpecificData_inline()->_runLoop; + int currentLoopPid = __CFGetThreadSpecificData_inline()->_runLoop_pid; + if (currentLoopPid != getpid()) { + // intentionally leak currentLoop so we don't kill any ports in the child + currentLoop = NULL; + } + if (!currentLoop) { + currentLoop = __CFRunLoopCreate(); + __CFGetThreadSpecificData_inline()->_runLoop = currentLoop; + __CFGetThreadSpecificData_inline()->_runLoop_pid = getpid(); + } + return currentLoop; +} + +void _CFRunLoopSetCurrent(CFRunLoopRef rl) { + if (pthread_main_np()) { + return _CFRunLoopSetMain(rl); + } + CFRunLoopRef currentLoop = __CFGetThreadSpecificData_inline()->_runLoop; + if (rl != currentLoop) { + if (rl) CFRetain(rl); +// intentionally leak old run loop +// if (currentLoop) CFRelease(currentLoop); + __CFGetThreadSpecificData_inline()->_runLoop = rl; + __CFGetThreadSpecificData_inline()->_runLoop_pid = getpid(); + } +} + +CFStringRef CFRunLoopCopyCurrentMode(CFRunLoopRef rl) { + CFStringRef result = NULL; + __CFRunLoopLock(rl); + if (NULL != rl->_currentMode) { + result = 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) { + CFMutableArrayRef array; + __CFRunLoopLock(rl); + array = CFArrayCreateMutable(kCFAllocatorDefault, 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); + } +} + +void CFRunLoopAddCommonMode(CFRunLoopRef rl, CFStringRef modeName) { + 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 __CFRunLoopObserverComparator(const void *val1, const void *val2, void *context) { + CFRunLoopObserverRef o1 = (CFRunLoopObserverRef)val1; + CFRunLoopObserverRef o2 = (CFRunLoopObserverRef)val2; + if (o1->_order < o2->_order) return kCFCompareLessThan; + if (o2->_order < o1->_order) return kCFCompareGreaterThan; + return kCFCompareEqualTo; +} + +struct _collectobs { + CFRunLoopActivity activity; + CFMutableArrayRef array; +}; + +static void __CFRunLoopCollectObservers(const void *value, void *context) { + CFRunLoopObserverRef rlo = (CFRunLoopObserverRef)value; + struct _collectobs *info = (struct _collectobs *)context; + if (0 != (rlo->_activities & info->activity) && __CFIsValid(rlo) && !__CFRunLoopObserverIsFiring(rlo)) { + CFArrayAppendValue(info->array, rlo); + } +} + +/* 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 */ + CFIndex idx, cnt; + CFMutableArrayRef array; + CFArrayRef submodes; + struct _collectobs info; + + /* Fire the observers */ + submodes = (NULL != rlm->_submodes && 0 < CFArrayGetCount(rlm->_submodes)) ? CFArrayCreateCopy(kCFAllocatorSystemDefault, rlm->_submodes) : NULL; + if (NULL != rlm->_observers) { + array = CFArrayCreateMutable(kCFAllocatorSystemDefault, CFSetGetCount(rlm->_observers), &kCFTypeArrayCallBacks); + info.array = array; + info.activity = activity; + CFSetApplyFunction(rlm->_observers, (__CFRunLoopCollectObservers), &info); + cnt = CFArrayGetCount(array); + if (0 < cnt) { + __CFRunLoopModeUnlock(rlm); + CFArraySortValues(array, CFRangeMake(0, cnt), (__CFRunLoopObserverComparator), NULL); + for (idx = 0; idx < cnt; idx++) { + CFRunLoopObserverRef rlo = (CFRunLoopObserverRef)CFArrayGetValueAtIndex(array, idx); + __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); + } + } + __CFRunLoopModeLock(rlm); + } + CFRelease(array); + } + 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 */ + 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 */ + } + sourceHandled = true; + } else { + __CFRunLoopSourceUnlock(rls); + } + } else { + cnt = CFArrayGetCount(sources); + CFArraySortValues((CFMutableArrayRef)sources, CFRangeMake(0, cnt), (__CFRunLoopSourceComparator), NULL); + for (idx = 0; idx < cnt; idx++) { + CFRunLoopSourceRef rls = (CFRunLoopSourceRef)CFArrayGetValueAtIndex(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 */ + } + sourceHandled = true; + } else { + __CFRunLoopSourceUnlock(rls); + } + if (stopAfterHandle && sourceHandled) { + break; + } + } + } + CFRelease(sources); + __CFRunLoopModeLock(rlm); + } + return sourceHandled; +} + +#if defined(__MACH__) +static Boolean __CFRunLoopDoSource1(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunLoopSourceRef rls, mach_msg_header_t *msg, CFIndex size, mach_msg_header_t **reply) { /* DOES CALLOUT */ + 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) { + *reply = rls->_context.version1.perform(msg, size, kCFAllocatorSystemDefault, rls->_context.version1.info); /* CALLOUT */ + } + sourceHandled = true; + } else { + __CFRunLoopSourceUnlock(rls); + } + CFRelease(rls); + __CFRunLoopModeLock(rlm); + return sourceHandled; +} +#endif + +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)) { + __CFRunLoopTimerSetFiring(rlt); + __CFRunLoopTimerUnlock(rlt); + __CFRunLoopTimerFireTSRLock(); + oldFireTSR = rlt->_fireTSR; + __CFRunLoopTimerFireTSRUnlock(); + rlt->_callout(rlt, rlt->_context.info); /* CALLOUT */ + __CFRunLoopTimerUnsetFiring(rlt); + timerHandled = true; + } else { + __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. */ + } 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; + __CFRunLoopTimerFireTSRUnlock(); + __CFRunLoopTimerRescheduleWithAllModes(rlt, rl); + } + } + CFRelease(rlt); + __CFRunLoopModeLock(rlm); + return timerHandled; +} + +CF_EXPORT Boolean _CFRunLoopFinished(CFRunLoopRef rl, CFStringRef modeName) { + 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 +#if defined(__MACH__) +static void __CFRunLoopModeAddPortsToPortSet(CFRunLoopRef rl, CFRunLoopModeRef rlm, mach_port_t 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 : CFAllocatorAllocate(kCFAllocatorSystemDefault, cnt * sizeof(void *), 0); + CFSetGetValues(rlm->_sources, list); + for (idx = 0; idx < cnt; idx++) { + CFRunLoopSourceRef rls = (CFRunLoopSourceRef)list[idx]; + mach_port_t port; + if (1 != rls->_context.version0.version) continue; + port = rls->_context.version1.getPort(rls->_context.version1.info); + if (MACH_PORT_NULL != port) { + mach_port_insert_member(mach_task_self(), port, portSet); + } + } + if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list); + } + if (NULL != rlm->_timers) { + cnt = CFSetGetCount(rlm->_timers); + list = (cnt <= 256) ? buffer : 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); + } + // 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); + } + } +} +#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 defined(__MACH__) + mach_port_name_t timeoutPort = MACH_PORT_NULL; + Boolean timeoutPortAdded = false; +#elif defined(__WIN32__) + HANDLE timeoutPort = NULL; +#endif + Boolean poll = false; + + if (__CFRunLoopIsStopped(rl)) { + return kCFRunLoopRunStopped; + } else if (rlm->_stopped) { + rlm->_stopped = false; + return kCFRunLoopRunStopped; + } +#if !defined(__WIN32__) + if (seconds <= 0.0) { + termTSR = 0; + } else if (__CFTSRToTimeInterval(LLONG_MAX) < seconds) { + termTSR = LLONG_MAX; + } else if ((uint64_t)LLONG_MAX <= __CFReadTSR() + (uint64_t)__CFTimeIntervalToTSR(seconds)) { + termTSR = LLONG_MAX; + } else { + termTSR = (int64_t)__CFReadTSR() + __CFTimeIntervalToTSR(seconds); + timeoutPort = mk_timer_create(); + mk_timer_arm(timeoutPort, __CFUInt64ToAbsoluteTime(termTSR)); + } +#elif defined(__WIN32__) + { + //int64_t time = (int64_t)(seconds * -10000000.0); + //timeoutPort = CreateWaitableTimer(NULL,FALSE,NULL); + //SetWaitableTimer(rl->_waitPort, &time, 0, NULL, NULL); + } +#endif + if (seconds <= 0.0) { + poll = true; + } + for (;;) { +#if defined(__MACH__) + mach_msg_header_t *msg; + kern_return_t ret; + mach_port_t waitSet = MACH_PORT_NULL; + Boolean destroyWaitSet = false; +#endif + CFRunLoopSourceRef rls; + int32_t returnValue = 0; + Boolean sourceHandledThisLoop = false; + uint8_t buffer[1024 + 80]; // large enough for 1k of inline payload + + __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 defined(__MACH__) + if (NULL != rlm->_submodes) { + // !!! what do we do if this doesn't succeed? + ret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_PORT_SET, &waitSet); + if (KERN_SUCCESS != ret) HALT; + __CFRunLoopModeUnlock(rlm); + __CFRunLoopLock(rl); + __CFRunLoopModeLock(rlm); + __CFRunLoopModeAddPortsToPortSet(rl, rlm, waitSet); + __CFRunLoopUnlock(rl); + if (MACH_PORT_NULL != timeoutPort) { + mach_port_insert_member(mach_task_self(), timeoutPort, waitSet); + } + destroyWaitSet = true; + } else { + waitSet = rlm->_portSet; + if (!timeoutPortAdded && MACH_PORT_NULL != timeoutPort) { + mach_port_insert_member(mach_task_self(), timeoutPort, waitSet); + timeoutPortAdded = true; + } + } + __CFRunLoopModeUnlock(rlm); + + 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; +#if defined(MACH_RCV_TRAILER_AUDIT) + 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); +#else + 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_SENDER), 0, msg->msgh_size, waitSet, 0, MACH_PORT_NULL); +#endif + if (MACH_RCV_TOO_LARGE == ret) { +#if defined(MACH_RCV_TRAILER_AUDIT) + uint32_t newSize = round_msg(msg->msgh_size) + sizeof(mach_msg_audit_trailer_t); +#else + uint32_t newSize = round_msg(msg->msgh_size) + sizeof(mach_msg_security_trailer_t); +#endif + if (msg == (mach_msg_header_t *)buffer) msg = NULL; + msg = 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 defined(__WIN32__) +// should msgQMask be an OR'ing of this and all submodes' masks? + if (0 == GetQueueStatus(rlm->_msgQMask)) { + HANDLE objects[2]; + objects[0] = rl->_waitPort; + //objects[1] = timeoutPort; + MsgWaitForMultipleObjects(1 /*1*/, objects /*&(rl->_waitPort)*/, false, seconds, rlm->_msgQMask); + } + ResetEvent(rl->_waitPort); +#endif + +#if defined(__MACH__) + if (destroyWaitSet) { + __CFClearPortSet(mach_task_self(), waitSet); + mach_port_destroy(mach_task_self(), waitSet); + } +#endif + __CFRunLoopLock(rl); + __CFRunLoopModeLock(rlm); + __CFRunLoopUnlock(rl); + if (!poll) { + __CFRunLoopUnsetSleeping(rl); + __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting); + } + poll = false; + __CFRunLoopModeUnlock(rlm); + __CFRunLoopLock(rl); + __CFRunLoopModeLock(rlm); + +#if defined(__MACH__) + if (NULL != msg) { + if (msg->msgh_local_port == timeoutPort) { + returnValue = kCFRunLoopRunTimedOut; + __CFRunLoopUnlock(rl); + } else if (msg->msgh_local_port == rl->_waitPort) { + // wakeup + __CFRunLoopUnlock(rl); + } else if (NULL != (rls = __CFRunLoopModeFindSourceForMachPort(rl, rlm, msg->msgh_local_port))) { + mach_msg_header_t *reply = NULL; + __CFRunLoopUnlock(rl); + if (__CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply)) { + sourceHandledThisLoop = true; + } + 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, msg->msgh_local_port); + __CFRunLoopUnlock(rl); + if (NULL != rlt) { + __CFRunLoopDoTimer(rl, rlm, rlt); + } + } + if (msg != (mach_msg_header_t *)buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, msg); + } else { + __CFRunLoopUnlock(rl); + } +#endif + + __CFRunLoopModeUnlock(rlm); // locks must be taken in order + __CFRunLoopLock(rl); + __CFRunLoopModeLock(rlm); + if (sourceHandledThisLoop && stopAfterHandle) { + returnValue = kCFRunLoopRunHandledSource; + } 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 defined(__MACH__) + if (MACH_PORT_NULL != timeoutPort) { + if (!destroyWaitSet) mach_port_extract_member(mach_task_self(), timeoutPort, waitSet); + mk_timer_destroy(timeoutPort); + } +#endif + return returnValue; + } + } +} + +void CFRunLoopRun(void) { /* DOES CALLOUT */ + int32_t result; + do { + result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false); + } while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result); +} + +SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) { /* DOES CALLOUT */ + CFRunLoopModeRef currentMode, previousMode; + CFIndex *previousStopped; + int32_t result; + + if (__CFRunLoopIsDeallocating(rl)) return kCFRunLoopRunFinished; + __CFRunLoopLock(rl); + currentMode = __CFRunLoopFindMode(rl, modeName, false); + if (NULL == currentMode || __CFRunLoopModeIsEmpty(rl, currentMode)) { + if (currentMode) __CFRunLoopModeUnlock(currentMode); + __CFRunLoopUnlock(rl); + return kCFRunLoopRunFinished; + } + previousStopped = (CFIndex *)rl->_stopped; + rl->_stopped = CFAllocatorAllocate(kCFAllocatorSystemDefault, 16, 0); + rl->_stopped[0] = 0x4346524C; + rl->_stopped[1] = 0x4346524C; // 'CFRL' + rl->_stopped[2] = 0x00000000; // here the value is stored + rl->_stopped[3] = 0x4346524C; + previousMode = rl->_currentMode; + rl->_currentMode = currentMode; + __CFRunLoopUnlock(rl); + __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry); + result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, false); + __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit); + __CFRunLoopModeUnlock(currentMode); + __CFRunLoopLock(rl); + CFAllocatorDeallocate(kCFAllocatorSystemDefault, (void *)rl->_stopped); + rl->_stopped = previousStopped; + rl->_currentMode = previousMode; + __CFRunLoopUnlock(rl); + return result; +} + +SInt32 CFRunLoopRunInMode(CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) { /* DOES CALLOUT */ + return CFRunLoopRunSpecific(CFRunLoopGetCurrent(), modeName, seconds, returnAfterSourceHandled); +} + +static void __CFRunLoopFindMinTimer(const void *value, void *ctx) { + CFRunLoopTimerRef rlt = (CFRunLoopTimerRef)value; + CFRunLoopTimerRef *result = ctx; + if (NULL == *result || rlt->_fireTSR < (*result)->_fireTSR) { + *result = rlt; + } +} + +CFAbsoluteTime CFRunLoopGetNextTimerFireDate(CFRunLoopRef rl, CFStringRef modeName) { + CFRunLoopModeRef rlm; + CFRunLoopTimerRef result = NULL; + int64_t fireTime = 0; + __CFRunLoopLock(rl); + rlm = __CFRunLoopFindMode(rl, modeName, false); + __CFRunLoopUnlock(rl); + if (rlm) { + if (NULL != rlm->_timers && 0 < CFSetGetCount(rlm->_timers)) { + __CFRunLoopTimerFireTSRLock(); + CFSetApplyFunction(rlm->_timers, (__CFRunLoopFindMinTimer), &result); + fireTime = result->_fireTSR; + __CFRunLoopTimerFireTSRUnlock(); + } + __CFRunLoopModeUnlock(rlm); + } + return (0 == fireTime) ? 0.0 : __CFTSRToAbsoluteTime(fireTime); +} + +Boolean CFRunLoopIsWaiting(CFRunLoopRef rl) { + return __CFRunLoopIsSleeping(rl); +} + +void CFRunLoopWakeUp(CFRunLoopRef rl) { +#if defined(__MACH__) + 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->_waitPort, 0, MACH_SEND_TIMEOUT, 0); + if (ret != MACH_MSG_SUCCESS && ret != MACH_SEND_TIMED_OUT) { + HALT; + } +#else + SetEvent(rl->_waitPort); +#endif +} + +void CFRunLoopStop(CFRunLoopRef rl) { + __CFRunLoopLock(rl); + __CFRunLoopSetStopped(rl); + __CFRunLoopUnlock(rl); + CFRunLoopWakeUp(rl); +} + +CF_EXPORT void _CFRunLoopStopMode(CFRunLoopRef rl, CFStringRef modeName) { + 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) { + 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) { + 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) { + 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) { + 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 */ + 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 */ + 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) { + 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) { + 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) { + 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) { + 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) { + 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) { + 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) { + contextDesc = CFStringCreateWithFormat(CFGetAllocator(rls), NULL, CFSTR(""), rls->_context.version0.info); + } + result = CFStringCreateWithFormat(CFGetAllocator(rls), NULL, CFSTR("{locked = %s, valid = %s, order = %d, context = %@}"), cf, CFGetAllocator(rls), rls->_lock ? "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 = { + 0, + "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) { + 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); + memory->_lock = 0; + memory->_bits = 0; + memory->_order = order; + memory->_runLoops = NULL; +#if defined(__MACH__) + memmove(&memory->_context, context, (0 == context->version) ? sizeof(CFRunLoopSourceContext) : sizeof(CFRunLoopSourceContext1)); +#else + memmove(&memory->_context, context, sizeof(CFRunLoopSourceContext)); +#endif + if (context->retain) { + memory->_context.version0.info = (void *)context->retain(context->info); + } + return memory; +} + +CFIndex CFRunLoopSourceGetOrder(CFRunLoopSourceRef rls) { + __CFGenericValidateType(rls, __kCFRunLoopSourceTypeID); + return rls->_order; +} + +static void __CFRunLoopSourceRemoveFromRunLoop(const void *value, void *context) { + CFRunLoopRef rl = (CFRunLoopRef)value; + CFTypeRef *params = 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 = CFArrayGetValueAtIndex(array, idx); + CFRunLoopRemoveSource(rl, rls, modeName); + } + CFRunLoopRemoveSource(rl, rls, kCFRunLoopCommonModes); + CFRelease(array); + params[1] = rl; +} + +void CFRunLoopSourceInvalidate(CFRunLoopSourceRef rls) { + __CFGenericValidateType(rls, __kCFRunLoopSourceTypeID); + CFRetain(rls); + __CFRunLoopSourceLock(rls); + if (__CFIsValid(rls)) { + __CFUnsetValid(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) { + __CFGenericValidateType(rls, __kCFRunLoopSourceTypeID); + return __CFIsValid(rls); +} + +void CFRunLoopSourceGetContext(CFRunLoopSourceRef rls, CFRunLoopSourceContext *context) { + __CFGenericValidateType(rls, __kCFRunLoopSourceTypeID); +#if defined(__MACH__) + CFAssert1(0 == context->version || 1 == context->version, __kCFLogAssertion, "%s(): context version not initialized to 0 or 1", __PRETTY_FUNCTION__); + memmove(context, &rls->_context, (0 == context->version) ? sizeof(CFRunLoopSourceContext) : sizeof(CFRunLoopSourceContext1)); +#else + CFAssert1(0 == context->version, __kCFLogAssertion, "%s(): context version not initialized to 0", __PRETTY_FUNCTION__); + memmove(context, &rls->_context, sizeof(CFRunLoopSourceContext)); +#endif +} + +void CFRunLoopSourceSignal(CFRunLoopSourceRef rls) { + __CFRunLoopSourceLock(rls); + if (__CFIsValid(rls)) { + __CFRunLoopSourceSetSignaled(rls); + } + __CFRunLoopSourceUnlock(rls); +} + + +/* CFRunLoopObserver */ + +static CFStringRef __CFRunLoopObserverCopyDescription(CFTypeRef cf) { /* DOES CALLOUT */ + CFRunLoopObserverRef rlo = (CFRunLoopObserverRef)cf; + CFStringRef result; + CFStringRef contextDesc = NULL; + __CFRunLoopObserverLock(rlo); + if (NULL != rlo->_context.copyDescription) { + contextDesc = rlo->_context.copyDescription(rlo->_context.info); + } + if (!contextDesc) { + contextDesc = CFStringCreateWithFormat(CFGetAllocator(rlo), NULL, CFSTR(""), rlo->_context.info); + } + result = CFStringCreateWithFormat(CFGetAllocator(rlo), NULL, CFSTR("{locked = %s, valid = %s, activities = 0x%x, repeats = %s, order = %d, callout = %p, context = %@}"), cf, CFGetAllocator(rlo), rlo->_lock ? "Yes" : "No", __CFIsValid(rlo) ? "Yes" : "No", rlo->_activities, __CFRunLoopObserverRepeats(rlo) ? "Yes" : "No", rlo->_order, rlo->_callout, contextDesc); + __CFRunLoopObserverUnlock(rlo); + 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) { + 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); + } + memory->_lock = 0; + 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) { + __CFGenericValidateType(rlo, __kCFRunLoopObserverTypeID); + return rlo->_activities; +} + +CFIndex CFRunLoopObserverGetOrder(CFRunLoopObserverRef rlo) { + __CFGenericValidateType(rlo, __kCFRunLoopObserverTypeID); + return rlo->_order; +} + +Boolean CFRunLoopObserverDoesRepeat(CFRunLoopObserverRef rlo) { + __CFGenericValidateType(rlo, __kCFRunLoopObserverTypeID); + return __CFRunLoopObserverRepeats(rlo); +} + +void CFRunLoopObserverInvalidate(CFRunLoopObserverRef rlo) { /* DOES CALLOUT */ + __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 = 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) { + return __CFIsValid(rlo); +} + +void CFRunLoopObserverGetContext(CFRunLoopObserverRef rlo, CFRunLoopObserverContext *context) { + __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(); + __CFRunLoopTimerLock(rlt); + if (NULL != rlt->_context.copyDescription) { + contextDesc = rlt->_context.copyDescription(rlt->_context.info); + } + if (NULL == contextDesc) { + contextDesc = CFStringCreateWithFormat(CFGetAllocator(rlt), NULL, CFSTR(""), rlt->_context.info); + } + result = CFStringCreateWithFormat(CFGetAllocator(rlt), NULL, CFSTR("{locked = %s, valid = %s, interval = %0.09g, next fire date = %0.09g, order = %d, callout = %p, context = %@}"), cf, CFGetAllocator(rlt), rlt->_lock ? "Yes" : "No", __CFIsValid(rlt) ? "Yes" : "No", __CFTSRToTimeInterval(rlt->_intervalTSR), __CFTSRToAbsoluteTime(fireTime), rlt->_order, rlt->_callout, contextDesc); + __CFRunLoopTimerUnlock(rlt); + 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) { + 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); + memory->_lock = 0; + memory->_runLoop = NULL; + memory->_rlCount = 0; +#if defined(__MACH__) + memory->_port = MACH_PORT_NULL; +#endif + memory->_order = order; + if (fireDate < __CFTSRToAbsoluteTime(0)) { + memory->_fireTSR = 0; + } else if (__CFTSRToAbsoluteTime(LLONG_MAX) < fireDate) { + memory->_fireTSR = LLONG_MAX; + } else { + memory->_fireTSR = __CFAbsoluteTimeToTSR(fireDate); + } + 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) { + 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); + return __CFTSRToAbsoluteTime(result); +} + +void CFRunLoopTimerSetNextFireDate(CFRunLoopTimerRef rlt, CFAbsoluteTime fireDate) { + __CFRunLoopTimerFireTSRLock(); + if (fireDate < __CFTSRToAbsoluteTime(0)) { + rlt->_fireTSR = 0; + } else if (__CFTSRToAbsoluteTime(LLONG_MAX) < fireDate) { + rlt->_fireTSR = LLONG_MAX; + } else { + rlt->_fireTSR = __CFAbsoluteTimeToTSR(fireDate); + } + __CFRunLoopTimerFireTSRUnlock(); + if (rlt->_runLoop != NULL) { + __CFRunLoopTimerRescheduleWithAllModes(rlt, rlt->_runLoop); + } +} + +CFTimeInterval CFRunLoopTimerGetInterval(CFRunLoopTimerRef rlt) { + CF_OBJC_FUNCDISPATCH0(__kCFRunLoopTimerTypeID, CFTimeInterval, rlt, "timeInterval"); + __CFGenericValidateType(rlt, __kCFRunLoopTimerTypeID); + return __CFTSRToTimeInterval(rlt->_intervalTSR); +} + +Boolean CFRunLoopTimerDoesRepeat(CFRunLoopTimerRef rlt) { + __CFGenericValidateType(rlt, __kCFRunLoopTimerTypeID); + return (0 != rlt->_intervalTSR); +} + +CFIndex CFRunLoopTimerGetOrder(CFRunLoopTimerRef rlt) { + CF_OBJC_FUNCDISPATCH0(__kCFRunLoopTimerTypeID, CFIndex, rlt, "order"); + __CFGenericValidateType(rlt, __kCFRunLoopTimerTypeID); + return rlt->_order; +} + +void CFRunLoopTimerInvalidate(CFRunLoopTimerRef rlt) { /* DOES CALLOUT */ + 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 defined(__MACH__) + __CFRunLoopTimerPortMapLock(); + if (NULL != __CFRLTPortMap) { + CFDictionaryRemoveValue(__CFRLTPortMap, (void *)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 = 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) { + CF_OBJC_FUNCDISPATCH0(__kCFRunLoopTimerTypeID, Boolean, rlt, "isValid"); + __CFGenericValidateType(rlt, __kCFRunLoopTimerTypeID); + return __CFIsValid(rlt); +} + +void CFRunLoopTimerGetContext(CFRunLoopTimerRef rlt, CFRunLoopTimerContext *context) { + __CFGenericValidateType(rlt, __kCFRunLoopTimerTypeID); + CFAssert1(0 == context->version, __kCFLogAssertion, "%s(): context version not initialized to 0", __PRETTY_FUNCTION__); + *context = rlt->_context; +} + +struct rlpair { + CFRunLoopRef rl; // not retained + CFStringRef mode; // not retained +}; + +static Boolean __CFRLPKeyEqual(const void *value1, const void *value2) { + const struct rlpair *s1 = value1; + const struct rlpair *s2 = value2; + return (s1->rl == s2->rl) && CFEqual(s1->mode, s2->mode); +} + +static CFHashCode __CFRLPKeyHash(const void *value) { + const struct rlpair *s = value; + return (CFHashCode)s->rl + CFHash(s->mode); +} + +static CFSpinLock_t __CFRunLoopPerformLock = 0; +static CFMutableDictionaryRef __CFRunLoopPerformSources = NULL; + +struct performentry { + CFRunLoopPerformCallBack callout; + void *info; +}; + +struct performinfo { + CFSpinLock_t lock; + CFRunLoopSourceRef source; + CFRunLoopRef rl; + CFStringRef mode; + CFIndex count; + CFIndex size; + struct performentry *entries; +}; + +static void __CFRunLoopPerformCancel(void *info, CFRunLoopRef rl, CFStringRef mode) { + // we don't ever remove the source, so we know the run loop is dying + struct rlpair key, *pair; + struct performinfo *pinfo = info; + __CFSpinLock(&__CFRunLoopPerformLock); + key.rl = rl; + key.mode = mode; + if (CFDictionaryGetKeyIfPresent(__CFRunLoopPerformSources, &key, (const void **)&pair)) { + CFDictionaryRemoveValue(__CFRunLoopPerformSources, pair); + CFAllocatorDeallocate(kCFAllocatorSystemDefault, pair); + } + CFRunLoopSourceInvalidate(pinfo->source); + CFAllocatorDeallocate(kCFAllocatorSystemDefault, pinfo->entries); + CFAllocatorDeallocate(kCFAllocatorSystemDefault, pinfo); + // We can free pinfo here, even though the source isn't freed and still has + // a weak pointer to it, because the hash and equal callbacks of the source + // don't indirect into the info for their operations. + __CFSpinUnlock(&__CFRunLoopPerformLock); +} + +static void __CFRunLoopPerformPerform(void *info) { + struct performinfo *pinfo = info; + struct performentry *entries; + CFIndex idx, cnt; + __CFSpinLock(&(pinfo->lock)); + entries = pinfo->entries; + cnt = pinfo->count; + pinfo->entries = NULL; + pinfo->count = 0; + pinfo->size = 0; + __CFSpinUnlock(&(pinfo->lock)); + for (idx = 0; idx < cnt; idx++) { + entries[idx].callout(entries[idx].info); + } + // done with this list + CFAllocatorDeallocate(kCFAllocatorSystemDefault, entries); + // don't need to check to see if there's still something in the queue, + // and resignal here, since anything added during the callouts, + // from this or another thread, would have caused resignalling +} + +// retaining and freeing the info pointer and stuff inside is completely +// the caller's (and probably the callout's) responsibility +void _CFRunLoopPerformEnqueue(CFRunLoopRef rl, CFStringRef mode, CFRunLoopPerformCallBack callout, void *info) { + CFRunLoopSourceContext context = {0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; + CFRunLoopSourceRef source; + struct rlpair key; + struct performinfo *pinfo; + __CFSpinLock(&__CFRunLoopPerformLock); + if (!__CFRunLoopPerformSources) { + CFDictionaryKeyCallBacks kcb = {0, NULL, NULL, NULL, __CFRLPKeyEqual, __CFRLPKeyHash}; + __CFRunLoopPerformSources = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kcb, &kCFTypeDictionaryValueCallBacks); + } + key.rl = rl; + key.mode = mode; + if (!CFDictionaryGetValueIfPresent(__CFRunLoopPerformSources, &key, (const void **)&source)) { + struct rlpair *pair; + context.info = CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(struct performinfo), 0); + pinfo = context.info; + pinfo->lock = 0; + pinfo->rl = rl; + pinfo->mode = mode; + pinfo->count = 0; + pinfo->size = 0; + pinfo->entries = NULL; + context.cancel = __CFRunLoopPerformCancel; + context.perform = __CFRunLoopPerformPerform; + source = CFRunLoopSourceCreate(kCFAllocatorSystemDefault, 0, &context); + pair = CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(*pair), 0); + *pair = key; + CFDictionarySetValue(__CFRunLoopPerformSources, pair, source); + pinfo->source = source; + CFRunLoopAddSource(rl, source, mode); + } else { + CFRetain(source); + CFRunLoopSourceGetContext(source, &context); + pinfo = context.info; + } + __CFSpinLock(&(pinfo->lock)); + __CFSpinUnlock(&__CFRunLoopPerformLock); + if (pinfo->count == pinfo->size) { + pinfo->size = (0 == pinfo->size ? 3 : 2 * pinfo->size); + pinfo->entries = CFAllocatorReallocate(kCFAllocatorSystemDefault, pinfo->entries, pinfo->size * sizeof(struct performentry), 0); + } + pinfo->entries[pinfo->count].callout = callout; + pinfo->entries[pinfo->count].info = info; + pinfo->count++; + __CFSpinUnlock(&(pinfo->lock)); + CFRunLoopSourceSignal(source); + CFRunLoopWakeUp(rl); + CFRelease(source); +} + diff --git a/RunLoop.subproj/CFRunLoop.h b/RunLoop.subproj/CFRunLoop.h new file mode 100644 index 0000000..111d291 --- /dev/null +++ b/RunLoop.subproj/CFRunLoop.h @@ -0,0 +1,433 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFRunLoop.h + Copyright (c) 1998-2003, Apple, Inc. All rights reserved. +*/ + +/*! + @header CFRunLoop + CFRunLoops monitor sources of input to a task and dispatch control + when sources become ready for processing. Examples of input sources + might include user input devices, network connections, periodic + or time-delayed events, and asynchronous callbacks. Input sources + are registered with a run loop, and when a run loop is "run", + callback functions associated with each source are called when + the sources have some activity. + + There is one run loop per thread. Each run loop has different + sets of input sources, called modes, which are named with strings. + A run loop is run -- in a named mode -- to have it monitor the + sources that have been registered in that mode, and the run loop + blocks there until something happens. Examples of modes include + the default mode, which a process would normally spend most of + its time in, and a modal panel mode, which might be run when + a modal panel is up, to restrict the set of input sources that + are allowed to "fire". This is not to the granularity of, for + example, what type of user input events are interesting, however. + That sort of finer-grained granularity is given by UI-level + frameworks with "get next event matching mask" or similar + functionality. + + The CFRunLoopSource type is an abstraction of input sources that + can be put in a run loop. An input source type would normally + define an API for creating and operating on instances of the type, + as if it were a separate entity from the run loop, then provide a + function to create a CFRunLoopSource for an instance. The + CFRunLoopSource can then be registered with the run loop, + represents the input source to the run loop, and acts as + intermediary between the run loop and the actual input source + type instance. Examples include CFMachPort and CFSocket. + + A CFRunLoopTimer is a specialization of run loop sources, a way + to generate either a one-shot delayed action, or a recurrent + action. + + While being run, a run loop goes through a cycle of activities. + Input sources are checked, timers which need firing are fired, + and then the run loop blocks, waiting for something to happen + (or in the case of timers, waiting for it to be time for + something to happen). When something does happen, the run loop + wakes up, processes the activity (usually by calling a callback + function for an input source), checks other sources, fires timers, + and goes back to sleep. And so on. CFRunLoopObservers can be + used to do processing at special points in this cycle. + + + + +*/ + +#if !defined(__COREFOUNDATION_CFRUNLOOP__) +#define __COREFOUNDATION_CFRUNLOOP__ 1 + +#include +#include +#include +#include +#if defined(__MACH__) + #include +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +/*! + @typedef CFRunLoopRef + This is the type of a reference to a run loop. +*/ +typedef struct __CFRunLoop * CFRunLoopRef; + +/*! + @typedef CFRunLoopSourceRef + This is the type of a reference to general run loop input sources. +*/ +typedef struct __CFRunLoopSource * CFRunLoopSourceRef; + +/*! + @typedef CFRunLoopObserverRef + This is the type of a reference to a run loop observer. +*/ +typedef struct __CFRunLoopObserver * CFRunLoopObserverRef; + +/*! + @typedef CFRunLoopTimerRef + This is the type of a reference to a run loop timer. +*/ +typedef struct __CFRunLoopTimer * CFRunLoopTimerRef; + +/* Reasons for CFRunLoopRunInMode() to Return */ +enum { + kCFRunLoopRunFinished = 1, + kCFRunLoopRunStopped = 2, + kCFRunLoopRunTimedOut = 3, + kCFRunLoopRunHandledSource = 4 +}; + +/* Run Loop Observer Activities */ +typedef enum { + kCFRunLoopEntry = (1 << 0), + kCFRunLoopBeforeTimers = (1 << 1), + kCFRunLoopBeforeSources = (1 << 2), + kCFRunLoopBeforeWaiting = (1 << 5), + kCFRunLoopAfterWaiting = (1 << 6), + kCFRunLoopExit = (1 << 7), + kCFRunLoopAllActivities = 0x0FFFFFFFU +} CFRunLoopActivity; + +CF_EXPORT const CFStringRef kCFRunLoopDefaultMode; +CF_EXPORT const CFStringRef kCFRunLoopCommonModes; + +/*! + @function CFRunLoopGetTypeID + Returns the type identifier of all CFRunLoop instances. +*/ +CF_EXPORT CFTypeID CFRunLoopGetTypeID(void); + +/*! + @function CFRunLoopGetCurrent + Returns the run loop for the current thread. There is exactly + one run loop per thread. +*/ +CF_EXPORT CFRunLoopRef CFRunLoopGetCurrent(void); + +/*! + @function CFRunLoopCopyCurrentMode + Returns the name of the mode in which the run loop is running. + NULL is returned if the run loop is not running. + @param rl The run loop for which the current mode should be + reported. +*/ +CF_EXPORT CFStringRef CFRunLoopCopyCurrentMode(CFRunLoopRef rl); + +/*! + @function CFRunLoopCopyAllModes + Returns an array of all the names of the modes known to the run + loop. + @param rl The run loop for which the mode list should be returned. +*/ +CF_EXPORT CFArrayRef CFRunLoopCopyAllModes(CFRunLoopRef rl); + +/*! + @function CFRunLoopAddCommonMode + Makes the named mode a "common mode" for the run loop. The set of + common modes are collectively accessed with the global constant + kCFRunLoopCommonModes. Input sources previously added to the + common modes are added to the new common mode. + @param rl The run loop for which the mode should be made common. + @param mode The name of the mode to mark as a common mode. +*/ +CF_EXPORT void CFRunLoopAddCommonMode(CFRunLoopRef rl, CFStringRef mode); + +/*! + @function CFRunLoopGetNextTimerFireDate + Returns the time at which the next timer will fire. + @param rl The run loop for which the next timer fire date should + be reported. + @param mode The name of the mode to query. +*/ +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 CFRunLoopSourceContext + Structure containing the callbacks of a CFRunLoopSource. + @field version The version number of the structure type being + passed in as a parameter to the CFArray creation + functions. Valid version numbers are currently 0 and 1. + Version 0 sources are fairly generic, but may require a + bit more implementation, or may require a separate + thread as part of the implementation, for a complex + source. Version 1 sources are available on Mach, and + have performance advantages when the source type can + be described with this style. + @field info An arbitrary pointer to client-defined data, which + can be associated with the source at creation time, and + is passed to the callbacks. + @field retain The callback used to add a retain for the source on + the info pointer for the life of the source, and may be + used for temporary references the source needs to take. + This callback returns the actual info pointer to store in + the source, almost always just the pointer passed as the + parameter. + @field release The callback used to remove a retain previously + added for the source on the info pointer. + @field copyDescription The callback used to create a descriptive + string representation of the info pointer (or the data + pointed to by the info pointer) for debugging purposes. + This is used by the CFCopyDescription() function. + @field equal The callback used to compare the info pointers of + two sources, to determine equality of sources. + @field hash The callback used to compute a hash code for the info + pointer for the source. The source uses this hash code + information to produce its own hash code. + @field schedule For a version 0 source, this callback is called + whenever the source is added to a run loop mode. This + information is often needed to implement complex sources. + @field cancel For a version 0 source, this callback is called + whenever the source is removed from a run loop mode. This + information is often needed to implement complex sources. + @field getPort Defined in version 1 sources, this function returns + the Mach port to represent the source to the run loop. + This function must return the same Mach port every time it + is called, for the lifetime of the source, and should be + quick. + @field perform This callback is the workhorse of a run loop source. + It is called when the source needs to be "handled" or + processing is needed for input or conditions relating to + the source. For version 0 sources, this function is called + when the source has been marked "signaled" with the + CFRunLoopSourceSignal() function, and should do whatever + handling is required for the source. For a version 1 + source, this function is called when a Mach message arrives + on the source's Mach port, with the Mach message, its + length, an allocator, and the source's info pointer. A + version 1 source performs whatever processing is required + on the Mach message, then can return a pointer to a Mach + message (or NULL if none) to be sent (usually this is a + "reply" message), which should be allocated with the + allocator (and will be deallocated by the run loop after + sending). +*/ +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; + +#if defined(__MACH__) +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); + mach_port_t (*getPort)(void *info); + void * (*perform)(void *msg, CFIndex size, CFAllocatorRef allocator, void *info); +} CFRunLoopSourceContext1; +#endif + +/*! + @function CFRunLoopSourceGetTypeID + Returns the type identifier of all CFRunLoopSource instances. +*/ +CF_EXPORT CFTypeID CFRunLoopSourceGetTypeID(void); + +/*! + @function CFRunLoopSourceCreate + Creates a new run loop source with the given context. + @param allocator The CFAllocator which should be used to allocate + memory for the array and its storage for values. If this + reference is not a valid CFAllocator, the behavior is + undefined. + @param order On platforms which support it, for source versions + which support it, this parameter determines the order in + which the sources which are ready to be processed are + handled. A lower order number causes processing before + higher order number sources. It is inadvisable to depend + on the order number for any architectural or design aspect + of code. In the absence of any reason to do otherwise, + zero should be used. + @param context A pointer to the context structure for the source. +*/ +CF_EXPORT CFRunLoopSourceRef CFRunLoopSourceCreate(CFAllocatorRef allocator, CFIndex order, CFRunLoopSourceContext *context); + +/*! + @function CFRunLoopSourceGetOrder + Returns the ordering parameter of the run loop source. + @param source The run loop source for which the order number + should be returned. +*/ +CF_EXPORT CFIndex CFRunLoopSourceGetOrder(CFRunLoopSourceRef source); + +/*! + @function CFRunLoopSourceInvalidate + Invalidates the run loop source. The run loop source is never + performed again after it becomes invalid, and will automatically + be removed from any run loops and modes which contain it. The + source is not destroyed by this operation, however -- the memory + is still valid; only the release of all references on the source + through the reference counting system can do that. But note, that + if the only retains on the source were held by run loops, those + retains may all be released by the time this function returns, + and the source may actually be destroyed through that process. + @param source The run loop source which should be invalidated. +*/ +CF_EXPORT void CFRunLoopSourceInvalidate(CFRunLoopSourceRef source); + +/*! + @function CFRunLoopSourceIsValid + Reports whether or not the source is valid. + @param source The run loop source for which the validity should + be returned. +*/ +CF_EXPORT Boolean CFRunLoopSourceIsValid(CFRunLoopSourceRef source); + +/*! + @function CFRunLoopSourceGetContext + Fills the memory pointed to by the context parameter with the + context structure of the source. + @param source The run loop source for which the context structure + should be returned. + @param context A pointer to a context structure to be filled. +*/ +CF_EXPORT void CFRunLoopSourceGetContext(CFRunLoopSourceRef source, CFRunLoopSourceContext *context); + +/*! + @function CFRunLoopSourceSignal + Marks the source as signalled, ready for handling by the run loop. + Has no effect on version 1 sources, which are automatically + handled when Mach messages for them come in. + @param source The run loop source which should be signalled. +*/ +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); + +/*! + @function CFRunLoopObserverGetTypeID + Returns the type identifier of all CFRunLoopObserver instances. +*/ +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); + +/*! + @function CFRunLoopTimerGetTypeID + Returns the type identifier of all CFRunLoopTimer instances. +*/ +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); + +#if defined(__cplusplus) +} +#endif + +#endif /* ! __COREFOUNDATION_CFRUNLOOP__ */ + diff --git a/RunLoop.subproj/CFSocket.c b/RunLoop.subproj/CFSocket.c new file mode 100644 index 0000000..45300be --- /dev/null +++ b/RunLoop.subproj/CFSocket.c @@ -0,0 +1,1615 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFSocket.c + Copyright 1999-2002, Apple, Inc. All rights reserved. + Responsibility: Doug Davidson +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "CFInternal.h" +#if defined(__WIN32__) +#include +#define EINPROGRESS 36 +//#include +#elif defined(__MACH__) +#include +#else +#include +#include +#include +#include +#include +#endif + +//#define LOG_CFSOCKET + +#if !defined(__WIN32__) +#define INVALID_SOCKET (CFSocketNativeHandle)(-1) +#endif /* __WIN32__ */ + +#define MAX_SOCKADDR_LEN 256 +#define MAX_DATA_SIZE 32768 + +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") + +/* locks are to be acquired in the following order: + (1) __CFAllSocketsLock + (2) an individual CFSocket's lock + (3) __CFActiveSocketsLock +*/ +static CFSpinLock_t __CFAllSocketsLock = 0; /* controls __CFAllSockets */ +static CFMutableDictionaryRef __CFAllSockets = NULL; +static CFSpinLock_t __CFActiveSocketsLock = 0; /* controls __CFRead/WriteSockets and __CFRead/WriteSocketsFds */ +static CFMutableArrayRef __CFWriteSockets = NULL; +static CFMutableArrayRef __CFReadSockets = NULL; +static CFMutableDataRef __CFWriteSocketsFds = NULL; +static CFMutableDataRef __CFReadSocketsFds = NULL; + +static CFSocketNativeHandle __CFWakeupSocketPair[2] = {0, 0}; +static void *__CFSocketManagerThread = NULL; + +#if defined(__WIN32__) +static Boolean __CFSocketWinSockInitialized = false; +#else +#define CFSOCKET_USE_SOCKETPAIR +#define closesocket(a) close((a)) +#define ioctlsocket(a,b,c) ioctl((a),(b),(c)) +#endif + +static CFTypeID __kCFSocketTypeID = _kCFRuntimeNotATypeID; + +struct __CFSocket { + CFRuntimeBase _base; + uint32_t _flags; + CFSpinLock_t _lock; + CFSpinLock_t _writeLock; + CFSocketNativeHandle _socket; /* immutable */ + SInt32 _socketType; + SInt32 _errorCode; + CFDataRef _address; + CFDataRef _peerAddress; + SInt32 _socketSetCount; + CFRunLoopSourceRef _source; + CFMutableArrayRef _runLoops; + CFSocketCallBack _callout; /* immutable */ + CFSocketContext _context; /* immutable */ + CFIndex _maxQueueLen; + CFMutableArrayRef _dataQueue; + CFMutableArrayRef _addressQueue; +}; + +/* 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)->_info, 6, 6); +} + +CF_INLINE void __CFSocketSetWriteSignalled(CFSocketRef s) { + __CFBitfieldSetValue(((CFRuntimeBase *)s)->_info, 6, 6, 1); +} + +CF_INLINE void __CFSocketUnsetWriteSignalled(CFSocketRef s) { + __CFBitfieldSetValue(((CFRuntimeBase *)s)->_info, 6, 6, 0); +} + +CF_INLINE Boolean __CFSocketIsReadSignalled(CFSocketRef s) { + return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)s)->_info, 5, 5); +} + +CF_INLINE void __CFSocketSetReadSignalled(CFSocketRef s) { + __CFBitfieldSetValue(((CFRuntimeBase *)s)->_info, 5, 5, 1); +} + +CF_INLINE void __CFSocketUnsetReadSignalled(CFSocketRef s) { + __CFBitfieldSetValue(((CFRuntimeBase *)s)->_info, 5, 5, 0); +} + +CF_INLINE Boolean __CFSocketIsValid(CFSocketRef s) { + return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)s)->_info, 4, 4); +} + +CF_INLINE void __CFSocketSetValid(CFSocketRef s) { + __CFBitfieldSetValue(((CFRuntimeBase *)s)->_info, 4, 4, 1); +} + +CF_INLINE void __CFSocketUnsetValid(CFSocketRef s) { + __CFBitfieldSetValue(((CFRuntimeBase *)s)->_info, 4, 4, 0); +} + +CF_INLINE uint8_t __CFSocketCallBackTypes(CFSocketRef s) { + return (uint8_t)__CFBitfieldGetValue(((const CFRuntimeBase *)s)->_info, 3, 0); +} + +CF_INLINE uint8_t __CFSocketReadCallBackType(CFSocketRef s) { + return (uint8_t)__CFBitfieldGetValue(((const CFRuntimeBase *)s)->_info, 1, 0); +} + +CF_INLINE void __CFSocketSetCallBackTypes(CFSocketRef s, uint8_t types) { + __CFBitfieldSetValue(((CFRuntimeBase *)s)->_info, 3, 0, types & 0xF); +} + +CF_INLINE void __CFSocketLock(CFSocketRef s) { + __CFSpinLock(&(s->_lock)); +} + +CF_INLINE void __CFSocketUnlock(CFSocketRef s) { + __CFSpinUnlock(&(s->_lock)); +} + +CF_INLINE void __CFSocketWriteLock(CFSocketRef s) { + __CFSpinLock(&(s->_writeLock)); +} + +CF_INLINE void __CFSocketWriteUnlock(CFSocketRef s) { + __CFSpinUnlock(&(s->_writeLock)); +} + +CF_INLINE Boolean __CFSocketIsConnectionOriented(CFSocketRef s) { + return (SOCK_STREAM == s->_socketType || SOCK_SEQPACKET == s->_socketType); +} + +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, &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, &namelen) && NULL != name && 0 < namelen) { + s->_peerAddress = CFDataCreate(CFGetAllocator(s), name, namelen); + } +} + +CF_INLINE CFIndex __CFSocketFdGetSize(CFDataRef fdSet) { +#if defined(__WIN32__) + fd_set* 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 defined(__WIN32__) + 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; +} + +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 defined(__WIN32__) + 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 defined(CFSOCKET_USE_SOCKETPAIR) + 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] = 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 +} + +static void __CFSocketInitializeSockets(void) { + UInt32 yes = 1; +#if defined(__WIN32__) + if (!__CFSocketWinSockInitialized) { + WORD versionRequested = MAKEWORD(1, 1); + 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!!!")); + } + __CFSocketWinSockInitialized = true; + } +#endif + __CFWriteSockets = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, NULL); + __CFReadSockets = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, NULL); + __CFWriteSocketsFds = CFDataCreateMutable(kCFAllocatorSystemDefault, 0); + __CFReadSocketsFds = CFDataCreateMutable(kCFAllocatorSystemDefault, 0); +#if defined(__WIN32__) + CFDataIncreaseLength(__CFWriteSocketsFds, sizeof(u_int) + sizeof(SOCKET)); + CFDataIncreaseLength(__CFReadSocketsFds, sizeof(u_int) + sizeof(SOCKET)); +#endif + if (0 > __CFSocketCreateWakeupSocketPair()) { + CFLog(0, CFSTR("*** Could not create wakeup socket pair for CFSocket!!!")); + } else { + /* 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 */ + for (idx = 0; idx < cnt; idx++) { + CFRunLoopRef value = (CFRunLoopRef)CFArrayGetValueAtIndex(s->_runLoops, idx); + CFStringRef currentMode = CFRunLoopCopyCurrentMode(value); + if (NULL != currentMode && CFRunLoopIsWaiting(value) && CFRunLoopContainsSource(value, s->_source, currentMode)) { + CFRelease(currentMode); + /* 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 move this run loop to the end of the list to scramble them + * a bit, and always search from the front */ + CFArrayRemoveValueAtIndex(s->_runLoops, idx); + rl = value; + break; + } + if (NULL != currentMode) CFRelease(currentMode); + } + if (NULL == rl) { /* didn't choose one above, so choose first */ + rl = (CFRunLoopRef)CFArrayGetValueAtIndex(s->_runLoops, 0); + CFArrayRemoveValueAtIndex(s->_runLoops, 0); + } + CFArrayAppendValue(s->_runLoops, rl); + } + } + if (NULL != rl) CFRetain(rl); + return rl; +} + +static void __CFSocketHandleWrite(CFSocketRef s) { + SInt32 errorCode = 0; + int errorSize = sizeof(errorCode); + CFRunLoopRef rl = NULL; + CFOptionFlags writeCallBacksAvailable; + + if (!CFSocketIsValid(s)) return; + if (0 != getsockopt(s->_socket, SOL_SOCKET, SO_ERROR, &errorCode, &errorSize)) errorCode = 0; +#if defined(LOG_CFSOCKET) + if (errorCode) printf("error %d on socket %d\n", errorCode, s->_socket); +#endif + __CFSocketLock(s); + writeCallBacksAvailable = __CFSocketCallBackTypes(s) & (kCFSocketWriteCallBack | kCFSocketConnectCallBack); + if ((s->_flags & kCFSocketConnectCallBack) != 0) writeCallBacksAvailable &= ~kCFSocketConnectCallBack; + if (!__CFSocketIsValid(s) || (((s->_flags >> 24) & writeCallBacksAvailable) == writeCallBacksAvailable)) { + __CFSocketUnlock(s); + return; + } + s->_errorCode = errorCode; + __CFSocketSetWriteSignalled(s); + CFRunLoopSourceSignal(s->_source); +#if defined(LOG_CFSOCKET) + printf("write signaling source for socket %d\n", s->_socket); +#endif + rl = __CFSocketCopyRunLoopToWakeUp(s); + __CFSocketUnlock(s); + if (NULL != rl) { + CFRunLoopWakeUp(rl); + CFRelease(rl); + } +} + +static void __CFSocketHandleRead(CFSocketRef s) { + static CFDataRef zeroLengthData = NULL; + CFRunLoopRef rl = NULL; + if (NULL == zeroLengthData) zeroLengthData = CFDataCreateMutable(kCFAllocatorSystemDefault, 0); + if (!CFSocketIsValid(s)) return; + if (__CFSocketReadCallBackType(s) == kCFSocketDataCallBack) { + uint8_t buffer[MAX_DATA_SIZE]; +#if !defined(__WIN32__) + uint8_t name[MAX_SOCKADDR_LEN]; + int namelen = sizeof(name); +#else + struct sockaddr* name = NULL; + int namelen = 0; +#endif + SInt32 recvlen = recvfrom(s->_socket, buffer, sizeof(buffer), 0, (struct sockaddr *)name, &namelen); + CFDataRef data, address = NULL; +#if defined(LOG_CFSOCKET) + printf("read %d 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 = CFRetain(zeroLengthData); + } else { + data = CFDataCreate(CFGetAllocator(s), buffer, recvlen); + } + __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 = CFRetain(s->_peerAddress); + } + if (NULL == address) { + address = 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 && 0 < s->_socketSetCount && (s->_flags & kCFSocketDataCallBack) != 0 && ((s->_flags >> 24) & kCFSocketDataCallBack) == 0 && (0 == s->_maxQueueLen || CFArrayGetCount(s->_dataQueue) < s->_maxQueueLen)) { + __CFSpinLock(&__CFActiveSocketsLock); + /* restore socket to fds */ + __CFSocketFdSet(s->_socket, __CFReadSocketsFds); + __CFSpinUnlock(&__CFActiveSocketsLock); + } + } else if (__CFSocketReadCallBackType(s) == kCFSocketAcceptCallBack) { + uint8_t name[MAX_SOCKADDR_LEN]; + int namelen = sizeof(name); + CFSocketNativeHandle sock = accept(s->_socket, (struct sockaddr *)name, &namelen); + CFDataRef address; + if (INVALID_SOCKET == sock) { + //??? should return error + return; + } + if (NULL != name && 0 < namelen) { + address = CFDataCreate(CFGetAllocator(s), name, namelen); + } else { + address = 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 *)sock); + CFArrayAppendValue(s->_addressQueue, address); + CFRelease(address); + if (0 < s->_socketSetCount && (s->_flags & kCFSocketAcceptCallBack) != 0 && ((s->_flags >> 24) & kCFSocketAcceptCallBack) == 0 && (0 == s->_maxQueueLen || CFArrayGetCount(s->_dataQueue) < s->_maxQueueLen)) { + __CFSpinLock(&__CFActiveSocketsLock); + /* restore socket to fds */ + __CFSocketFdSet(s->_socket, __CFReadSocketsFds); + __CFSpinUnlock(&__CFActiveSocketsLock); + } + } else { + __CFSocketLock(s); + if (!__CFSocketIsValid(s) || ((s->_flags >> 24) & kCFSocketReadCallBack) != 0) { + __CFSocketUnlock(s); + return; + } + __CFSocketSetReadSignalled(s); + } + CFRunLoopSourceSignal(s->_source); +#if defined(LOG_CFSOCKET) + printf("read signaling source for socket %d\n", s->_socket); +#endif + rl = __CFSocketCopyRunLoopToWakeUp(s); + __CFSocketUnlock(s); + if (NULL != rl) { + CFRunLoopWakeUp(rl); + CFRelease(rl); + } +} + +static void * __CFSocketManager(void * arg) { + SInt32 nrfds, maxnrfds, fdentries = 1; +#if defined(__WIN32__) + 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); +#else + fd_set *writefds = (fd_set *)CFAllocatorAllocate(kCFAllocatorSystemDefault, fdentries * sizeof(fd_mask), 0); + fd_set *readfds = (fd_set *)CFAllocatorAllocate(kCFAllocatorSystemDefault, fdentries * sizeof(fd_mask), 0); +#endif + fd_set *tempfds; + SInt32 idx, cnt; + uint8_t buffer[256]; + CFMutableArrayRef selectedWriteSockets = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks); + CFMutableArrayRef selectedReadSockets = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks); + + for (;;) { + __CFSpinLock(&__CFActiveSocketsLock); +#if defined(LOG_CFSOCKET) + printf("socket manager looking at read sockets "); + tempfds = (fd_set *)CFDataGetBytePtr(__CFReadSocketsFds); + for (idx = 0, cnt = CFArrayGetCount(__CFReadSockets); idx < cnt; idx++) { + CFSocketRef s = (CFSocketRef)CFArrayGetValueAtIndex(__CFReadSockets, idx); + if (FD_ISSET(s->_socket, tempfds)) { + printf("%d ", s->_socket); + } else { + printf("(%d) ", s->_socket); + } + } + if (0 < CFArrayGetCount(__CFWriteSockets)) printf(" and write sockets "); + tempfds = (fd_set *)CFDataGetBytePtr(__CFWriteSocketsFds); + for (idx = 0, cnt = CFArrayGetCount(__CFWriteSockets); idx < cnt; idx++) { + CFSocketRef s = (CFSocketRef)CFArrayGetValueAtIndex(__CFWriteSockets, idx); + if (FD_ISSET(s->_socket, tempfds)) { + printf("%d ", s->_socket); + } else { + printf("(%d) ", s->_socket); + } + } + printf("\n"); +#endif + maxnrfds = MAX(__CFSocketFdGetSize(__CFWriteSocketsFds), __CFSocketFdGetSize(__CFReadSocketsFds)); +#if defined(__WIN32__) + if (maxnrfds > fdentries) { + fdentries = maxnrfds; + 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(writefds, 0, fdentries * sizeof(SOCKET) + sizeof(u_int)); + memset(readfds, 0, fdentries * sizeof(SOCKET) + sizeof(u_int)); +#else + 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)); +#endif + CFDataGetBytes(__CFWriteSocketsFds, CFRangeMake(0, CFDataGetLength(__CFWriteSocketsFds)), (UInt8 *)writefds); + CFDataGetBytes(__CFReadSocketsFds, CFRangeMake(0, CFDataGetLength(__CFReadSocketsFds)), (UInt8 *)readfds); + __CFSpinUnlock(&__CFActiveSocketsLock); + + nrfds = select(maxnrfds, readfds, writefds, NULL, NULL); + if (0 == nrfds) continue; + if (0 > nrfds) { + SInt32 selectError = thread_errno(); +#if defined(LOG_CFSOCKET) + printf("socket manager received error %d 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); + SInt32 flags = fcntl(s->_socket, F_GETFL, 0); + if (0 > flags && EBADF == thread_errno()) { +#if defined(LOG_CFSOCKET) + printf("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); + SInt32 flags = fcntl(s->_socket, F_GETFL, 0); + if (0 > flags && EBADF == thread_errno()) { +#if defined(LOG_CFSOCKET) + printf("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], buffer, sizeof(buffer), 0); +#if defined(LOG_CFSOCKET) + printf("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; + if (INVALID_SOCKET != sock && 0 <= sock && sock < maxnrfds && FD_ISSET(sock, writefds)) { + CFArrayAppendValue(selectedWriteSockets, s); + /* socket is removed from fds here, restored by CFSocketReschedule */ + if (!tempfds) tempfds = (fd_set *)CFDataGetMutableBytePtr(__CFWriteSocketsFds); + FD_CLR(sock, tempfds); + } + } + tempfds = NULL; + cnt = CFArrayGetCount(__CFReadSockets); + for (idx = 0; idx < cnt; idx++) { + CFSocketRef s = (CFSocketRef)CFArrayGetValueAtIndex(__CFReadSockets, idx); + CFSocketNativeHandle sock = s->_socket; + if (INVALID_SOCKET != sock && 0 <= sock && sock < maxnrfds && FD_ISSET(sock, readfds)) { + CFArrayAppendValue(selectedReadSockets, s); + /* 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); + + cnt = CFArrayGetCount(selectedWriteSockets); + for (idx = 0; idx < cnt; idx++) { + CFSocketRef s = (CFSocketRef)CFArrayGetValueAtIndex(selectedWriteSockets, idx); +#if defined(LOG_CFSOCKET) + printf("socket manager signaling socket %d for write\n", s->_socket); +#endif + __CFSocketHandleWrite(s); + } + if (0 < cnt) CFArrayRemoveAllValues(selectedWriteSockets); + cnt = CFArrayGetCount(selectedReadSockets); + for (idx = 0; idx < cnt; idx++) { + CFSocketRef s = (CFSocketRef)CFArrayGetValueAtIndex(selectedReadSockets, idx); +#if defined(LOG_CFSOCKET) + printf("socket manager signaling socket %d for read\n", s->_socket); +#endif + __CFSocketHandleRead(s); + } + if (0 < cnt) CFArrayRemoveAllValues(selectedReadSockets); + } + return (void *)0; +} + +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); + CFStringAppendFormat(result, NULL, CFSTR("{valid = %s, type = %d, socket = %d, socket set count = %d\n callback types = 0x%x, callout = %x, source = %p,\n run loops = %@,\n context = "), cf, CFGetAllocator(s), (__CFSocketIsValid(s) ? "Yes" : "No"), s->_socketType, s->_socket, s->_socketSetCount, __CFSocketCallBackTypes(s), s->_callout, s->_source, 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(""), contextInfo); + } + CFStringAppend(result, contextDesc); + 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; + } +} + +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; +} + +CFSocketError CFSocketSetAddress(CFSocketRef s, CFDataRef address) { + const uint8_t *name; + SInt32 namelen, result = 0; + __CFGenericValidateType(s, __kCFSocketTypeID); + if (NULL == address) return -1; + name = CFDataGetBytePtr(address); + namelen = CFDataGetLength(address); + __CFSocketLock(s); + if (__CFSocketIsValid(s) && INVALID_SOCKET != s->_socket && NULL != name && 0 < namelen) { + result = bind(s->_socket, (struct sockaddr *)name, namelen); + if (0 == result) { + __CFSocketEstablishAddress(s); + listen(s->_socket, 256); + } + } + if (NULL == s->_address && NULL != name && 0 < namelen && 0 == result) { + s->_address = CFDataCreateCopy(CFGetAllocator(s), address); + } + __CFSocketUnlock(s); + //??? should return errno + return result; +} + +__private_extern__ void CFSocketSetAcceptBacklog(CFSocketRef s, CFIndex limit) { + __CFGenericValidateType(s, __kCFSocketTypeID); + __CFSocketLock(s); + if (__CFSocketIsValid(s) && INVALID_SOCKET != s->_socket) { + listen(s->_socket, limit); + } + __CFSocketUnlock(s); +} + +__private_extern__ void CFSocketSetMaximumQueueLength(CFSocketRef s, CFIndex length) { + __CFGenericValidateType(s, __kCFSocketTypeID); + __CFSocketLock(s); + if (__CFSocketIsValid(s)) { + s->_maxQueueLen = length; + } + __CFSocketUnlock(s); +} + +CFSocketError CFSocketConnectToAddress(CFSocketRef s, CFDataRef address, CFTimeInterval timeout) { + //??? need error handling, retries + const uint8_t *name; + SInt32 namelen, result = 0, connect_err = 0, flags; + UInt32 yes = 1, no = 0; + Boolean wasBlocking = true; +#if !defined(__WIN32__) + __CFGenericValidateType(s, __kCFSocketTypeID); + name = CFDataGetBytePtr(address); + namelen = CFDataGetLength(address); + __CFSocketLock(s); + if (__CFSocketIsValid(s) && INVALID_SOCKET != s->_socket && NULL != name && 0 < namelen) { +#if !defined(__WIN32__) + flags = fcntl(s->_socket, F_GETFL, 0); + if (flags >= 0) wasBlocking = ((flags & O_NONBLOCK) == 0); +#endif + if (wasBlocking && (timeout > 0.0 || timeout < 0.0)) ioctlsocket(s->_socket, FIONBIO, &yes); + result = connect(s->_socket, (struct sockaddr *)name, namelen); +#if defined(__WIN32__) + if (result != 0 && WSAGetLastError() == WSAEWOULDBLOCK) connect_err = EINPROGRESS; +#else + if (result != 0) connect_err = thread_errno(); +#endif +#if defined(LOG_CFSOCKET) + printf("connection attempt returns %d error %d on socket %d (flags 0x%x blocking %d)\n", result, connect_err, s->_socket, flags, wasBlocking); +#endif + if (EINPROGRESS == connect_err && timeout >= 0.0) { + /* select on socket */ + CFMutableDataRef fds = CFDataCreateMutable(kCFAllocatorSystemDefault, 0); + SInt32 nrfds; + struct timeval tv; + __CFSocketFdSet(s->_socket, 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); + result = (nrfds > 0) ? 0 : -1; + CFRelease(fds); +#if defined(LOG_CFSOCKET) + printf("timed connection attempt %s on socket %d\n", (result == 0) ? "succeeds" : "fails", s->_socket); +#endif + } + if (wasBlocking && (timeout > 0.0 || timeout < 0.0)) ioctlsocket(s->_socket, FIONBIO, &no); + if (0 == result) { + __CFSocketEstablishPeerAddress(s); + if (NULL == s->_peerAddress && NULL != name && 0 < namelen && __CFSocketIsConnectionOriented(s)) { + s->_peerAddress = CFDataCreateCopy(CFGetAllocator(s), address); + } + } + if (EINPROGRESS == connect_err && timeout < 0.0) { + result = 0; +#if defined(LOG_CFSOCKET) + printf("connection attempt continues in background on socket %d\n", s->_socket); +#endif + } + } + __CFSocketUnlock(s); + //??? should return errno +#endif + return result; +} + +CFSocketRef CFSocketCreate(CFAllocatorRef allocator, SInt32 protocolFamily, SInt32 socketType, SInt32 protocol, CFOptionFlags callBackTypes, CFSocketCallBack callout, const CFSocketContext *context) { + 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 !defined(__WIN32__) + if (PF_LOCAL == protocolFamily && 0 >= socketType) socketType = SOCK_STREAM; +#endif + /* Needs to be initialized at this point for Windows, before socket() */ + if (NULL == __CFReadSockets) __CFSocketInitializeSockets(); + sock = socket(protocolFamily, socketType, protocol); + if (INVALID_SOCKET != sock) { + s = CFSocketCreateWithNative(allocator, sock, callBackTypes, callout, context); + } + return s; +} + +CFSocketRef CFSocketCreateWithNative(CFAllocatorRef allocator, CFSocketNativeHandle sock, CFOptionFlags callBackTypes, CFSocketCallBack callout, const CFSocketContext *context) { + 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 *)sock, (const void **)&memory)) { + __CFSpinUnlock(&__CFAllSocketsLock); + CFRetain(memory); + return memory; + } + 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->_flags = ((callBackTypes & (~kCFSocketConnectCallBack)) & (~kCFSocketWriteCallBack)) | kCFSocketCloseOnInvalidate; + memory->_lock = 0; + memory->_writeLock = 0; + memory->_socket = sock; + if (INVALID_SOCKET == sock || 0 != getsockopt(sock, SOL_SOCKET, SO_TYPE, &(memory->_socketType), &typeSize)) memory->_socketType = 0; + memory->_errorCode = 0; + memory->_address = NULL; + memory->_peerAddress = NULL; + memory->_socketSetCount = 0; + memory->_source = 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->_maxQueueLen = 0; + memory->_context.info = 0; + memory->_context.retain = 0; + memory->_context.release = 0; + memory->_context.copyDescription = 0; + if (INVALID_SOCKET != sock) CFDictionaryAddValue(__CFAllSockets, (void *)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 CFSocketCreateWithSocketSignature(CFAllocatorRef allocator, const CFSocketSignature *signature, CFOptionFlags callBackTypes, CFSocketCallBack callout, const CFSocketContext *context) { + 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) { + 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; +} + +void CFSocketInvalidate(CFSocketRef s) { + __CFGenericValidateType(s, __kCFSocketTypeID); + CFRetain(s); + __CFSpinLock(&__CFAllSocketsLock); + __CFSocketLock(s); + if (__CFSocketIsValid(s)) { + SInt32 idx; + CFRunLoopSourceRef source; + void *contextInfo = NULL; + void (*contextRelease)(const void *info) = NULL; + __CFSocketUnsetValid(s); + __CFSocketUnsetWriteSignalled(s); + __CFSocketUnsetReadSignalled(s); + __CFSpinLock(&__CFActiveSocketsLock); + if (NULL == __CFReadSockets) __CFSocketInitializeSockets(); + idx = CFArrayGetFirstIndexOfValue(__CFWriteSockets, CFRangeMake(0, CFArrayGetCount(__CFWriteSockets)), s); + if (0 <= idx) { + CFArrayRemoveValueAtIndex(__CFWriteSockets, idx); + __CFSocketFdClr(s->_socket, __CFWriteSocketsFds); + } + idx = CFArrayGetFirstIndexOfValue(__CFReadSockets, CFRangeMake(0, CFArrayGetCount(__CFReadSockets)), s); + if (0 <= idx) { + CFArrayRemoveValueAtIndex(__CFReadSockets, idx); + __CFSocketFdClr(s->_socket, __CFReadSocketsFds); + } + __CFSpinUnlock(&__CFActiveSocketsLock); + CFDictionaryRemoveValue(__CFAllSockets, (void *)(s->_socket)); + if ((s->_flags & 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; + source = s->_source; + s->_source = 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 != source) { + CFRunLoopSourceInvalidate(source); + CFRelease(source); + } + } else { + __CFSocketUnlock(s); + } + __CFSpinUnlock(&__CFAllSocketsLock); + CFRelease(s); +} + +Boolean CFSocketIsValid(CFSocketRef s) { + __CFGenericValidateType(s, __kCFSocketTypeID); + return __CFSocketIsValid(s); +} + +CFSocketNativeHandle CFSocketGetNative(CFSocketRef s) { + __CFGenericValidateType(s, __kCFSocketTypeID); + return s->_socket; +} + +CFDataRef CFSocketCopyAddress(CFSocketRef s) { + CFDataRef result = NULL; + __CFGenericValidateType(s, __kCFSocketTypeID); + __CFSocketLock(s); + __CFSocketEstablishAddress(s); + if (NULL != s->_address) { + result = CFRetain(s->_address); + } + __CFSocketUnlock(s); + return result; +} + +CFDataRef CFSocketCopyPeerAddress(CFSocketRef s) { + CFDataRef result = NULL; + __CFGenericValidateType(s, __kCFSocketTypeID); + __CFSocketLock(s); + __CFSocketEstablishPeerAddress(s); + if (NULL != s->_peerAddress) { + result = CFRetain(s->_peerAddress); + } + __CFSocketUnlock(s); + return result; +} + +void CFSocketGetContext(CFSocketRef s, CFSocketContext *context) { + __CFGenericValidateType(s, __kCFSocketTypeID); + CFAssert1(0 == context->version, __kCFLogAssertion, "%s(): context version not initialized to 0", __PRETTY_FUNCTION__); + *context = s->_context; +} + +__private_extern__ void CFSocketReschedule(CFSocketRef s) { + CFSocketEnableCallBacks(s, __CFSocketCallBackTypes(s)); +} + +CFOptionFlags CFSocketGetSocketFlags(CFSocketRef s) { + __CFGenericValidateType(s, __kCFSocketTypeID); + return (s->_flags & (~(kCFSocketConnectCallBack | (0xff << 24)))); +} + +void CFSocketSetSocketFlags(CFSocketRef s, CFOptionFlags flags) { + __CFGenericValidateType(s, __kCFSocketTypeID); + __CFSocketLock(s); + flags &= ~(kCFSocketConnectCallBack | (0xff << 24)); +#if defined(LOG_CFSOCKET) + printf("setting flags for socket %d with %x, flags going from %x to %x\n", s->_socket, flags, s->_flags, (s->_flags & (kCFSocketConnectCallBack | (0xff << 24))) | flags); +#endif + s->_flags &= (kCFSocketConnectCallBack | (0xff << 24)); + s->_flags |= flags; + __CFSocketUnlock(s); +} + +void CFSocketDisableCallBacks(CFSocketRef s, CFOptionFlags callBackTypes) { + Boolean wakeup = false; + uint8_t c = 'u', readCallBackType; + __CFGenericValidateType(s, __kCFSocketTypeID); + __CFSocketLock(s); + if (__CFSocketIsValid(s) && 0 < s->_socketSetCount) { + callBackTypes &= __CFSocketCallBackTypes(s); + readCallBackType = __CFSocketReadCallBackType(s); + s->_flags |= (callBackTypes << 24); +#if defined(LOG_CFSOCKET) + printf("unscheduling socket %d with flags 0x%x for types 0x%x\n", s->_socket, s->_flags, callBackTypes); +#endif + __CFSpinLock(&__CFActiveSocketsLock); + if (NULL == __CFReadSockets) __CFSocketInitializeSockets(); + if ((readCallBackType == kCFSocketAcceptCallBack) || !__CFSocketIsConnectionOriented(s)) s->_flags |= kCFSocketConnectCallBack; + if (((callBackTypes & kCFSocketWriteCallBack) != 0) || (((callBackTypes & kCFSocketConnectCallBack) != 0) && ((s->_flags & kCFSocketConnectCallBack) == 0))) { + if (__CFSocketFdClr(s->_socket, __CFWriteSocketsFds)) { + // do not wake up the socket manager thread if all relevant write callbacks are disabled + CFOptionFlags writeCallBacksAvailable = __CFSocketCallBackTypes(s) & (kCFSocketWriteCallBack | kCFSocketConnectCallBack); + if ((s->_flags & kCFSocketConnectCallBack) != 0) writeCallBacksAvailable &= ~kCFSocketConnectCallBack; + if (((s->_flags >> 24) & writeCallBacksAvailable) != writeCallBacksAvailable) wakeup = true; + } + } + if (readCallBackType != kCFSocketNoCallBack && (callBackTypes & readCallBackType) != 0) { + if (__CFSocketFdClr(s->_socket, __CFReadSocketsFds)) { + // do not wake up the socket manager thread if callback type is read + if (readCallBackType != kCFSocketReadCallBack) wakeup = true; + } + } + __CFSpinUnlock(&__CFActiveSocketsLock); + } + __CFSocketUnlock(s); + if (wakeup) { + if (NULL == __CFSocketManagerThread) { + __CFSocketManagerThread = __CFStartSimpleThread(__CFSocketManager, 0); + } + send(__CFWakeupSocketPair[0], &c, sizeof(c), 0); + } +} + +void CFSocketEnableCallBacks(CFSocketRef s, CFOptionFlags callBackTypes) { + Boolean wakeup = false; + uint8_t c = 'r', readCallBackType; + __CFGenericValidateType(s, __kCFSocketTypeID); + __CFSocketLock(s); + if (__CFSocketIsValid(s) && 0 < s->_socketSetCount) { + callBackTypes &= __CFSocketCallBackTypes(s); + readCallBackType = __CFSocketReadCallBackType(s); + s->_flags &= ~(callBackTypes << 24); +#if defined(LOG_CFSOCKET) + printf("rescheduling socket %d with flags 0x%x for types 0x%x\n", s->_socket, s->_flags, callBackTypes); +#endif + __CFSpinLock(&__CFActiveSocketsLock); + if (NULL == __CFReadSockets) __CFSocketInitializeSockets(); + /* we will wait for connection only for connection-oriented, non-rendezvous sockets that are not already connected */ + if ((readCallBackType == kCFSocketAcceptCallBack) || !__CFSocketIsConnectionOriented(s)) s->_flags |= kCFSocketConnectCallBack; + if (((callBackTypes & kCFSocketWriteCallBack) != 0) || (((callBackTypes & kCFSocketConnectCallBack) != 0) && ((s->_flags & kCFSocketConnectCallBack) == 0))) { + SInt32 idx = CFArrayGetFirstIndexOfValue(__CFWriteSockets, CFRangeMake(0, CFArrayGetCount(__CFWriteSockets)), s); + if (kCFNotFound == idx) CFArrayAppendValue(__CFWriteSockets, s); + if (__CFSocketFdSet(s->_socket, __CFWriteSocketsFds)) wakeup = true; + } + if (readCallBackType != kCFSocketNoCallBack && (callBackTypes & readCallBackType) != 0) { + SInt32 idx = CFArrayGetFirstIndexOfValue(__CFReadSockets, CFRangeMake(0, CFArrayGetCount(__CFReadSockets)), s); + if (kCFNotFound == idx) CFArrayAppendValue(__CFReadSockets, s); + if (__CFSocketFdSet(s->_socket, __CFReadSocketsFds)) wakeup = true; + } + __CFSpinUnlock(&__CFActiveSocketsLock); + } + __CFSocketUnlock(s); + if (wakeup) { + if (NULL == __CFSocketManagerThread) { + __CFSocketManagerThread = __CFStartSimpleThread(__CFSocketManager, 0); + } + send(__CFWakeupSocketPair[0], &c, sizeof(c), 0); + } +} + +static void __CFSocketSchedule(void *info, CFRunLoopRef rl, CFStringRef mode) { + CFSocketRef s = info; + Boolean wakeup = false; + uint8_t c = 's', readCallBackType, callBackTypes; + __CFSocketLock(s); + //??? also need to arrange delivery of all pending data + if (__CFSocketIsValid(s)) { + CFArrayAppendValue(s->_runLoops, rl); + s->_socketSetCount++; + if (1 == s->_socketSetCount) { +#if defined(LOG_CFSOCKET) + printf("scheduling socket %d\n", s->_socket); +#endif + callBackTypes = __CFSocketCallBackTypes(s); + readCallBackType = __CFSocketReadCallBackType(s); + __CFSpinLock(&__CFActiveSocketsLock); + if (NULL == __CFReadSockets) __CFSocketInitializeSockets(); + /* we will wait for connection only for connection-oriented, non-rendezvous sockets that are not already connected */ + if ((readCallBackType == kCFSocketAcceptCallBack) || !__CFSocketIsConnectionOriented(s)) s->_flags |= kCFSocketConnectCallBack; + if (((callBackTypes & kCFSocketWriteCallBack) != 0) || (((callBackTypes & kCFSocketConnectCallBack) != 0) && ((s->_flags & kCFSocketConnectCallBack) == 0))) { + SInt32 idx = CFArrayGetFirstIndexOfValue(__CFWriteSockets, CFRangeMake(0, CFArrayGetCount(__CFWriteSockets)), s); + if (kCFNotFound == idx) CFArrayAppendValue(__CFWriteSockets, s); + if (__CFSocketFdSet(s->_socket, __CFWriteSocketsFds)) wakeup = true; + } + if (readCallBackType != kCFSocketNoCallBack) { + SInt32 idx = CFArrayGetFirstIndexOfValue(__CFReadSockets, CFRangeMake(0, CFArrayGetCount(__CFReadSockets)), s); + if (kCFNotFound == idx) CFArrayAppendValue(__CFReadSockets, s); + if (__CFSocketFdSet(s->_socket, __CFReadSocketsFds)) wakeup = true; + } + __CFSpinUnlock(&__CFActiveSocketsLock); + } + } + __CFSocketUnlock(s); + if (wakeup) { + if (NULL == __CFSocketManagerThread) { + __CFSocketManagerThread = __CFStartSimpleThread(__CFSocketManager, 0); + } + send(__CFWakeupSocketPair[0], &c, sizeof(c), 0); + } +} + +static void __CFSocketCancel(void *info, CFRunLoopRef rl, CFStringRef mode) { + CFSocketRef s = info; + SInt32 idx; + __CFSocketLock(s); + s->_socketSetCount--; + if (0 == s->_socketSetCount) { + __CFSpinLock(&__CFActiveSocketsLock); + if (NULL == __CFReadSockets) __CFSocketInitializeSockets(); + idx = CFArrayGetFirstIndexOfValue(__CFWriteSockets, CFRangeMake(0, CFArrayGetCount(__CFWriteSockets)), s); + if (0 <= idx) { + CFArrayRemoveValueAtIndex(__CFWriteSockets, idx); + __CFSocketFdClr(s->_socket, __CFWriteSocketsFds); + } + idx = CFArrayGetFirstIndexOfValue(__CFReadSockets, CFRangeMake(0, CFArrayGetCount(__CFReadSockets)), s); + if (0 <= idx) { + CFArrayRemoveValueAtIndex(__CFReadSockets, idx); + __CFSocketFdClr(s->_socket, __CFReadSocketsFds); + } + __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); +} + +static void __CFSocketPerform(void *info) { + CFSocketRef s = info; + CFDataRef data = NULL; + CFDataRef address = NULL; + CFSocketNativeHandle sock = INVALID_SOCKET; + CFSocketCallBack callout = NULL; + void *contextInfo = NULL; + SInt32 errorCode = 0; + Boolean wakeup = false, readSignalled = false, writeSignalled = false, connectSignalled = false, calledOut = false; + uint8_t c = 'p', readCallBackType, callBackTypes; + CFRunLoopRef rl = NULL; + + __CFSocketLock(s); + if (!__CFSocketIsValid(s)) { + __CFSocketUnlock(s); + return; + } + callBackTypes = __CFSocketCallBackTypes(s); + readCallBackType = __CFSocketReadCallBackType(s); + readSignalled = __CFSocketIsReadSignalled(s); + writeSignalled = __CFSocketIsWriteSignalled(s); + connectSignalled = writeSignalled && ((s->_flags & kCFSocketConnectCallBack) == 0); + __CFSocketUnsetReadSignalled(s); + __CFSocketUnsetWriteSignalled(s); + callout = s->_callout; + contextInfo = s->_context.info; +#if defined(LOG_CFSOCKET) + printf("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 (kCFSocketDataCallBack == readCallBackType) { + if (NULL != s->_dataQueue && 0 < CFArrayGetCount(s->_dataQueue)) { + data = CFArrayGetValueAtIndex(s->_dataQueue, 0); + CFRetain(data); + CFArrayRemoveValueAtIndex(s->_dataQueue, 0); + address = CFArrayGetValueAtIndex(s->_addressQueue, 0); + CFRetain(address); + CFArrayRemoveValueAtIndex(s->_addressQueue, 0); + } + } else if (kCFSocketAcceptCallBack == readCallBackType) { + if (NULL != s->_dataQueue && 0 < CFArrayGetCount(s->_dataQueue)) { + sock = (CFSocketNativeHandle)CFArrayGetValueAtIndex(s->_dataQueue, 0); + CFArrayRemoveValueAtIndex(s->_dataQueue, 0); + address = CFArrayGetValueAtIndex(s->_addressQueue, 0); + CFRetain(address); + CFArrayRemoveValueAtIndex(s->_addressQueue, 0); + } + } + if (writeSignalled) { + errorCode = s->_errorCode; + s->_flags |= kCFSocketConnectCallBack; + } + __CFSocketUnlock(s); + if ((callBackTypes & kCFSocketConnectCallBack) != 0) { + if (connectSignalled && (!calledOut || CFSocketIsValid(s))) { + if (errorCode) { +#if defined(LOG_CFSOCKET) + printf("perform calling out error %d to socket %d\n", errorCode, s->_socket); +#endif + if (callout) callout(s, kCFSocketConnectCallBack, NULL, &errorCode, contextInfo); + calledOut = true; + } else { +#if defined(LOG_CFSOCKET) + printf("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) + printf("perform calling out data of length %d to socket %d\n", datalen, s->_socket); +#endif + if (callout) callout(s, kCFSocketDataCallBack, address, data, contextInfo); + calledOut = true; + CFRelease(data); + CFRelease(address); + if (0 == datalen) CFSocketInvalidate(s); + } + } else if (kCFSocketAcceptCallBack == readCallBackType) { + if (INVALID_SOCKET != sock && (!calledOut || CFSocketIsValid(s))) { +#if defined(LOG_CFSOCKET) + printf("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; + CFRelease(address); + } + } else if (kCFSocketReadCallBack == readCallBackType) { + if (readSignalled && (!calledOut || CFSocketIsValid(s))) { +#if defined(LOG_CFSOCKET) + printf("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) + printf("perform calling out write to socket %d\n", s->_socket); +#endif + if (callout) callout(s, kCFSocketWriteCallBack, NULL, NULL, contextInfo); + calledOut = true; + } + } + __CFSocketLock(s); + if (__CFSocketIsValid(s) && kCFSocketNoCallBack != readCallBackType) { + if ((kCFSocketDataCallBack == readCallBackType || kCFSocketAcceptCallBack == readCallBackType) && NULL != s->_dataQueue && 0 < CFArrayGetCount(s->_dataQueue)) { + CFRunLoopSourceSignal(s->_source); +#if defined(LOG_CFSOCKET) + printf("perform signaling source for socket %d with flags 0x%x\n", s->_socket, s->_flags); +#endif + rl = __CFSocketCopyRunLoopToWakeUp(s); + } + if (readSignalled && 0 < s->_socketSetCount && (s->_flags & readCallBackType) != 0 && ((s->_flags >> 24) & readCallBackType) == 0) { + __CFSpinLock(&__CFActiveSocketsLock); + /* restore socket to fds */ + if (__CFSocketFdSet(s->_socket, __CFReadSocketsFds)) wakeup = true; + __CFSpinUnlock(&__CFActiveSocketsLock); + } + } + if (__CFSocketIsValid(s) && (callBackTypes & kCFSocketWriteCallBack) != 0) { + if (writeSignalled && 0 < s->_socketSetCount && (s->_flags & kCFSocketWriteCallBack) != 0 && ((s->_flags >> 24) & kCFSocketWriteCallBack) == 0) { + __CFSpinLock(&__CFActiveSocketsLock); + /* restore socket to fds */ + if (__CFSocketFdSet(s->_socket, __CFWriteSocketsFds)) wakeup = true; + __CFSpinUnlock(&__CFActiveSocketsLock); + } + } + __CFSocketUnlock(s); + if (wakeup) { + if (NULL == __CFSocketManagerThread) { + __CFSocketManagerThread = __CFStartSimpleThread(__CFSocketManager, 0); + } + send(__CFWakeupSocketPair[0], &c, sizeof(c), 0); + } + if (NULL != rl) { + CFRunLoopWakeUp(rl); + CFRelease(rl); + } +} + +CFRunLoopSourceRef CFSocketCreateRunLoopSource(CFAllocatorRef allocator, CFSocketRef s, CFIndex order) { + CFRunLoopSourceRef result = NULL; + __CFGenericValidateType(s, __kCFSocketTypeID); + __CFSocketLock(s); + if (__CFSocketIsValid(s)) { + if (NULL == s->_source) { + CFRunLoopSourceContext context; + context.version = 0; + context.info = (void *)s; + context.retain = (const void *(*)(const void *))CFRetain; + context.release = (void (*)(const void *))CFRelease; + context.copyDescription = (CFStringRef (*)(const void *))CFCopyDescription; + context.equal = (Boolean (*)(const void *, const void *))CFEqual; + context.hash = (CFHashCode (*)(const void *))CFHash; + context.schedule = __CFSocketSchedule; + context.cancel = __CFSocketCancel; + context.perform = __CFSocketPerform; + s->_source = CFRunLoopSourceCreate(allocator, order, &context); + } + CFRetain(s->_source); /* This retain is for the receiver */ + result = s->_source; + } + __CFSocketUnlock(s); + return result; +} + +//??? need timeout, error handling, retries +CFSocketError CFSocketSendData(CFSocketRef s, CFDataRef address, CFDataRef data, CFTimeInterval timeout) { + const uint8_t *dataptr, *addrptr = NULL; + SInt32 datalen, addrlen = 0, size = 0; + CFSocketNativeHandle sock = INVALID_SOCKET; + struct timeval tv; + __CFGenericValidateType(s, __kCFSocketTypeID); + if (address) { + addrptr = CFDataGetBytePtr(address); + addrlen = CFDataGetLength(address); + } + dataptr = CFDataGetBytePtr(data); + datalen = CFDataGetLength(data); + __CFSocketLock(s); + if (__CFSocketIsValid(s)) sock = s->_socket; + __CFSocketUnlock(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, &tv, sizeof(tv)); + if (NULL != addrptr && 0 < addrlen) { + size = sendto(sock, dataptr, datalen, 0, (struct sockaddr *)addrptr, addrlen); + } else { + size = send(sock, dataptr, datalen, 0); + } +#if defined(LOG_CFSOCKET) + printf("wrote %d bytes to socket %d\n", size, s->_socket); +#endif + __CFSocketWriteUnlock(s); + CFRelease(s); + } + return (size > 0) ? kCFSocketSuccess : kCFSocketError; +} + +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 = CFPropertyListCreateFromXMLData(NULL, 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(NULL, 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(NULL, requestDictionary); + if (NULL != requestData) { + if (NULL != response->error) *(response->error) = kCFSocketTimeout; + s = CFSocketCreateConnectedToSocketSignature(NULL, signature, kCFSocketDataCallBack, __CFSocketHandleNameRegistryReply, &context, timeout); + if (NULL != s) { + if (kCFSocketSuccess == CFSocketSendData(s, NULL, requestData, timeout)) { + source = CFSocketCreateRunLoopSource(NULL, 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(&sin, 0, sizeof(sain)); +#if !defined(__WIN32__) + 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(NULL, (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(NULL, (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 !defined(__WIN32__) + 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(NULL, (uint8_t *)&sain, sizeof(sain)); + } else { + signature->address = CFRetain(providedSignature->address); + } + } + } +} + +CFSocketError CFSocketRegisterValue(const CFSocketSignature *nameServerSignature, CFTimeInterval timeout, CFStringRef name, CFPropertyListRef value) { + CFSocketSignature signature; + CFMutableDictionaryRef dictionary = CFDictionaryCreateMutable(NULL, 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(NULL, 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(NULL, sizeof(bytes) + length); + bytes[0] = validatedSignature.protocolFamily; + bytes[1] = validatedSignature.socketType; + bytes[2] = validatedSignature.protocol; + bytes[3] = 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(NULL, 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(NULL, 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 ? 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/RunLoop.subproj/CFSocket.h b/RunLoop.subproj/CFSocket.h new file mode 100644 index 0000000..c6853fa --- /dev/null +++ b/RunLoop.subproj/CFSocket.h @@ -0,0 +1,245 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFSocket.h + Copyright (c) 1999-2003, Apple, Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFSOCKET__) +#define __COREFOUNDATION_CFSOCKET__ 1 + +#include +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif /* __cplusplus */ + +#if defined(__WIN32__) +#include +typedef SOCKET CFSocketNativeHandle; +#else +typedef int CFSocketNativeHandle; +#endif +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. + +*/ + +typedef enum { + kCFSocketSuccess = 0, + kCFSocketError = -1, + kCFSocketTimeout = -2 +} CFSocketError; + +typedef struct { + SInt32 protocolFamily; + SInt32 socketType; + SInt32 protocol; + CFDataRef address; +} CFSocketSignature; + +typedef 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 +} 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; + +#if defined(__cplusplus) +} +#endif /* __cplusplus */ + +#endif /* ! __COREFOUNDATION_CFSOCKET__ */ + diff --git a/RunLoop.subproj/CFWindowsMessageQueue.c b/RunLoop.subproj/CFWindowsMessageQueue.c new file mode 100644 index 0000000..73dfef9 --- /dev/null +++ b/RunLoop.subproj/CFWindowsMessageQueue.c @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFWindowsMessageQueue.c + Copyright 1999-2002, Apple, Inc. All rights reserved. + Responsibility: Christopher Kane +*/ + +#if defined(__WIN32__) + +#include "CFWindowsMessageQueue.h" +#include "CFInternal.h" + +extern unsigned long __CFRunLoopGetWindowsMessageQueueMask(CFRunLoopRef rl, CFStringRef mode); +extern void __CFRunLoopSetWindowsMessageQueueMask(CFRunLoopRef rl, unsigned long mask, CFStringRef mode); + +struct __CFWindowsMessageQueue { + CFRuntimeBase _base; + CFAllocatorRef _allocator; + CFSpinLock_t _lock; + DWORD _mask; + CFRunLoopSourceRef _source; + CFMutableArrayRef _runLoops; +}; + +/* Bit 3 in the base reserved bits is used for invalid state */ + +CF_INLINE Boolean __CFWindowsMessageQueueIsValid(CFWindowsMessageQueueRef wmq) { + return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)wmq)->_info, 3, 3); +} + +CF_INLINE void __CFWindowsMessageQueueSetValid(CFWindowsMessageQueueRef wmq) { + __CFBitfieldSetValue(((CFRuntimeBase *)wmq)->_info, 3, 3, 1); +} + +CF_INLINE void __CFWindowsMessageQueueUnsetValid(CFWindowsMessageQueueRef wmq) { + __CFBitfieldSetValue(((CFRuntimeBase *)wmq)->_info, 3, 3, 0); +} + +CF_INLINE void __CFWindowsMessageQueueLock(CFWindowsMessageQueueRef wmq) { + __CFSpinLock(&(wmq->_lock)); +} + +CF_INLINE void __CFWindowsMessageQueueUnlock(CFWindowsMessageQueueRef wmq) { + __CFSpinUnlock(&(wmq->_lock)); +} + +CFTypeID CFWindowsMessageQueueGetTypeID(void) { + return __kCFWindowsMessageQueueTypeID; +} + +Boolean __CFWindowsMessageQueueEqual(CFTypeRef cf1, CFTypeRef cf2) { + CFWindowsMessageQueueRef wmq1 = (CFWindowsMessageQueueRef)cf1; + CFWindowsMessageQueueRef wmq2 = (CFWindowsMessageQueueRef)cf2; + return (wmq1 == wmq2); +} + +CFHashCode __CFWindowsMessageQueueHash(CFTypeRef cf) { + CFWindowsMessageQueueRef wmq = (CFWindowsMessageQueueRef)cf; + return (CFHashCode)wmq; +} + +CFStringRef __CFWindowsMessageQueueCopyDescription(CFTypeRef cf) { +#warning CF: this and many other CopyDescription functions are probably +#warning CF: broken, in that some of these fields being printed out can +#warning CF: be NULL, when the object is in the invalid state + CFWindowsMessageQueueRef wmq = (CFWindowsMessageQueueRef)cf; + CFMutableStringRef result; + result = CFStringCreateMutable(CFGetAllocator(wmq), 0); + __CFWindowsMessageQueueLock(wmq); +#warning CF: here, and probably everywhere with a per-instance lock, +#warning CF: the locked state will always be true because we lock, +#warning CF: and you cannot call description if the object is locked; +#warning CF: probably should not lock description, and call it unsafe + CFStringAppendFormat(result, NULL, CFSTR("{locked = %s, valid = %s, mask = 0x%x,\n run loops = %@}"), (UInt32)cf, (UInt32)CFGetAllocator(wmq), (wmq->_lock ? "Yes" : "No"), (__CFWindowsMessageQueueIsValid(wmq) ? "Yes" : "No"), (UInt32)wmq->_mask, wmq->_runLoops); + __CFWindowsMessageQueueUnlock(wmq); + return result; +} + +CFAllocatorRef __CFWindowsMessageQueueGetAllocator(CFTypeRef cf) { + CFWindowsMessageQueueRef wmq = (CFWindowsMessageQueueRef)cf; + return wmq->_allocator; +} + +void __CFWindowsMessageQueueDeallocate(CFTypeRef cf) { + CFWindowsMessageQueueRef wmq = (CFWindowsMessageQueueRef)cf; + CFAllocatorRef allocator = CFGetAllocator(wmq); + CFAllocatorDeallocate(allocator, wmq); + CFRelease(allocator); +} + +CFWindowsMessageQueueRef CFWindowsMessageQueueCreate(CFAllocatorRef allocator, DWORD mask) { + CFWindowsMessageQueueRef memory; + UInt32 size; + size = sizeof(struct __CFWindowsMessageQueue); + allocator = (NULL == allocator) ? CFRetain(__CFGetDefaultAllocator()) : CFRetain(allocator); + memory = CFAllocatorAllocate(allocator, size, 0); + if (NULL == memory) { + CFRelease(allocator); + return NULL; + } + __CFGenericInitBase(memory, NULL, __kCFWindowsMessageQueueTypeID); + memory->_allocator = allocator; + __CFWindowsMessageQueueSetValid(memory); + memory->_lock = 0; + memory->_mask = mask; + memory->_source = NULL; + memory->_runLoops = CFArrayCreateMutable(allocator, 0, NULL); + return memory; +} + +void CFWindowsMessageQueueInvalidate(CFWindowsMessageQueueRef wmq) { + __CFGenericValidateType(wmq, __kCFWindowsMessageQueueTypeID); + CFRetain(wmq); + __CFWindowsMessageQueueLock(wmq); + if (__CFWindowsMessageQueueIsValid(wmq)) { + SInt32 idx; + __CFWindowsMessageQueueUnsetValid(wmq); + for (idx = CFArrayGetCount(wmq->_runLoops); idx--;) { + CFRunLoopWakeUp((CFRunLoopRef)CFArrayGetValueAtIndex(wmq->_runLoops, idx)); + } + CFRelease(wmq->_runLoops); + wmq->_runLoops = NULL; + if (NULL != wmq->_source) { + CFRunLoopSourceInvalidate(wmq->_source); + CFRelease(wmq->_source); + wmq->_source = NULL; + } + } + __CFWindowsMessageQueueUnlock(wmq); + CFRelease(wmq); +} + +Boolean CFWindowsMessageQueueIsValid(CFWindowsMessageQueueRef wmq) { + __CFGenericValidateType(wmq, __kCFWindowsMessageQueueTypeID); + return __CFWindowsMessageQueueIsValid(wmq); +} + +DWORD CFWindowsMessageQueueGetMask(CFWindowsMessageQueueRef wmq) { + __CFGenericValidateType(wmq, __kCFWindowsMessageQueueTypeID); + return wmq->_mask; +} + +static void __CFWindowsMessageQueueSchedule(void *info, CFRunLoopRef rl, CFStringRef mode) { + CFWindowsMessageQueueRef wmq = info; + __CFWindowsMessageQueueLock(wmq); + if (__CFWindowsMessageQueueIsValid(wmq)) { + unsigned long mask; + CFArrayAppendValue(wmq->_runLoops, rl); + mask = __CFRunLoopGetWindowsMessageQueueMask(rl, mode); + mask |= wmq->_mask; + __CFRunLoopSetWindowsMessageQueueMask(rl, mask, mode); + } + __CFWindowsMessageQueueUnlock(wmq); +} + +static void __CFWindowsMessageQueueCancel(void *info, CFRunLoopRef rl, CFStringRef mode) { + CFWindowsMessageQueueRef wmq = info; + __CFWindowsMessageQueueLock(wmq); +#warning CF: should fix up run loop modes mask here, if not done +#warning CF: previously by the invalidation, where it should also +#warning CF: be done + if (NULL != wmq->_runLoops) { + SInt32 idx = CFArrayGetFirstIndexOfValue(wmq->_runLoops, CFRangeMake(0, CFArrayGetCount(wmq->_runLoops)), rl); + if (0 <= idx) CFArrayRemoveValueAtIndex(wmq->_runLoops, idx); + } + __CFWindowsMessageQueueUnlock(wmq); +} + +static void __CFWindowsMessageQueuePerform(void *info) { + CFWindowsMessageQueueRef wmq = info; + MSG msg; + __CFWindowsMessageQueueLock(wmq); + if (!__CFWindowsMessageQueueIsValid(wmq)) { + __CFWindowsMessageQueueUnlock(wmq); + return; + } + __CFWindowsMessageQueueUnlock(wmq); + if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE | PM_NOYIELD)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } +} + +CFRunLoopSourceRef CFWindowsMessageQueueCreateRunLoopSource(CFAllocatorRef allocator, CFWindowsMessageQueueRef wmq, CFIndex order) { + CFRunLoopSourceRef result = NULL; + __CFWindowsMessageQueueLock(wmq); + if (NULL == wmq->_source) { + CFRunLoopSourceContext context; + context.version = 0; + context.info = (void *)wmq; + context.retain = (const void *(*)(const void *))CFRetain; + context.release = (void (*)(const void *))CFRelease; + context.copyDescription = (CFStringRef (*)(const void *))__CFWindowsMessageQueueCopyDescription; + context.equal = (Boolean (*)(const void *, const void *))__CFWindowsMessageQueueEqual; + context.hash = (CFHashCode (*)(const void *))__CFWindowsMessageQueueHash; + context.schedule = __CFWindowsMessageQueueSchedule; + context.cancel = __CFWindowsMessageQueueCancel; + context.perform = __CFWindowsMessageQueuePerform; + wmq->_source = CFRunLoopSourceCreate(allocator, order, &context); + } + CFRetain(wmq->_source); /* This retain is for the receiver */ + result = wmq->_source; + __CFWindowsMessageQueueUnlock(wmq); + return result; +} + +#endif + diff --git a/RunLoop.subproj/CFWindowsMessageQueue.h b/RunLoop.subproj/CFWindowsMessageQueue.h new file mode 100644 index 0000000..66c24f6 --- /dev/null +++ b/RunLoop.subproj/CFWindowsMessageQueue.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFWindowsMessageQueue.h + Copyright (c) 1999-2003, Apple, Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFWINDOWSMESSAGEQUEUE__) +#define __COREFOUNDATION_CFWINDOWSMESSAGEQUEUE__ 1 + +#if defined(__WIN32__) + +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +typedef struct __CFWindowsMessageQueue * CFWindowsMessageQueueRef; + +CF_EXPORT CFTypeID CFWindowsMessageQueueGetTypeID(void); + +CF_EXPORT CFWindowsMessageQueueRef CFWindowsMessageQueueCreate(CFAllocatorRef allocator, DWORD mask); + +CF_EXPORT DWORD CFWindowsMessageQueueGetMask(CFWindowsMessageQueueRef wmq); +CF_EXPORT void CFWindowsMessageQueueInvalidate(CFWindowsMessageQueueRef wmq); +CF_EXPORT Boolean CFWindowsMessageQueueIsValid(CFWindowsMessageQueueRef wmq); + +CF_EXPORT CFRunLoopSourceRef CFWindowsMessageQueueCreateRunLoopSource(CFAllocatorRef allocator, CFWindowsMessageQueueRef wmq, CFIndex order); + +#if defined(__cplusplus) +} +#endif + +#endif /* __WIN32__ */ + +#endif /* ! __COREFOUNDATION_CFWINDOWSMESSAGEQUEUE__ */ + diff --git a/String.subproj/CFCharacterSet.c b/String.subproj/CFCharacterSet.c new file mode 100644 index 0000000..4cbd4e6 --- /dev/null +++ b/String.subproj/CFCharacterSet.c @@ -0,0 +1,2681 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFCharacterSet.c + Copyright 1999-2002, Apple, Inc. All rights reserved. + Responsibility: Aki Inoue +*/ + +#include +#include +#include "CFCharacterSetPriv.h" +#include +#include +#include "CFInternal.h" +#include "CFUniChar.h" +#include "CFUniCharPriv.h" +#include +#include + +#if !defined(__MACOS8__) +#define __MACOS8__ 0 +#endif + +#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 kCFCharacterSetSymbol + +/* 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; + unsigned short _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._info & __kCFCharSetIsMutableMask) == __kCFCharSetIsMutable;} +CF_INLINE Boolean __CFCSetIsBuiltin(CFCharacterSetRef cset) {return (cset->_base._info & __kCFCharSetClassTypeMask) == __kCFCharSetClassBuiltin;} +CF_INLINE Boolean __CFCSetIsRange(CFCharacterSetRef cset) {return (cset->_base._info & __kCFCharSetClassTypeMask) == __kCFCharSetClassRange;} +CF_INLINE Boolean __CFCSetIsString(CFCharacterSetRef cset) {return (cset->_base._info & __kCFCharSetClassTypeMask) == __kCFCharSetClassString;} +CF_INLINE Boolean __CFCSetIsBitmap(CFCharacterSetRef cset) {return (cset->_base._info & __kCFCharSetClassTypeMask) == __kCFCharSetClassBitmap;} +CF_INLINE Boolean __CFCSetIsCompactBitmap(CFCharacterSetRef cset) {return (cset->_base._info & __kCFCharSetClassTypeMask) == __kCFCharSetClassCompactBitmap;} +CF_INLINE Boolean __CFCSetIsInverted(CFCharacterSetRef cset) {return (cset->_base._info & __kCFCharSetIsInvertedMask) == __kCFCharSetIsInverted;} +CF_INLINE Boolean __CFCSetHasHashValue(CFCharacterSetRef cset) {return (cset->_base._info & __kCFCharSetHasHashValueMask) == __kCFCharSetHasHashValue;} +CF_INLINE UInt32 __CFCSetClassType(CFCharacterSetRef cset) {return (cset->_base._info & __kCFCharSetClassTypeMask);} + +CF_INLINE void __CFCSetPutIsMutable(CFMutableCharacterSetRef cset, Boolean isMutable) {(isMutable ? (cset->_base._info |= __kCFCharSetIsMutable) : (cset->_base._info &= ~ __kCFCharSetIsMutable));} +CF_INLINE void __CFCSetPutIsInverted(CFMutableCharacterSetRef cset, Boolean isInverted) {(isInverted ? (cset->_base._info |= __kCFCharSetIsInverted) : (cset->_base._info &= ~__kCFCharSetIsInverted));} +CF_INLINE void __CFCSetPutHasHashValue(CFMutableCharacterSetRef cset, Boolean hasHash) {(hasHash ? (cset->_base._info |= __kCFCharSetHasHashValue) : (cset->_base._info &= ~__kCFCharSetHasHashValue));} +CF_INLINE void __CFCSetPutClassType(CFMutableCharacterSetRef cset, UInt32 classType) {cset->_base._info &= ~__kCFCharSetClassTypeMask; cset->_base._info |= 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))) << (((sizeof(UInt32) - 1) - length) * BITSPERBYTE)) | (0xFFFFFFFF >> ((length + 1) * BITSPERBYTE)); + + length = lastCharIndex % sizeof(UInt32); + lastCharMask = ((((UInt32)0xFF) >> ((BITSPERBYTE - 1) - (lastChar & (BITSPERBYTE - 1)))) << (((sizeof(UInt32) - 1) - length) * BITSPERBYTE)) | (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 ? 0xFFFFFFFF : 0); + while (length--) { + if (*(bits++) != value) return FALSE; + } + + ++bits; // Skip firstCharIndex + length = (lastCharIndex - (firstCharIndex + 1)); + value = (isInverted ? 0 : 0xFFFFFFFF); + while (length-- > 0) { + if (*(bits++) != value) return FALSE; + } + if (firstCharIndex != lastCharIndex) ++bits; + + length = (0xFFFF >> LOG_BPLW) - lastCharIndex; + value = (isInverted ? 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 ? (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 __CFCSetAnnexSetIsInverted(CFCharacterSetRef cset, Boolean flag) { + if (cset->_annex) ((CFMutableCharacterSetRef)cset)->_annex->_isAnnexInverted = flag; +} + +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 = (CFCharacterSetRef*)CFAllocatorAllocate(CFGetAllocator(cset), sizeof(CFCharacterSetRef) * plane, 0); + } else if (cset->_annex->_numOfAllocEntries < plane) { + cset->_annex->_numOfAllocEntries = plane; + cset->_annex->_nonBMPPlanes = (CFCharacterSetRef*)CFAllocatorReallocate(CFGetAllocator(cset), (void *)cset->_annex->_nonBMPPlanes, sizeof(CFCharacterSetRef) * plane, 0); + } +} + +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] = 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"); + long value = (envVar ? strtol(envVar, NULL, 0) : 0); + __CFNumberOfPlanesForLogging = (((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(0, 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); + 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, (theChar + length) - 1); + } else { + __CFCSetBitmapAddCharactersInRange(bits, theChar, (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]; +#if __MACOS8__ + uint8_t *bitsBuf2 = NULL; +#else __MACOS8__ + uint8_t bitsBuf2[__kCFBitmapSize]; +#endif __MACOS8__ + + for (idx = 1;idx <= MAX_ANNEX_PLANE;idx++) { + subSet1 = __CFCSetGetAnnexPlaneCharacterSetNoAlloc(cf1, idx); + subSet2 = __CFCSetGetAnnexPlaneCharacterSetNoAlloc(cf2, idx); + + if (subSet1 == NULL && subSet2 == NULL) { +#if __MACOS8__ + if (bitsBuf2) CFAllocatorDeallocate(NULL, bitsBuf2); +#endif __MACOS8__ + return false; + } else if (subSet1 == NULL) { + if (__CFCSetIsBitmap(subSet2)) { + if (!__CFCSetIsEqualBitmap((const UInt32 *)__CFCSetBitmapBits(subSet2), (const UInt32 *)-1)) { +#if __MACOS8__ + if (bitsBuf2) CFAllocatorDeallocate(NULL, bitsBuf2); +#endif __MACOS8__ + return false; + } + } else { + __CFCSetGetBitmap(subSet2, bitsBuf); + if (!__CFCSetIsEqualBitmap((const UInt32 *)bitsBuf, (const UInt32 *)-1)) { +#if __MACOS8__ + if (bitsBuf2) CFAllocatorDeallocate(NULL, bitsBuf2); +#endif __MACOS8__ + return false; + } + } + } else if (subSet2 == NULL) { + if (__CFCSetIsBitmap(subSet1)) { + if (!__CFCSetIsEqualBitmap((const UInt32 *)__CFCSetBitmapBits(subSet1), (const UInt32 *)-1)) { +#if __MACOS8__ + if (bitsBuf2) CFAllocatorDeallocate(NULL, bitsBuf2); +#endif __MACOS8__ + return false; + } + } else { + __CFCSetGetBitmap(subSet1, bitsBuf); + if (!__CFCSetIsEqualBitmap((const UInt32 *)bitsBuf, (const UInt32 *)-1)) { +#if __MACOS8__ + if (bitsBuf2) CFAllocatorDeallocate(NULL, bitsBuf2); +#endif __MACOS8__ + return false; + } + } + } else { + Boolean isBitmap1 = __CFCSetIsBitmap(subSet1); + Boolean isBitmap2 = __CFCSetIsBitmap(subSet2); + + if (isBitmap1 && isBitmap2) { + if (!__CFCSetIsEqualBitmapInverted((const UInt32 *)__CFCSetBitmapBits(subSet1), (const UInt32 *)__CFCSetBitmapBits(subSet2))) { +#if __MACOS8__ + if (bitsBuf2) CFAllocatorDeallocate(NULL, bitsBuf2); +#endif __MACOS8__ + return false; + } + } else if (!isBitmap1 && !isBitmap2) { + __CFCSetGetBitmap(subSet1, bitsBuf); +#if __MACOS8__ + if (bitsBuf2 == NULL) bitsBuf2 = (UInt32 *)CFAllocatorAllocate(NULL, __kCFBitmapSize, 0); +#endif __MACOS8__ + __CFCSetGetBitmap(subSet2, bitsBuf2); + if (!__CFCSetIsEqualBitmapInverted((const UInt32 *)bitsBuf, (const UInt32 *)bitsBuf2)) { +#if __MACOS8__ + CFAllocatorDeallocate(NULL, bitsBuf2); +#endif __MACOS8__ + return false; + } + } else { + if (isBitmap2) { + CFCharacterSetRef tmp = subSet2; + subSet2 = subSet1; + subSet1 = tmp; + } + __CFCSetGetBitmap(subSet2, bitsBuf); + if (!__CFCSetIsEqualBitmapInverted((const UInt32 *)__CFCSetBitmapBits(subSet1), (const UInt32 *)bitsBuf)) { +#if __MACOS8__ + if (bitsBuf2) CFAllocatorDeallocate(NULL, bitsBuf2); +#endif __MACOS8__ + 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)) { + uint8_t *bitmap = CFAllocatorAllocate(CFGetAllocator(cset), __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 = CFAllocatorAllocate(CFGetAllocator(cset), __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(CFGetAllocator(cset), annexBitmap); + } + } else if (__CFCSetIsCompactBitmap(cset) && __CFCSetCompactBitmapBits(cset)) { + CFAllocatorDeallocate(CFGetAllocator(cset), __CFCSetCompactBitmapBits(cset)); + __CFCSetPutCompactBitmapBits(cset, NULL); + } else if (__CFCSetIsString(cset) && __CFCSetStringBuffer(cset)) { + CFAllocatorDeallocate(CFGetAllocator(cset), __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._info |= 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, "") +CONST_STRING_DECL(__kCFCSetNameWhitespace, "") +CONST_STRING_DECL(__kCFCSetNameWhitespaceAndNewline, "") +CONST_STRING_DECL(__kCFCSetNameDecimalDigit, "") +CONST_STRING_DECL(__kCFCSetNameLetter, "") +CONST_STRING_DECL(__kCFCSetNameLowercaseLetter, "") +CONST_STRING_DECL(__kCFCSetNameUppercaseLetter, "") +CONST_STRING_DECL(__kCFCSetNameNonBase, "") +CONST_STRING_DECL(__kCFCSetNameDecomposable, "") +CONST_STRING_DECL(__kCFCSetNameAlphaNumeric, "") +CONST_STRING_DECL(__kCFCSetNamePunctuation, "") +CONST_STRING_DECL(__kCFCSetNameIllegal, "") +CONST_STRING_DECL(__kCFCSetNameCapitalizedLetter, "") +CONST_STRING_DECL(__kCFCSetNameSymbol, "") + +CONST_STRING_DECL(__kCFCSetNameStringTypeFormat, "_hashValue != ((CFCharacterSetRef)cf2)->_hashValue) return false; + if (__CFCSetIsEmpty(cf1) && __CFCSetIsEmpty(cf2) && !isInvertStateIdentical) return false; + + if (__CFCSetClassType(cf1) == __CFCSetClassType(cf2)) { // Types are identical, we can do it fast + switch (__CFCSetClassType(cf1)) { + case __kCFCharSetClassBuiltin: + return (__CFCSetBuiltinType(cf1) == __CFCSetBuiltinType(cf2) && isInvertStateIdentical ? true : false); + + case __kCFCharSetClassRange: + return (__CFCSetRangeFirstChar(cf1) == __CFCSetRangeFirstChar(cf2) && __CFCSetRangeLength(cf1) && __CFCSetRangeLength(cf2) && isInvertStateIdentical ? true : false); + + case __kCFCharSetClassString: + if (__CFCSetStringLength(cf1) == __CFCSetStringLength(cf2) && isInvertStateIdentical) { + const UniChar *buf1 = __CFCSetStringBuffer(cf1); + const UniChar *buf2 = __CFCSetStringBuffer(cf2); + CFIndex length = __CFCSetStringLength(cf1); + + while (length--) if (*buf1++ != *buf2++) return false; + } else { + return false; + } + break; + + case __kCFCharSetClassBitmap: + if (!__CFCSetIsEqualBitmap((const UInt32 *)__CFCSetBitmapBits(cf1), (const UInt32 *)__CFCSetBitmapBits(cf2))) return false; + break; + } + return __CFCSetIsEqualAnnex(cf1, cf2); + } + + // Check for easy empty cases + if (__CFCSetIsEmpty(cf1) || __CFCSetIsEmpty(cf2)) { + CFCharacterSetRef emptySet = (__CFCSetIsEmpty(cf1) ? cf1 : cf2); + CFCharacterSetRef nonEmptySet = (emptySet == cf1 ? cf2 : 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(cf1) || __CFCSetIsBuiltin(cf2)) { + CFCharacterSetRef builtinSet = (__CFCSetIsBuiltin(cf1) ? cf1 : cf2); + CFCharacterSetRef nonBuiltinSet = (builtinSet == cf1 ? cf2 : 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); + + 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 { +#if __MACOS8__ + uint8_t *bitsBuf2 = NULL; +#else __MACOS8__ + uint8_t bitsBuf2[__kCFBitmapSize]; +#endif __MACOS8__ + uint8_t result; + + result = CFUniCharGetBitmapForPlane(__CFCSetBuiltinType(builtinSet), 0, bitsBuf, __CFCSetIsInverted(builtinSet)); + if (result == kCFUniCharBitmapFilled) { + if (__CFCSetIsBitmap(nonBuiltinSet)) { + if (!__CFCSetIsEqualBitmap((const UInt32 *)bitsBuf, (const UInt32 *)__CFCSetBitmapBits(nonBuiltinSet))) return false; + } else { +#if __MACOS8__ + bitsBuf2 = CFAllocatorAllocate(CFGetAllocator(nonBuiltinSet), __kCFBitmapSize, 0); +#endif __MACOS8__ + + __CFCSetGetBitmap(nonBuiltinSet, bitsBuf2); + if (!__CFCSetIsEqualBitmap((const UInt32 *)bitsBuf, (const UInt32 *)bitsBuf2)) { +#if __MACOS8__ + CFAllocatorDeallocate(CFGetAllocator(nonBuiltinSet), bitsBuf2); +#endif __MACOS8__ + 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) { +#if __MACOS8__ + if (bitsBuf2) CFAllocatorDeallocate(CFGetAllocator(nonBuiltinSet), bitsBuf2); +#endif __MACOS8__ + return false; + } else if (__CFCSetIsBitmap(subSet1)) { + if (!__CFCSetIsEqualBitmap((const UInt32*)bitsBuf, (const UInt32*)__CFCSetBitmapBits(subSet1))) { +#if __MACOS8__ + if (bitsBuf2) CFAllocatorDeallocate(CFGetAllocator(nonBuiltinSet), bitsBuf2); +#endif __MACOS8__ + return false; + } + } else { +#if __MACOS8__ + if (NULL == bitsBuf2) bitsBuf2 = CFAllocatorAllocate(CFGetAllocator(nonBuiltinSet), __kCFBitmapSize, 0); +#endif __MACOS8__ + + __CFCSetGetBitmap(subSet1, bitsBuf2); + if (!__CFCSetIsEqualBitmap((const UInt32*)bitsBuf, (const UInt32*)bitsBuf2)) { +#if __MACOS8__ + CFAllocatorDeallocate(CFGetAllocator(nonBuiltinSet), bitsBuf2); +#endif __MACOS8__ + return false; + } + } + } else { + if (NULL == subSet1) { + if (result == kCFUniCharBitmapAll) { +#if __MACOS8__ + if (bitsBuf2) CFAllocatorDeallocate(CFGetAllocator(nonBuiltinSet), bitsBuf2); +#endif __MACOS8__ + return false; + } + } else if (__CFCSetIsBitmap(subSet1)) { + if (!__CFCSetIsEqualBitmap((result == kCFUniCharBitmapAll ? (const UInt32*)-1: NULL), (const UInt32*)__CFCSetBitmapBits(subSet1))) { +#if __MACOS8__ + if (bitsBuf2) CFAllocatorDeallocate(CFGetAllocator(nonBuiltinSet), bitsBuf2); +#endif __MACOS8__ + return false; + } + } else { + __CFCSetGetBitmap(subSet1, bitsBuf); + if (!__CFCSetIsEqualBitmap((result == kCFUniCharBitmapAll ? (const UInt32*)-1: NULL), (const UInt32*)bitsBuf)) { +#if __MACOS8__ + if (bitsBuf2) CFAllocatorDeallocate(CFGetAllocator(nonBuiltinSet), bitsBuf2); +#endif __MACOS8__ + return false; + } + } + } + } +#if __MACOS8__ + if (bitsBuf2) CFAllocatorDeallocate(CFGetAllocator(nonBuiltinSet), bitsBuf2); +#endif __MACOS8__ + return true; + } + } + + if (__CFCSetIsRange(cf1) || __CFCSetIsRange(cf2)) { + CFCharacterSetRef rangeSet = (__CFCSetIsRange(cf1) ? cf1 : cf2); + CFCharacterSetRef nonRangeSet = (rangeSet == cf1 ? cf2 : 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(cf1); + isBitmap2 = __CFCSetIsBitmap(cf2); + + if (isBitmap1 && isBitmap2) { + if (!__CFCSetIsEqualBitmap((const UInt32*)__CFCSetBitmapBits(cf1), (const UInt32*)__CFCSetBitmapBits(cf2))) return false; + } else if (!isBitmap1 && !isBitmap2) { +#if __MACOS8__ + uint8_t *bitsBuf2 = (uint8_t *)CFAllocatorAllocate(NULL, __kCFBitmapSize, 0); +#else __MACOS8__ + uint8_t bitsBuf2[__kCFBitmapSize]; +#endif __MACOS8__ + + __CFCSetGetBitmap(cf1, bitsBuf); + __CFCSetGetBitmap(cf2, bitsBuf2); + + if (!__CFCSetIsEqualBitmap((const UInt32*)bitsBuf, (const UInt32*)bitsBuf2)) { +#if __MACOS8__ + CFAllocatorDeallocate(NULL, bitsBuf2); +#endif __MACOS8__ + return false; + } +#if __MACOS8__ + CFAllocatorDeallocate(NULL, bitsBuf2); +#endif __MACOS8__ + } else { + if (isBitmap2) { + CFCharacterSetRef tmp = cf2; + cf2 = cf1; + cf1 = tmp; + } + + __CFCSetGetBitmap(cf2, bitsBuf); + + if (!__CFCSetIsEqualBitmap((const UInt32*)__CFCSetBitmapBits(cf1), (const UInt32*)bitsBuf)) return false; + } + return __CFCSetIsEqualAnnex(cf1, cf2); +} + +static CFHashCode __CFCharacterSetHash(CFTypeRef cf) { + if (!__CFCSetHasHashValue(cf)) { + if (__CFCSetIsEmpty(cf)) { + ((CFMutableCharacterSetRef)cf)->_hashValue = (__CFCSetIsInverted(cf) ? 0xFFFFFFFF : 0); + } else if (__CFCSetIsBitmap(cf)) { + ((CFMutableCharacterSetRef)cf)->_hashValue = CFHashBytes(__CFCSetBitmapBits(cf), __kCFBitmapSize); + } else { + uint8_t bitsBuf[__kCFBitmapSize]; + __CFCSetGetBitmap(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(cf)) { + return (__CFCSetIsInverted(cf) ? CFRetain(CFSTR("")) : CFRetain(CFSTR(""))); + } + + switch (__CFCSetClassType(cf)) { + case __kCFCharSetClassBuiltin: + switch (__CFCSetBuiltinType(cf)) { + case kCFCharacterSetControl: return CFRetain(__kCFCSetNameControl); + case kCFCharacterSetWhitespace : return CFRetain(__kCFCSetNameWhitespace); + case kCFCharacterSetWhitespaceAndNewline: return CFRetain(__kCFCSetNameWhitespaceAndNewline); + case kCFCharacterSetDecimalDigit: return CFRetain(__kCFCSetNameDecimalDigit); + case kCFCharacterSetLetter: return CFRetain(__kCFCSetNameLetter); + case kCFCharacterSetLowercaseLetter: return CFRetain(__kCFCSetNameLowercaseLetter); + case kCFCharacterSetUppercaseLetter: return CFRetain(__kCFCSetNameUppercaseLetter); + case kCFCharacterSetNonBase: return CFRetain(__kCFCSetNameNonBase); + case kCFCharacterSetDecomposable: return CFRetain(__kCFCSetNameDecomposable); + case kCFCharacterSetAlphaNumeric: return CFRetain(__kCFCSetNameAlphaNumeric); + case kCFCharacterSetPunctuation: return CFRetain(__kCFCSetNamePunctuation); + case kCFCharacterSetIllegal: return CFRetain(__kCFCSetNameIllegal); + case kCFCharacterSetCapitalizedLetter: return CFRetain(__kCFCSetNameCapitalizedLetter); + case kCFCharacterSetSymbol: return CFRetain(__kCFCSetNameSymbol); + } + break; + + case __kCFCharSetClassRange: + return CFStringCreateWithFormat(CFGetAllocator(cf), NULL, CFSTR(""), __CFCSetRangeFirstChar(cf), __CFCSetRangeLength(cf)); + + case __kCFCharSetClassString: + length = __CFCSetStringLength(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(cf))[idx])); + } + CFStringAppend(string, CFSTR(")>")); + return string; + + case __kCFCharSetClassBitmap: + case __kCFCharSetClassCompactBitmap: + return CFRetain(CFSTR("")); // ??? 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(cf) && !__CFCSetIsMutable(cf) && !__CFCSetIsInverted(cf)) { + CFCharacterSetRef sharedSet = CFCharacterSetGetPredefined(__CFCSetBuiltinType(cf)); + if (sharedSet == cf) { // We're trying to dealloc the builtin set + CFAssert1(0, __kCFLogAssertion, "%s: Trying to deallocate predefined set", __PRETTY_FUNCTION__); + return; // We never deallocate builtin set + } + } + + if (__CFCSetIsString(cf) && __CFCSetStringBuffer(cf)) CFAllocatorDeallocate(allocator, __CFCSetStringBuffer(cf)); + else if (__CFCSetIsBitmap(cf) && __CFCSetBitmapBits(cf)) CFAllocatorDeallocate(allocator, __CFCSetBitmapBits(cf)); + else if (__CFCSetIsCompactBitmap(cf) && __CFCSetCompactBitmapBits(cf)) CFAllocatorDeallocate(allocator, __CFCSetCompactBitmapBits(cf)); + __CFCSetDeallocateAnnexPlane(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 +*/ +#if defined(__MACOS8__) +CFTypeID CFCharacterSetTypeID(void) { +#ifdef DEBUG + CFLog(__kCFLogAssertion, CFSTR("CFCharacterSetTypeID should be CFCharacterSetGetTypeID")); +#endif /* DEBUG */ + return __kCFCharacterSetTypeID; +} +#endif /* __MACOS8__ */ + +CFTypeID CFCharacterSetGetTypeID(void) { + return __kCFCharacterSetTypeID; +} + +/*** CharacterSet creation ***/ +/* Functions to create basic immutable characterset. +*/ +CFCharacterSetRef CFCharacterSetGetPredefined(CFCharacterSetPredefinedSet theSetIdentifier) { + CFMutableCharacterSetRef cset; + + __CFCSetValidateBuiltinType(theSetIdentifier, __PRETTY_FUNCTION__); + + if (__CFBuiltinSets && __CFBuiltinSets[theSetIdentifier - 1]) return __CFBuiltinSets[theSetIdentifier - 1]; + + if (!(cset = __CFCSetGenericCreate(kCFAllocatorSystemDefault, __kCFCharSetClassBuiltin))) return NULL; + __CFCSetPutBuiltinType(cset, theSetIdentifier); + + if (!__CFBuiltinSets) { + __CFSpinLock(&__CFCharacterSetLock); + __CFBuiltinSets = (CFCharacterSetRef *)CFAllocatorAllocate(CFRetain(__CFGetDefaultAllocator()), sizeof(CFCharacterSetRef) * __kCFLastBuiltinSetID, 0); + memset(__CFBuiltinSets, 0, sizeof(CFCharacterSetRef) * __kCFLastBuiltinSetID); + __CFSpinUnlock(&__CFCharacterSetLock); + } + + __CFBuiltinSets[theSetIdentifier - 1] = cset; + + 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, 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; + } +} + +#if defined(__MACOS8__) +CFCharacterSetRef CFCharacterSetCreateWithBitmapReresentation(CFAllocatorRef allocator, CFDataRef theData) { +#ifdef DEBUG + CFLog(__kCFLogAssertion, CFSTR("CFCharacterSetCreateWithBitmapReresentation should be CFCharacterSetCreateWithBitmapRepresentation")); +#endif /* DEBUG */ + return CFCharacterSetCreateWithBitmapRepresentation(allocator, theData); +} +#endif /* __MACOS8__ */ + +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 char *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, 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) { + return CFCharacterSetIsLongCharacterMember(theSet, theChar); +} + +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 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(NULL, 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) { + 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); + + 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); + + 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) { + char *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; + Boolean isInverted = __CFCSetIsInverted(theSet); + + for (idx = 0;idx < numNonBMPPlanes;idx++) { + if ((result = CFUniCharGetBitmapForPlane(__CFCSetBuiltinType(theSet), idx + 1, bytes + 1, isInverted)) == kCFUniCharBitmapEmpty) continue; + *(bytes++) = idx + 1; + if (result == kCFUniCharBitmapAll) { + CFIndex bitmapLength = __kCFBitmapSize; + while (bitmapLength-- > 0) *(bytes++) = (uint8_t)0xFF; + } else { + bytes += __kCFBitmapSize; + } + } + if (bytes - (const char *)CFDataGetBytePtr(data) < length) CFDataSetLength(data, bytes - (const char *)CFDataGetBytePtr(data)); + } 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, CFAllocatorAllocate(CFGetAllocator(theSet), __kCFStringCharSetMax * sizeof(UniChar), 0)); + buffer = __CFCSetStringBuffer(theSet) + __CFCSetStringLength(theSet); + __CFCSetPutStringLength(theSet, __CFCSetStringLength(theSet) + theRange.length); + while (theRange.length--) *buffer++ = 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), theRange.location, 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, CFAllocatorAllocate(CFGetAllocator(theSet), __kCFStringCharSetMax * sizeof(UniChar), 0)); + buffer = __CFCSetStringBuffer(theSet) + __CFCSetStringLength(theSet); + __CFCSetPutStringLength(theSet, __CFCSetStringLength(theSet) + theRange.length); + while (theRange.length--) *buffer++ = 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), theRange.location, 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)) __CFCSetPutStringLength(theSet, 0); // Make sure to reset this + + if (!__CFCSetStringBuffer(theSet)) + __CFCSetPutStringBuffer(theSet, 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)) __CFCSetPutStringLength(theSet, 0); // Make sure to reset this + + if (!__CFCSetStringBuffer(theSet)) + __CFCSetPutStringBuffer(theSet, 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 { + return; // Nothing to do here + } + } + + 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); + 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(NULL, 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, CFAllocatorAllocate(CFGetAllocator(theSet), __kCFStringCharSetMax * sizeof(UniChar), 0)); + memmove(__CFCSetStringBuffer(theSet), __CFCSetStringBuffer(theOtherSet), __CFCSetStringLength(theSet) * sizeof(UniChar)); + break; + + case __kCFCharSetClassBitmap: + __CFCSetPutBitmapBits(theSet, 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); + 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(NULL, 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] = 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 : kCFCharacterSetKeyedCodingTypeBitmap); + case __kCFCharSetClassRange: return kCFCharacterSetKeyedCodingTypeRange; + + case __kCFCharSetClassString: // We have to check if we have non-BMP here + if (!__CFCSetHasNonBMPPlane(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(NULL, __CFCSetStringBuffer(cset), __CFCSetStringLength(cset)); } + +bool _CFCharacterSetIsInverted(CFCharacterSetRef cset) { return __CFCSetIsInverted(cset); } +void _CFCharacterSetSetIsInverted(CFCharacterSetRef cset, bool flag) { __CFCSetPutIsInverted((CFMutableCharacterSetRef)cset, flag); } + + diff --git a/String.subproj/CFCharacterSet.h b/String.subproj/CFCharacterSet.h new file mode 100644 index 0000000..39adc8a --- /dev/null +++ b/String.subproj/CFCharacterSet.h @@ -0,0 +1,416 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFCharacterSet.h + Copyright (c) 1999-2003, 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 +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +/*! + @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. +*/ +typedef 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*) */ + kCFCharacterSetIllegal /* Illegal character set */ +#if MAC_OS_X_VERSION_10_2 <= MAC_OS_X_VERSION_MAX_ALLOWED + , kCFCharacterSetCapitalizedLetter /* Titlecase character set (Unicode General Category Lt) */ +#endif +#if MAC_OS_X_VERSION_10_3 <= MAC_OS_X_VERSION_MAX_ALLOWED + , kCFCharacterSetSymbol /* Symbol character set (Unicode General Category S*) */ +#endif +} 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 8K bytes + of the data represents the BMP range. The BMP range 8K + bytes can be followed by zero to sixteen 8K 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 (8K bytes for BMP, 1 byte + index + 8K 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 /* MAC_OS_X_VERSION_10_3 <= MAC_OS_X_VERSION_MAX_ALLOWED */ + +/*! + @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); + +#if defined(__cplusplus) +} +#endif + +#endif /* !__COREFOUNDATION_CFCHARACTERSET__ */ + diff --git a/String.subproj/CFCharacterSetPriv.h b/String.subproj/CFCharacterSetPriv.h new file mode 100644 index 0000000..503121b --- /dev/null +++ b/String.subproj/CFCharacterSetPriv.h @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFCharacterSetPriv.h + Copyright (c) 1998-2003, Apple, Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFCHARACTERSET_PRIV__) +#define __COREFOUNDATION_CFCHARACTERSET_PRIV__ 1 + +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +#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 +*/ +typedef enum { + kCFCharacterSetKeyedCodingTypeBitmap = 1, + kCFCharacterSetKeyedCodingTypeBuiltin = 2, + kCFCharacterSetKeyedCodingTypeRange = 3, + kCFCharacterSetKeyedCodingTypeString = 4 +} 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 _CFCharacterSetIsMutable(CFCharacterSetRef cset); +CF_EXPORT bool _CFCharacterSetIsInverted(CFCharacterSetRef cset); +CF_EXPORT void _CFCharacterSetSetIsInverted(CFCharacterSetRef cset, bool flag); + +#if defined(__cplusplus) +} +#endif + +#endif /* !__COREFOUNDATION_CFCHARACTERSET_PRIV__ */ diff --git a/String.subproj/CFString.c b/String.subproj/CFString.c new file mode 100644 index 0000000..ff6ed41 --- /dev/null +++ b/String.subproj/CFString.c @@ -0,0 +1,4783 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFString.c + Copyright 1998-2002, Apple, Inc. All rights reserved. + Responsibility: Ali Ozer +*/ + +#include +#include +#include +#include "CFStringEncodingConverterExt.h" +#include "CFUniChar.h" +#include "CFUnicodeDecomposition.h" +#include "CFUnicodePrecomposition.h" +#include "CFUtilities.h" +#include "CFInternal.h" +#include +#include +/* strncmp, etc */ +#include +#if defined(__MACH__) || defined(__LINUX__) || defined(__FREEBSD__) +#include +#endif +#if defined(__WIN32__) +#include +#endif /* __WIN32__ */ + +extern size_t malloc_good_size(size_t size); +extern void __CFStrConvertBytesToUnicode(const uint8_t *bytes, UniChar *buffer, CFIndex numChars); + +#if defined(DEBUG) + +// Special allocator used by CFSTRs to catch deallocations +static CFAllocatorRef constantStringAllocatorForDebugging = NULL; + +// 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, "") +CONST_STRING_DECL(kCFNSDecimalSeparatorKey, "NSDecimalSeparator") + + +/* !!! 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 { + SInt32 length; + } inline1; + + struct { + void *buffer; + UInt32 length; + CFAllocatorRef contentsDeallocator; // Just the dealloc func is used + } externalImmutable1; + struct { + void *buffer; + CFAllocatorRef contentsDeallocator; // Just the dealloc func is used + } externalImmutable2; + struct { + void *buffer; + UInt32 length; + UInt32 capacityFields; // Currently only stores capacity + UInt32 gapEtc; // Stores some bits, plus desired or fixed capacity + CFAllocatorRef contentsAllocator; // Optional + } externalMutable; + } 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) +X = is external mutable + +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 X I + +B6 B5 + 0 0 inline contents + 0 1 E (freed with default allocator) + 1 0 E (not freed) + 1 1 E D +*/ + +enum { + __kCFFreeContentsWhenDoneMask = 0x020, + __kCFFreeContentsWhenDone = 0x020, + __kCFContentsMask = 0x060, + __kCFHasInlineData = 0x000, + __kCFHasExternalDataNoFree = 0x040, // Don't free + __kCFHasExternalDataDefaultFree = 0x020, // Use allocator's free function + __kCFHasExternalDataCustomFree = 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, + __kCFIsExternalMutableMask = 0x02, // For now we use this bit; can switch to something else + __kCFIsExternalMutable = 0x02, + // These are in variants.externalMutable.gapEtc + __kCFGapMask = 0x00ffffff, + __kCFGapBitNumber = 24, + __kCFDesiredCapacityMask = 0x00ffffff, // Currently gap and fixed share same bits as gap not implemented + __kCFDesiredCapacityBitNumber = 24, + __kCFIsFixedMask = 0x80000000, + __kCFIsFixed = 0x80000000, + __kCFHasGapMask = 0x40000000, + __kCFHasGap = 0x40000000, + __kCFCapacityProvidedExternallyMask = 0x20000000, // Set if the external buffer is set explicitly by the developer + __kCFCapacityProvidedExternally = 0x20000000 +}; + + +// !!! 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._info & __kCFIsMutableMask) == __kCFIsMutable;} +CF_INLINE Boolean __CFStrIsExternalMutable(CFStringRef str) {return (str->base._info & __kCFIsExternalMutableMask) == __kCFIsExternalMutable;} +CF_INLINE Boolean __CFStrIsInline(CFStringRef str) {return (str->base._info & __kCFContentsMask) == __kCFHasInlineData;} +CF_INLINE Boolean __CFStrFreeContentsWhenDone(CFStringRef str) {return (str->base._info & __kCFFreeContentsWhenDoneMask) == __kCFFreeContentsWhenDone;} +CF_INLINE Boolean __CFStrHasContentsDeallocator(CFStringRef str) {return (str->base._info & __kCFHasContentsDeallocatorMask) == __kCFHasContentsDeallocator;} +CF_INLINE Boolean __CFStrIsUnicode(CFStringRef str) {return (str->base._info & __kCFIsUnicodeMask) == __kCFIsUnicode;} +CF_INLINE Boolean __CFStrIsEightBit(CFStringRef str) {return (str->base._info & __kCFIsUnicodeMask) != __kCFIsUnicode;} +CF_INLINE Boolean __CFStrHasNullByte(CFStringRef str) {return (str->base._info & __kCFHasNullByteMask) == __kCFHasNullByte;} +CF_INLINE Boolean __CFStrHasLengthByte(CFStringRef str) {return (str->base._info & __kCFHasLengthByteMask) == __kCFHasLengthByte;} +CF_INLINE Boolean __CFStrHasExplicitLength(CFStringRef str) {return (str->base._info & (__kCFIsMutableMask | __kCFHasLengthByteMask)) != __kCFHasLengthByte;} // Has explicit length if (1) mutable or (2) not mutable and no length byte + +CF_INLINE SInt32 __CFStrSkipAnyLengthByte(CFStringRef str) {return ((str->base._info & __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 *)(((UInt32)&(str->variants)) + (__CFStrHasExplicitLength(str) ? sizeof(UInt32) : 0)); + } else { // External; pointer is always word 2 + return str->variants.externalImmutable1.buffer; + } +} + +static CFAllocatorRef *__CFStrContentsDeallocatorPtr(CFStringRef str) { + return __CFStrHasExplicitLength(str) ? &(((CFMutableStringRef)str)->variants.externalImmutable1.contentsDeallocator) : &(((CFMutableStringRef)str)->variants.externalImmutable2.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.externalMutable.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 { + CFIndex len = str->variants.externalImmutable1.length; + if (len == 0x0ffffff) ((CFMutableStringRef)str)->variants.externalImmutable1.length = (len = strlen(__CFStrContents(str))); /* For compile-time constant strings */ + return len; + } + } 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 { + CFIndex len = str->variants.externalImmutable1.length; + if (len == 0x0ffffff) ((CFMutableStringRef)str)->variants.externalImmutable1.length = (len = strlen(buffer)); /* For compile-time constant strings */ + return len; + } + } else { + return (CFIndex)(*((uint8_t *)buffer)); + } +} + +Boolean __CFStringIsMutable(CFStringRef str) { + return __CFStrIsMutable(str); +} + +Boolean __CFStringIsEightBit(CFStringRef str) { + return __CFStrIsEightBit(str); +} + +/* Sets the external content pointer for immutable or mutable strings. +*/ +CF_INLINE void __CFStrSetContentPtr(CFStringRef str, const void *p) {((CFMutableStringRef)str)->variants.externalImmutable1.buffer = (void *)p;} +CF_INLINE void __CFStrSetInfoBits(CFStringRef str, UInt32 v) {__CFBitfieldSetValue(((CFMutableStringRef)str)->base._info, 6, 0, v);} + +CF_INLINE void __CFStrSetExplicitLength(CFStringRef str, CFIndex v) { + if (__CFStrIsInline(str)) { + ((CFMutableStringRef)str)->variants.inline1.length = v; + } else { + ((CFMutableStringRef)str)->variants.externalImmutable1.length = v; + } +} + +// Assumption: Called with mutable strings only +CF_INLINE Boolean __CFStrIsFixed(CFStringRef str) {return (str->variants.externalMutable.gapEtc & __kCFIsFixedMask) == __kCFIsFixed;} +CF_INLINE Boolean __CFStrHasContentsAllocator(CFStringRef str) {return (str->base._info & __kCFHasContentsAllocatorMask) == __kCFHasContentsAllocator;} + +// 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.externalMutable.gapEtc & __kCFCapacityProvidedExternallyMask) == __kCFCapacityProvidedExternally;} +CF_INLINE void __CFStrSetCapacityProvidedExternally(CFMutableStringRef str) {str->variants.externalMutable.gapEtc |= __kCFCapacityProvidedExternally;} +CF_INLINE void __CFStrClearCapacityProvidedExternally(CFMutableStringRef str) {str->variants.externalMutable.gapEtc &= ~__kCFCapacityProvidedExternally;} + + +CF_INLINE void __CFStrSetIsFixed(CFMutableStringRef str) {str->variants.externalMutable.gapEtc |= __kCFIsFixed;} +CF_INLINE void __CFStrSetHasGap(CFMutableStringRef str) {str->variants.externalMutable.gapEtc |= __kCFHasGap;} +CF_INLINE void __CFStrSetUnicode(CFMutableStringRef str) {str->base._info |= __kCFIsUnicode;} +CF_INLINE void __CFStrClearUnicode(CFMutableStringRef str) {str->base._info &= ~__kCFIsUnicode;} +CF_INLINE void __CFStrSetHasLengthAndNullBytes(CFMutableStringRef str) {str->base._info |= (__kCFHasLengthByte | __kCFHasNullByte);} +CF_INLINE void __CFStrClearHasLengthAndNullBytes(CFMutableStringRef str) {str->base._info &= ~(__kCFHasLengthByte | __kCFHasNullByte);} + + +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); + CFAllocatorDeallocate(alloc, buffer); +} + + +// The following set of functions should only be called on mutable strings + +/* "Capacity" is stored in number of bytes, not characters. It indicates the total number of bytes in the contents buffer. + "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 __CFStrCapacity(CFStringRef str) {return str->variants.externalMutable.capacityFields;} +CF_INLINE void __CFStrSetCapacity(CFMutableStringRef str, CFIndex cap) {str->variants.externalMutable.capacityFields = cap;} +CF_INLINE CFIndex __CFStrDesiredCapacity(CFStringRef str) {return __CFBitfieldGetValue(str->variants.externalMutable.gapEtc, __kCFDesiredCapacityBitNumber, 0);} +CF_INLINE void __CFStrSetDesiredCapacity(CFMutableStringRef str, CFIndex size) {__CFBitfieldSetValue(str->variants.externalMutable.gapEtc, __kCFDesiredCapacityBitNumber, 0, size);} + + + + +/* 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 */ +}; + +/* Size for temporary buffers +*/ +#define MAXTMPBUFFERLEN (2048) +#define MAXISDECOMPBUFFERLEN (32) + +/* System Encoding. +*/ +static CFStringEncoding __CFDefaultSystemEncoding = kCFStringEncodingInvalidId; +static CFStringEncoding __CFDefaultFileSystemEncoding = kCFStringEncodingInvalidId; +CFStringEncoding __CFDefaultEightBitStringEncoding = kCFStringEncodingInvalidId; + +CFStringEncoding CFStringGetSystemEncoding(void) { + + if (__CFDefaultSystemEncoding == kCFStringEncodingInvalidId) { + const CFStringEncodingConverter *converter = NULL; +#if defined(__MACOS8__) || defined(__MACH__) + __CFDefaultSystemEncoding = kCFStringEncodingMacRoman; // MacRoman is built-in so always available +#elif defined(__WIN32__) + __CFDefaultSystemEncoding = kCFStringEncodingWindowsLatin1; // WinLatin1 is built-in so always available +#elif defined(__LINUX__) || defined(__FREEBSD__) + __CFDefaultSystemEncoding = kCFStringEncodingISOLatin1; // a reasonable default +#else // Solaris && HP-UX ? + __CFDefaultSystemEncoding = kCFStringEncodingISOLatin1; // a reasonable default +#endif + converter = CFStringEncodingGetConverter(__CFDefaultSystemEncoding); + + __CFSetCharToUniCharFunc(converter->encodingClass == kCFStringEncodingConverterCheapEightBit ? 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 defined(__MACH__) + __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 { + 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) { + case kCFStringEncodingInvalidId: + case kCFStringEncodingUnicode: + case kCFStringEncodingUTF8: + 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 __CFAssertLengthIsOK(len) CFAssert2(len < __kCFMaxLength, __kCFLogAssertion, "%s(): length %d too large", __PRETTY_FUNCTION__, len) +#define __CFAssertIsStringAndMutable(cf) {__CFGenericValidateType(cf, __kCFStringTypeID); CFAssert1(__CFStrIsMutable(cf), __kCFLogAssertion, "%s(): string not mutable", __PRETTY_FUNCTION__);} +#define __CFAssertIsStringAndExternalMutable(cf) {__CFGenericValidateType(cf, __kCFStringTypeID); CFAssert1(__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 +*/ +#define SHRINKFACTOR(c) (c / 2) +#define GROWFACTOR(c) ((c * 3 + 1) / 2) + +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); + } else { + newCapacity = malloc_good_size(newCapacity); + } + 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 { + int beginning; + int length; + int shift; +} CFStringDeferredRange; + +typedef struct _CFStringStackInfo { + int capacity; // Capacity (if capacity == count, need to realloc to add another) + int 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 = CFAllocatorReallocate(NULL, si->stack, si->capacity * sizeof(CFStringDeferredRange), 0); + } else { + CFStringDeferredRange *newStack = (CFStringDeferredRange *)CFAllocatorAllocate(NULL, 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}; + int currentRange = 0; + int 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 (NULL, 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); + } + } +} + + +/* 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 = __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 { + int 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 = allocNewBuffer ? __CFStrAllocateMutableContents(str, newCapacity) : (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) __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); +} + + +static void __CFStringDeallocate(CFTypeRef cf) { + CFStringRef str = cf; + + // constantStringAllocatorForDebugging is not around unless DEBUG is defined, but neither is CFAssert2()... + CFAssert1(__CFConstantStringTableBeingFreed || CFGetAllocator(str) != constantStringAllocatorForDebugging, __kCFLogAssertion, "Tried to deallocate CFSTR(\"%@\")", str); + + if (!__CFStrIsInline(str)) { + uint8_t *contents; + Boolean mutable = __CFStrIsMutable(str); + if (__CFStrFreeContentsWhenDone(str) && (contents = (uint8_t *)__CFStrContents(str))) { + if (mutable) { + __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 (mutable && __CFStrHasContentsAllocator(str)) CFRelease(__CFStrContentsAllocator((CFMutableStringRef)str)); + } +} + +static Boolean __CFStringEqual(CFTypeRef cf1, CFTypeRef cf2) { + CFStringRef str1 = cf1; + CFStringRef str2 = 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 = __CFStrContents(str1); + contents2 = __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 16, 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 16, then the above algorithm applies to +characters 0..7 and length-8..length-1; thus the first and last 8 characters. +*/ +CFHashCode __CFStringHash(CFTypeRef cf) { + CFStringRef str = cf; + const uint8_t *contents; + CFIndex len; + CFIndex cnt; + UInt32 result; + + /* !!! We do not need an IsString assertion here, as this is called by the CFBase runtime only */ + + contents = __CFStrContents(str); + len = __CFStrLength2(str, contents); + result = len; + if (__CFStrIsEightBit(str)) { + contents += __CFStrSkipAnyLengthByte(str); + if (len <= 16) { + for (cnt = 0; cnt < len; cnt++) result = result * 257 + __CFCharToUniCharTable[contents[cnt]]; + } else { + for (cnt = 0; cnt < 8; cnt++) result = result * 257 + __CFCharToUniCharTable[contents[cnt]]; + for (cnt = len - 8; cnt < len; cnt++) result = result * 257 + __CFCharToUniCharTable[contents[cnt]]; + } +#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 + Boolean err = false; + if (len <= 16) { + for (cnt = 0; cnt < len; cnt++) if (contents[cnt] >= 128) err = true; + } else { + for (cnt = 0; cnt < 8; cnt++) if (contents[cnt] >= 128) err = true; + for (cnt = len - 8; cnt < len; cnt++) if (contents[cnt] >= 128) err = true; + } + if (err) { + // Can't do log here, as it might be too early + printf("Warning: CFHash() attempting to hash CFString containing high bytes before properly initialized to do so\n"); + } + } +#endif + } else { + const UniChar *uContents = (UniChar *)contents; + if (len <= 16) { + for (cnt = 0; cnt < len; cnt++) result = result * 257 + uContents[cnt]; + } else { + for (cnt = 0; cnt < 8; cnt++) result = result * 257 + uContents[cnt]; + for (cnt = len - 8; cnt < len; cnt++) result = result * 257 + uContents[cnt]; + } + } + result += (result << (len & 31)); + return result; +} + + +static CFStringRef __CFStringCopyDescription(CFTypeRef cf) { + return CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("{contents = \"%@\"}"), cf, __CFGetAllocator(cf), cf); +} + +static CFStringRef __CFStringCopyFormattingDescription(CFTypeRef cf, CFDictionaryRef formatOptions) { + return CFStringCreateCopy(__CFGetAllocator(cf), cf); +} + +static CFTypeID __kCFStringTypeID = _kCFRuntimeNotATypeID; + +static const CFRuntimeClass __CFStringClass = { + 0, + "CFString", + NULL, // init + (void *)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 ((void *)-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 (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 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 + + // 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 && !__CFCanUseEightBitCFStringForBytes(bytes, numBytes, encoding))) { + 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(realBytes, realNumBytes, encoding, false, &vBuf, &usingPassedInMemory, converterFlags)) { + return NULL; // !!! Is this acceptable failure mode? + } + + encoding = vBuf.isASCII ? kCFStringEncodingASCII : kCFStringEncodingUnicode; + + if (!usingPassedInMemory) { + + // 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; + } else { + 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; + hasLengthByte = __CFCanUseLengthByte(len); + hasNullByte = true; + numBytes = (len + 1 + (hasLengthByte ? 1 : 0)) * sizeof(uint8_t); // NULL and possible length byte + // See if we can use that temporary local buffer in vBuf... + mem = ptr = (uint8_t *)((numBytes >= __kCFVarWidthLocalBufferSize) ? CFAllocatorAllocate(alloc, numBytes, 0) : vBuf.localBuffer); + if (mem != vBuf.localBuffer && __CFOASafe) __CFSetLastAllocationEventName(mem, "CFString (store)"); + if (hasLengthByte) *ptr++ = len; + for (cnt = 0; cnt < len; cnt++) ptr[cnt] = ((const UniChar *)bytes)[cnt]; + ptr[len] = 0; + if (noCopy && contentsDeallocator != kCFAllocatorNull) { + CFAllocatorDeallocate(contentsDeallocator, (void *)bytes); + } + 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... + } + + // At this point, all necessary input arguments have been changed to reflect the new state + } + + // 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(SInt32); // 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(SInt32); // Explicit length + } + if (hasNullByte || encoding != kCFStringEncodingUnicode) { + useNullByte = true; + size += 1; + } + } + + // Finally, allocate! + + str = (CFMutableStringRef)_CFRuntimeCreateInstance(alloc, __kCFStringTypeID, size, NULL); + if (__CFOASafe) __CFSetLastAllocationEventName(str, "CFString (immutable)"); + + __CFStrSetInfoBits(str, + (useInlineData ? __kCFHasInlineData : (contentsDeallocator == alloc ? __kCFHasExternalDataDefaultFree : (contentsDeallocator == kCFAllocatorNull ? __kCFHasExternalDataNoFree : __kCFHasExternalDataCustomFree))) | + ((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++ = numBytes; + memmove(contents, bytes, numBytes); + if (useNullByte) contents[numBytes] = 0; + } else { + __CFStrSetContentPtr(str, bytes); + if (contentsDeallocator != alloc && contentsDeallocator != kCFAllocatorNull) __CFStrSetContentsDeallocator(str, CFRetain(contentsDeallocator)); + } + 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 = 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 = 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 CFStringCreateWithFormatAndArguments(CFAllocatorRef alloc, CFDictionaryRef formatOptions, CFStringRef format, va_list arguments) { + return _CFStringCreateWithFormatAndArgumentsAux(alloc, NULL, formatOptions, format, arguments); +} + +CFStringRef _CFStringCreateWithFormatAndArgumentsAux(CFAllocatorRef alloc, CFStringRef (*copyDescFunc)(void *, CFDictionaryRef), 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 = 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 CFStringCreateCopy(alloc, str); + } else if (__CFStrIsEightBit(str)) { + const uint8_t *contents = __CFStrContents(str); + return __CFStringCreateImmutableFunnel3(alloc, contents + range.location + __CFStrSkipAnyLengthByte(str), range.length, __CFStringGetEightBitStringEncoding(), false, false, false, false, false, ALLOCATORSFREEFUNC, 0); + } else { + const UniChar *contents = __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(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(str) || __CFStrFreeContentsWhenDone(str) || str->base._rc == 0)) { // 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(str)) { + const uint8_t *contents = __CFStrContents(str); + return __CFStringCreateImmutableFunnel3(alloc, contents + __CFStrSkipAnyLengthByte(str), __CFStrLength2(str, contents), __CFStringGetEightBitStringEncoding(), false, false, false, false, false, ALLOCATORSFREEFUNC, 0); + } else { + const UniChar *contents = __CFStrContents(str); + return __CFStringCreateImmutableFunnel3(alloc, contents, __CFStrLength2(str, contents) * sizeof(UniChar), kCFStringEncodingUnicode, false, true, false, false, false, ALLOCATORSFREEFUNC, 0); + } +} + + + +/*** Constant string stuff... ***/ + +static CFMutableDictionaryRef constantStringTable = NULL; + +/* For now we call a function to create a constant string and keep previously created constant strings in a dictionary. The keys are the 8-bit constant C-strings from the compiler; the values are the CFStrings created for them. +*/ + +static CFStringRef __cStrCopyDescription(const void *ptr) { + return CFStringCreateWithCStringNoCopy(NULL, (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 unsigned char *cStr = (const unsigned char *)ptr; + CFIndex len = 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; +} + +#if defined(DEBUG) +/* We use a special allocator (which simply calls through to the default) for constant strings so that we can catch them being freed... +*/ +static void *csRealloc(void *oPtr, CFIndex size, CFOptionFlags hint, void *info) { + return CFAllocatorReallocate(NULL, oPtr, size, hint); +} + +static void *csAlloc(CFIndex size, CFOptionFlags hint, void *info) { + return CFAllocatorAllocate(NULL, size, hint); +} + +static void csDealloc(void *ptr, void *info) { + CFAllocatorDeallocate(NULL, ptr); +} + +static CFStringRef csCopyDescription(const void *info) { + return CFRetain(CFSTR("Debug allocator for CFSTRs")); +} +#endif + +static CFSpinLock_t _CFSTRLock = 0; + +CFStringRef __CFStringMakeConstantString(const char *cStr) { + CFStringRef result; + if (constantStringTable == NULL) { + CFDictionaryKeyCallBacks constantStringCallBacks = {0, NULL, NULL, __cStrCopyDescription, __cStrEqual, __cStrHash}; + constantStringTable = CFDictionaryCreateMutable(NULL, 0, &constantStringCallBacks, &kCFTypeDictionaryValueCallBacks); + _CFDictionarySetCapacity(constantStringTable, 2500); // avoid lots of rehashing +#if defined(DEBUG) + { + CFAllocatorContext context = {0, NULL, NULL, NULL, csCopyDescription, csAlloc, csRealloc, csDealloc, NULL}; + constantStringAllocatorForDebugging = CFAllocatorCreate(NULL, &context); + } +#else +#define constantStringAllocatorForDebugging NULL +#endif + } + + __CFSpinLock(&_CFSTRLock); + if ((result = (CFStringRef)CFDictionaryGetValue(constantStringTable, cStr))) { + __CFSpinUnlock(&_CFSTRLock); + } else { + __CFSpinUnlock(&_CFSTRLock); + + { +#if 0 +// This #if treats non-7 bit chars in CFSTR() as MacOSRoman, for backward compatibility + char *key; + Boolean isASCII = true; +//#warning Ali: Code to verify CFSTRs active, should be disabled before ship + const unsigned char *tmp = cStr; + while (*tmp) { + if (*tmp++ > 127) { + isASCII = false; + break; + } + } + + if (isASCII) result = CFStringCreateWithCString(constantStringAllocatorForDebugging, cStr, kCFStringEncodingASCII); + if (result == NULL) { + const char *log; + result = CFStringCreateWithCString(constantStringAllocatorForDebugging, cStr, kCFStringEncodingUTF8); + if (result == NULL) { + result = CFStringCreateWithCString(constantStringAllocatorForDebugging, cStr, kCFStringEncodingMacRoman); + if (result == NULL) { + log = "that are not UTF-8, crashing"; + } else { + log = "that are not UTF-8; treating as Mac OS Roman for now. FIX THIS!"; + } + } else { + log = "that seem to be UTF-8; please VERIFY"; + } + { + const unsigned char *tmp = cStr; + CFMutableStringRef ms = CFStringCreateMutable(NULL, 0); + while (*tmp) { + CFStringAppendFormat(ms, NULL, (*tmp > 127) ? CFSTR("\\%3o") : CFSTR("%1c"), *tmp); + tmp++; + } + CFLog(__kCFLogAssertion, CFSTR("CFSTR(\"%@\") has non-7 bit chars %s"), ms, log); + CFRelease(ms); + if (result == NULL) HALT; + } + } +#else +// This #else treats non-7 bit chars in CFSTR() as UTF8 first, and if that doesn't work, as MacOSRoman, for compatibility + char *key; + Boolean isASCII = true; +//#warning Ali: Code to verify CFSTRs active, should be disabled before ship + const unsigned char *tmp = cStr; + while (*tmp) { + if (*tmp++ > 127) { + isASCII = false; + break; + } + } + if (!isASCII) { + CFMutableStringRef ms = CFStringCreateMutable(NULL, 0); + tmp = cStr; + while (*tmp) { + CFStringAppendFormat(ms, NULL, (*tmp > 127) ? CFSTR("\\%3o") : CFSTR("%1c"), *tmp); + tmp++; + } + CFLog(0, 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); + } + result = CFStringCreateWithCString(constantStringAllocatorForDebugging, cStr, kCFStringEncodingMacRoman); + if (result == NULL) { + CFLog(__kCFLogAssertion, CFSTR("Can't interpret CFSTR() as MacOS Roman, crashing")); + HALT; + } +#endif + 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 = CFAllocatorAllocate(NULL, strlen(cStr) + 1, 0); + if (__CFOASafe) __CFSetLastAllocationEventName((void *)key, "CFString (CFSTR key)"); + strcpy(key, cStr); // !!! We will leak this, if the string is removed from the table (or table is freed) + } + + { +#if !defined(DEBUG) + CFStringRef resultToBeReleased = result; +#endif + 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); + } + __CFSpinUnlock(&_CFSTRLock); +#if !defined(DEBUG) + // Can't release this in the DEBUG case; will get assertion failure + CFRelease(resultToBeReleased); +#endif + } + } + } + return result; +} + +#if defined(__MACOS8__) || defined(__WIN32__) + +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 + } +#if defined(DEBUG) + CFAllocatorDeallocate( constantStringAllocatorForDebugging, (void*) constantStringAllocatorForDebugging ); +#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; + 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); + } + } +} + +// Can pass in NSString as replacement string + +static void __CFStringReplace(CFMutableStringRef str, CFRange range, CFStringRef replacement) { + 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 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(void *) + sizeof(UInt32) * 3 + (hasExternalContentsAllocator ? sizeof(CFAllocatorRef) : 0), NULL); + if (str) { + if (__CFOASafe) __CFSetLastAllocationEventName(str, "CFString (mutable)"); + + __CFStrSetInfoBits(str, __kCFIsMutable | additionalInfoBits); + str->variants.externalMutable.buffer = NULL; + __CFStrSetExplicitLength(str, 0); + str->variants.externalMutable.gapEtc = 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) ? __kCFHasExternalDataNoFree : __kCFHasContentsAllocator) : __kCFHasExternalDataDefaultFree; + CFMutableStringRef string = __CFStringCreateMutableFunnel(alloc, 0, contentsAllocationBits | __kCFIsExternalMutable | __kCFIsUnicode); + if (string) { + if (contentsAllocationBits == __kCFHasContentsAllocator) __CFStrSetContentsAllocator(string, CFRetain(externalCharactersAllocator)); + CFStringSetExternalCharactersNoCopy(string, chars, numChars, capacity); + } + return string; +} + +CFMutableStringRef CFStringCreateMutable(CFAllocatorRef alloc, CFIndex maxLength) { + return __CFStringCreateMutableFunnel(alloc, maxLength, __kCFHasExternalDataDefaultFree); +} + +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 + printf("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, __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 = __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, __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 = __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 = __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 = __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)) { + 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 = __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 = length; + return true; + } + } + + if (__CFStringEncodeByteStream(str, 0, length, false, encoding, false, (void*)(1 + (uint8_t*)buffer), bufferSize - 1, &usedLen) != length) { +#if defined(DEBUG) + if (bufferSize > 0) { + strncpy((char *)buffer + 1, CONVERSIONFAILURESTR, bufferSize - 1); + buffer[0] = (CFIndex)sizeof(CONVERSIONFAILURESTR) < (bufferSize - 1) ? (CFIndex)sizeof(CONVERSIONFAILURESTR) : (bufferSize - 1); + } +#else + if (bufferSize > 0) buffer[0] = 0; +#endif + return false; + } + *buffer = 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 = __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) + strncpy(buffer, CONVERSIONFAILURESTR, bufferSize); +#else + if (bufferSize > 0) buffer[0] = 0; +#endif + return false; + } + } +} + +#define MAX_CASE_MAPPING_BUF (8) + +/* Special casing for Uk sorting */ +#define DO_IGNORE_PUNCTUATION 1 +#if DO_IGNORE_PUNCTUATION +#define UKRAINIAN_LANG_CODE (45) +static bool __CFLocaleChecked = false; +static const uint8_t *__CFPunctSetBMP = NULL; +#endif /* DO_IGNORE_PUNCTUATION */ + +/* ??? We need to implement some additional flags here + ??? Also, pay attention to flag 2, which is the NS flag (which CF has as flag 16, w/opposite meaning). +*/ +CFComparisonResult CFStringCompareWithOptions(CFStringRef string, CFStringRef string2, CFRange rangeToCompare, CFOptionFlags compareOptions) { +/* No objc dispatch needed here since CFStringInlineBuffer works with both CFString and NSString */ + CFStringInlineBuffer strBuf1, strBuf2; + UTF32Char ch1, ch2; + const uint8_t *punctBMP = NULL; + Boolean caseInsensitive = (compareOptions & kCFCompareCaseInsensitive ? true : false); + Boolean decompose = (compareOptions & kCFCompareNonliteral ? true : false); + Boolean numerically = (compareOptions & kCFCompareNumerically ? true : false); + Boolean localized = (compareOptions & kCFCompareLocalized ? true : false); + +#if DO_IGNORE_PUNCTUATION + if (localized) { + if (!__CFLocaleChecked) { + CFArrayRef locales = _CFBundleCopyUserLanguages(false); + + if (locales && (CFArrayGetCount(locales) > 0)) { + SInt32 langCode; + + if (CFBundleGetLocalizationInfoForLocalization((CFStringRef)CFArrayGetValueAtIndex(locales, 0), &langCode, NULL, NULL, NULL) && (langCode == UKRAINIAN_LANG_CODE)) { + __CFPunctSetBMP = CFUniCharGetBitmapPtrForPlane(kCFUniCharPunctuationCharacterSet, 0); + } + + CFRelease(locales); + } + __CFLocaleChecked = true; + } + + punctBMP = __CFPunctSetBMP; + } +#endif /* DO_IGNORE_PUNCTUATION */ + + CFStringInitInlineBuffer(string, &strBuf1, CFRangeMake(rangeToCompare.location, rangeToCompare.length)); + CFIndex strBuf1_idx = 0; + CFIndex string2_len = CFStringGetLength(string2); + CFStringInitInlineBuffer(string2, &strBuf2, CFRangeMake(0, string2_len)); + CFIndex strBuf2_idx = 0; + + while (strBuf1_idx < rangeToCompare.length && strBuf2_idx < string2_len) { + ch1 = CFStringGetCharacterFromInlineBuffer(&strBuf1, strBuf1_idx); + ch2 = CFStringGetCharacterFromInlineBuffer(&strBuf2, strBuf2_idx); + + if (numerically && (ch1 <= '9' && ch1 >= '0') && (ch2 <= '9' && ch2 >= '0')) { // If both are not digits, then don't do numerical comparison + unsigned long long n1 = 0; // !!! Doesn't work if numbers are > max unsigned long long + unsigned long long n2 = 0; + do { + n1 = n1 * 10 + (ch1 - '0'); + strBuf1_idx++; + if (rangeToCompare.length <= strBuf1_idx) break; + ch1 = CFStringGetCharacterFromInlineBuffer(&strBuf1, strBuf1_idx); + } while (ch1 <= '9' && ch1 >= '0'); + do { + n2 = n2 * 10 + (ch2 - '0'); + strBuf2_idx++; + if (string2_len <= strBuf2_idx) break; + ch2 = CFStringGetCharacterFromInlineBuffer(&strBuf2, strBuf2_idx); + } while (ch2 <= '9' && ch2 >= '0'); + if (n1 < n2) return kCFCompareLessThan; else if (n1 > n2) return kCFCompareGreaterThan; + continue; // If numbers were equal, go back to top without incrementing the buffer pointers + } + + if (CFUniCharIsSurrogateHighCharacter(ch1)) { + strBuf1_idx++; + if (strBuf1_idx < rangeToCompare.length && CFUniCharIsSurrogateLowCharacter(CFStringGetCharacterFromInlineBuffer(&strBuf1, strBuf1_idx))) { + ch1 = CFUniCharGetLongCharacterForSurrogatePair(ch1, CFStringGetCharacterFromInlineBuffer(&strBuf1, strBuf1_idx)); + } else { + strBuf1_idx--; + } + } + if (CFUniCharIsSurrogateHighCharacter(ch2)) { + strBuf2_idx++; + if (strBuf2_idx < string2_len && CFUniCharIsSurrogateLowCharacter(CFStringGetCharacterFromInlineBuffer(&strBuf2, strBuf2_idx))) { + ch2 = CFUniCharGetLongCharacterForSurrogatePair(ch2, CFStringGetCharacterFromInlineBuffer(&strBuf2, strBuf2_idx)); + } else { + strBuf2_idx--; + } + } + + if (ch1 != ch2) { +#if DO_IGNORE_PUNCTUATION + if (punctBMP) { + if (CFUniCharIsMemberOfBitmap(ch1, (ch1 < 0x10000 ? punctBMP : CFUniCharGetBitmapPtrForPlane(kCFUniCharPunctuationCharacterSet, (ch1 >> 16))))) { + ++strBuf1_idx; continue; + } + if (CFUniCharIsMemberOfBitmap(ch2, (ch2 < 0x10000 ? punctBMP : CFUniCharGetBitmapPtrForPlane(kCFUniCharPunctuationCharacterSet, (ch2 >> 16))))) { + ++strBuf2_idx; continue; + } + } +#endif /* DO_IGNORE_PUNCTUATION */ + // We standardize to lowercase here since currently, as of Unicode 3.1.1, it's one-to-one mapping. + // Note we map to uppercase for both SMALL LETTER SIGMA and SMALL LETTER FINAL SIGMA + if (caseInsensitive) { + if (ch1 < 128) { + ch1 -= ((ch1 >= 'A' && ch1 <= 'Z') ? 'A' - 'a' : 0); + } else if (ch1 == 0x03C2 || ch1 == 0x03C3 || ch1 == 0x03A3) { // SMALL SIGMA + ch1 = 0x03A3; + } else { + UniChar buffer[MAX_CASE_MAPPING_BUF]; + + if (CFUniCharMapCaseTo(ch1, buffer, MAX_CASE_MAPPING_BUF, kCFUniCharToLowercase, 0, NULL) > 1) { // It's supposed to be surrogates + ch1 = CFUniCharGetLongCharacterForSurrogatePair(buffer[0], buffer[1]); + } else { + ch1 = *buffer; + } + } + if (ch2 < 128) { + ch2 -= ((ch2 >= 'A' && ch2 <= 'Z') ? 'A' - 'a' : 0); + } else if (ch2 == 0x03C2 || ch2 == 0x03C3 || ch2 == 0x03A3) { // SMALL SIGMA + ch2 = 0x03A3; + } else { + UniChar buffer[MAX_CASE_MAPPING_BUF]; + + if (CFUniCharMapCaseTo(ch2, buffer, MAX_CASE_MAPPING_BUF, kCFUniCharToLowercase, 0, NULL) > 1) { // It's supposed to be surrogates + ch2 = CFUniCharGetLongCharacterForSurrogatePair(buffer[0], buffer[1]); + } else { + ch2 = *buffer; + } + } + } + + if (ch1 != ch2) { // still different + if (decompose) { // ??? This is not exactly the canonical comparison (We need to do priority sort) + Boolean isCh1Decomposable = (ch1 > 0x7F && CFUniCharIsMemberOf(ch1, kCFUniCharDecomposableCharacterSet)); + Boolean isCh2Decomposable = (ch2 > 0x7F && CFUniCharIsMemberOf(ch2, kCFUniCharDecomposableCharacterSet)); + + if (isCh1Decomposable != isCh2Decomposable) { + UTF32Char decomposedCharater[MAX_DECOMPOSED_LENGTH]; + UInt32 decomposedCharacterLength; + UInt32 idx; + + if (isCh1Decomposable) { + decomposedCharacterLength = CFUniCharDecomposeCharacter(ch1, decomposedCharater, MAX_DECOMPOSED_LENGTH); + for (idx = 0; idx < decomposedCharacterLength && strBuf2_idx < string2_len; idx++) { + ch1 = decomposedCharater[idx]; + if (ch1 < ch2) return kCFCompareLessThan; else if (ch1 > ch2) return kCFCompareGreaterThan; + strBuf2_idx++; ch2 = (strBuf2_idx < string2_len ? CFStringGetCharacterFromInlineBuffer(&strBuf2, strBuf2_idx) : 0xffff); + if (CFUniCharIsSurrogateHighCharacter(ch2)) { + strBuf2_idx++; + if (strBuf2_idx < string2_len && CFUniCharIsSurrogateLowCharacter(CFStringGetCharacterFromInlineBuffer(&strBuf2, strBuf2_idx))) { + ch2 = CFUniCharGetLongCharacterForSurrogatePair(ch2, CFStringGetCharacterFromInlineBuffer(&strBuf2, strBuf2_idx)); + } else { + strBuf2_idx--; + } + } + } + strBuf1_idx++; continue; + } else { // ch2 is decomposable, then + decomposedCharacterLength = CFUniCharDecomposeCharacter(ch2, decomposedCharater, MAX_DECOMPOSED_LENGTH); + for (idx = 0; idx < decomposedCharacterLength && strBuf1_idx < rangeToCompare.length; idx++) { + ch2 = decomposedCharater[idx]; + if (ch1 < ch2) return kCFCompareLessThan; else if (ch1 > ch2) return kCFCompareGreaterThan; + strBuf1_idx++; ch1 = (strBuf1_idx < rangeToCompare.length ? CFStringGetCharacterFromInlineBuffer(&strBuf1, strBuf1_idx) : 0xffff); + if (CFUniCharIsSurrogateHighCharacter(ch1)) { + strBuf1_idx++; + if (strBuf1_idx < rangeToCompare.length && CFUniCharIsSurrogateLowCharacter(CFStringGetCharacterFromInlineBuffer(&strBuf1, strBuf1_idx))) { + ch1 = CFUniCharGetLongCharacterForSurrogatePair(ch1, CFStringGetCharacterFromInlineBuffer(&strBuf1, strBuf1_idx)); + } else { + strBuf1_idx--; + } + } + } + strBuf2_idx++; continue; + } + } + } + if (ch1 < ch2) return kCFCompareLessThan; else if (ch1 > ch2) return kCFCompareGreaterThan; + } + } + strBuf1_idx++; strBuf2_idx++; + } + if (strBuf1_idx < rangeToCompare.length) { + return kCFCompareGreaterThan; + } else if (strBuf2_idx < string2_len) { + return kCFCompareLessThan; + } else { + return kCFCompareEqualTo; + } +} + + +CFComparisonResult CFStringCompare(CFStringRef string, CFStringRef str2, CFOptionFlags options) { + return CFStringCompareWithOptions(string, str2, CFRangeMake(0, CFStringGetLength(string)), options); +} + +/* ??? Need to implement localized find +*/ +Boolean CFStringFindWithOptions(CFStringRef string, CFStringRef stringToFind, CFRange rangeToSearch, CFOptionFlags compareOptions, CFRange *result) { + /* No objc dispatch needed here since CFStringInlineBuffer works with both CFString and NSString */ + int step; + SInt32 fromLoc, toLoc; // fromLoc and toLoc are inclusive + SInt32 cnt, findStrLen = CFStringGetLength(stringToFind); + Boolean done = false; + Boolean caseInsensitive = (compareOptions & kCFCompareCaseInsensitive) ? true : false; + Boolean decompose = (compareOptions & kCFCompareNonliteral) ? true : false; + UniChar tmpBuf[MAXTMPBUFFERLEN]; + UniChar *findBuf; + UTF32Char ch1, ch2; + Boolean isDecompBuf[MAXISDECOMPBUFFERLEN]; + CFStringInlineBuffer buf; + CFAllocatorRef tmpAlloc = NULL; + + if (findStrLen == 0) return false; // This is the way it is, by definition (even find("", "") -> false) + if (!decompose && findStrLen > rangeToSearch.length) return false; + if (rangeToSearch.length == 0) return false; // This protects against crashes further below (see 2908472); if we ever implement ignorable characters, this shouldn't be here + + findBuf = (findStrLen > MAXTMPBUFFERLEN) ? CFAllocatorAllocate(tmpAlloc = __CFGetDefaultAllocator(), findStrLen * sizeof(UniChar), 0) : tmpBuf; + if (findBuf != tmpBuf && __CFOASafe) __CFSetLastAllocationEventName(findBuf, "CFString (temp)"); + CFStringGetCharacters(stringToFind, CFRangeMake(0, findStrLen), findBuf); + + if (decompose) { + SInt32 max = __CFMin(MAXISDECOMPBUFFERLEN, findStrLen); + + for (cnt = 0;cnt < max;cnt++) { + if (CFUniCharIsSurrogateHighCharacter(findBuf[cnt]) && (cnt + 1 < max) && CFUniCharIsSurrogateLowCharacter(findBuf[cnt + 1])) { + isDecompBuf[cnt] = isDecompBuf[cnt + 1] = CFUniCharIsMemberOf(CFUniCharGetLongCharacterForSurrogatePair(findBuf[cnt], findBuf[cnt + 1]), kCFUniCharDecomposableCharacterSet); + ++cnt; + } else { + isDecompBuf[cnt] = (findBuf[cnt] > 0x7F && CFUniCharIsMemberOf(findBuf[cnt], kCFCharacterSetDecomposable)); + } + } + } + + if (caseInsensitive) { /* Lower case the search string */ + for (cnt = 0; cnt < findStrLen; cnt++) { + ch1 = findBuf[cnt]; + if (ch1 < 128) { + if (ch1 >= 'A' && ch1 <= 'Z') findBuf[cnt] = (ch1 - 'A' + 'a'); /* Lower case the cheap way */ + } else if (ch1 == 0x03C2 || ch1 == 0x03C3 || ch1 == 0x03A3) { // SMALL SIGMA + findBuf[cnt] = 0x03A3; + } else { + UniChar buffer[MAX_CASE_MAPPING_BUF]; + + if (CFUniCharIsSurrogateHighCharacter(ch1) && (cnt + 1 < findStrLen) && CFUniCharIsSurrogateLowCharacter(findBuf[cnt + 1])) { + ch1 = CFUniCharGetLongCharacterForSurrogatePair(ch1, findBuf[cnt + 1]); + } + if (CFUniCharMapCaseTo(ch1, buffer, MAX_CASE_MAPPING_BUF, kCFUniCharToLowercase, 0, NULL) > 1) { // It's supposed to be surrogates + findBuf[cnt] = buffer[0]; + findBuf[++cnt] = buffer[1]; + } else { + findBuf[cnt] = *buffer; + } + } + } + } + + if (compareOptions & kCFCompareBackwards) { + fromLoc = rangeToSearch.location + rangeToSearch.length - (decompose ? 1 : findStrLen); + toLoc = ((compareOptions & kCFCompareAnchored) && !decompose ? fromLoc : rangeToSearch.location); + } else { + fromLoc = rangeToSearch.location; + toLoc = ((compareOptions & kCFCompareAnchored) ? fromLoc : rangeToSearch.location + rangeToSearch.length - (decompose ? 1 : findStrLen)); + } + + step = (fromLoc <= toLoc) ? 1 : -1; + cnt = fromLoc; + CFStringInitInlineBuffer(string, &buf, CFRangeMake(0, rangeToSearch.location + rangeToSearch.length)); + CFIndex buf_idx = fromLoc, buf_idx_end = rangeToSearch.location + rangeToSearch.length; + + do { + CFIndex chCnt; + for (chCnt = 0; chCnt < findStrLen; chCnt++) { + ch2 = buf_idx < buf_idx_end ? CFStringGetCharacterFromInlineBuffer(&buf, buf_idx) : 0xffff; + if (decompose && ch2 == 0xffff) break; + + if (caseInsensitive) { + if (CFUniCharIsSurrogateHighCharacter(ch2)) { + buf_idx++; + if (buf_idx < buf_idx_end && CFUniCharIsSurrogateLowCharacter(CFStringGetCharacterFromInlineBuffer(&buf, buf_idx))) { + ch2 = CFUniCharGetLongCharacterForSurrogatePair(ch2, CFStringGetCharacterFromInlineBuffer(&buf, buf_idx)); + } else { + buf_idx--; + } + } + + if (ch2 < 128) { + if (ch2 >= 'A' && ch2 <= 'Z') ch2 = (ch2 - 'A' + 'a'); /* Lower case the cheap way */ + } else if (ch2 == 0x03C2 || ch2 == 0x03C3 || ch2 == 0x03A3) { // SMALL SIGMA + ch2 = 0x03A3; + } else { + UniChar buffer[MAX_CASE_MAPPING_BUF]; + + if (CFUniCharMapCaseTo(ch2, buffer, MAX_CASE_MAPPING_BUF, kCFUniCharToLowercase, 0, NULL) > 1) { // It's supposed to be surrogates + ch2 = CFUniCharGetLongCharacterForSurrogatePair(buffer[0], buffer[1]); + } else { + ch2 = *buffer; + } + } + } + if (decompose) { + if (CFUniCharIsSurrogateHighCharacter(ch2)) { + buf_idx++; + if (buf_idx < buf_idx_end && CFUniCharIsSurrogateLowCharacter(CFStringGetCharacterFromInlineBuffer(&buf, buf_idx))) { + ch2 = CFUniCharGetLongCharacterForSurrogatePair(ch2, CFStringGetCharacterFromInlineBuffer(&buf, buf_idx)); + } else { + buf_idx--; + } + } + + if (CFUniCharIsSurrogateHighCharacter(findBuf[chCnt]) && (chCnt + 1 < findStrLen) && CFUniCharIsSurrogateLowCharacter(findBuf[chCnt + 1])) { + ch1 = CFUniCharGetLongCharacterForSurrogatePair(findBuf[chCnt], findBuf[chCnt + 1]); + ++chCnt; + } else { + ch1 = findBuf[chCnt]; + } + + if (ch1 != ch2) { // ??? This is not exactly the canonical comparison. Needs to be addressed by Cheetah. + Boolean isCh1Decomposable = (chCnt < MAXISDECOMPBUFFERLEN ? isDecompBuf[chCnt] : (ch1 > 0x7F && CFUniCharIsMemberOf(ch1, kCFUniCharDecomposableCharacterSet))); + Boolean isCh2Decomposable = (ch2 > 0x7F && CFUniCharIsMemberOf(ch2, kCFUniCharDecomposableCharacterSet)); + + if (isCh1Decomposable != isCh2Decomposable) { + UTF32Char decomposedCharater[MAX_DECOMPOSED_LENGTH]; + UInt32 decomposedCharacterLength; + UInt32 idx; + + if (isCh1Decomposable) { + decomposedCharacterLength = CFUniCharDecomposeCharacter(ch1, decomposedCharater, MAX_DECOMPOSED_LENGTH); + for (idx = 0; idx < decomposedCharacterLength && buf_idx < buf_idx_end; idx++) { + if (decomposedCharater[idx] != ch2) break; + + buf_idx++; ch2 = buf_idx < buf_idx_end ? CFStringGetCharacterFromInlineBuffer(&buf, buf_idx) : 0xffff; + if (CFUniCharIsSurrogateHighCharacter(ch2)) { + buf_idx++; + if (buf_idx < buf_idx_end && CFUniCharIsSurrogateLowCharacter(CFStringGetCharacterFromInlineBuffer(&buf, buf_idx))) { + ch2 = CFUniCharGetLongCharacterForSurrogatePair(ch2, CFStringGetCharacterFromInlineBuffer(&buf, buf_idx)); + } else { + buf_idx--; + } + } + } + if (idx < decomposedCharacterLength) break; + continue; + } else { // ch2 is decomposable, then + int32_t foundLen = chCnt; + + decomposedCharacterLength = CFUniCharDecomposeCharacter(ch2, decomposedCharater, MAX_DECOMPOSED_LENGTH); + for (idx = 0;idx < decomposedCharacterLength && foundLen < findStrLen;idx++) { + if (CFUniCharIsSurrogateHighCharacter(findBuf[foundLen]) && ((foundLen + 1) < findStrLen) && CFUniCharIsSurrogateLowCharacter(findBuf[foundLen + 1])) { + if (CFUniCharGetLongCharacterForSurrogatePair(findBuf[foundLen], findBuf[foundLen + 1]) != decomposedCharater[idx]) break; + ++foundLen; + } else { + if (findBuf[foundLen] != decomposedCharater[idx]) break; + } + ++foundLen; + } + if (idx < decomposedCharacterLength) break; + chCnt = foundLen - 1; // Decrement so we can back up + buf_idx++; continue; + } + } + break; + } + } else { + if (ch2 > 0xFFFF) { // Non-BMP + if (CFUniCharIsSurrogateHighCharacter(findBuf[chCnt]) && (chCnt + 1 < findStrLen) && CFUniCharIsSurrogateLowCharacter(findBuf[chCnt + 1])) { + if (ch2 != CFUniCharGetLongCharacterForSurrogatePair(findBuf[chCnt], findBuf[chCnt + 1])) break; + ++chCnt; + } else { + break; + } + } else { + if (findBuf[chCnt] != ch2) break; + } + } + buf_idx++; + } + if (chCnt == findStrLen) { + if (decompose && (buf_idx < buf_idx_end)) { + if ((compareOptions & kCFCompareAnchored) && (compareOptions & kCFCompareBackwards)) break; + + ch2 = CFStringGetCharacterFromInlineBuffer(&buf, buf_idx); + + if (CFUniCharIsSurrogateHighCharacter(ch2)) { + if ((buf_idx + 1) < buf_idx_end && CFUniCharIsSurrogateLowCharacter(CFStringGetCharacterFromInlineBuffer(&buf, buf_idx + 1))) { + ch2 = CFUniCharGetLongCharacterForSurrogatePair(ch2, CFStringGetCharacterFromInlineBuffer(&buf, buf_idx + 1)); + } + } + if (ch2 > 0x7F && CFUniCharIsMemberOf(ch2, kCFUniCharNonBaseCharacterSet)) continue; // Next char is non-base + } + done = true; + if (result) { + result->location = cnt; + result->length = (decompose ? buf_idx - cnt : findStrLen); + } + } else if (cnt == toLoc) { + break; + } else { + cnt += step; + buf_idx = cnt; + } + } while (!done); + + if (findBuf != tmpBuf) CFAllocatorDeallocate(tmpAlloc, findBuf); + + return done; +} + + +// 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(NULL /* ??? allocator */, 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; + 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 ZERO_WIDTH_JOINER (0x200D) +#define COMBINING_GRAPHEME_JOINER (0x034F) +#define MAX_TRANSCODING_LENGTH 4 + +// 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) + +#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 *nonBaseBMP) { + CFIndex end = start + 1; + const uint8_t *nonBase = nonBaseBMP; + 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); + nonBase = CFUniCharGetBitmapPtrForPlane(kCFUniCharNonBaseCharacterSet, (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); + nonBase = CFUniCharGetBitmapPtrForPlane(kCFUniCharNonBaseCharacterSet, (character >> 16)); + --start; + } else { + nonBase = nonBaseBMP; + } + } + + if (!CFUniCharIsMemberOfBitmap(character, nonBase) && (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); + nonBase = CFUniCharGetBitmapPtrForPlane(kCFUniCharNonBaseCharacterSet, (character >> 16)); + step = 2; + } else { + nonBase = nonBaseBMP; + step = 1; + } + + if (!CFUniCharIsMemberOfBitmap(character, nonBase) && (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, ((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); + CFStringInlineBuffer stringBuffer; + UTF32Char character; + UTF16Char otherSurrogate; + static const uint8_t *nonBaseBMP = NULL; + static const uint8_t *letterBMP = NULL; + static const uint8_t *combClassBMP = NULL; + + 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); + + if (NULL == nonBaseBMP) { + nonBaseBMP = CFUniCharGetBitmapPtrForPlane(kCFUniCharNonBaseCharacterSet, 0); + letterBMP = CFUniCharGetBitmapPtrForPlane(kCFUniCharLetterCharacterSet, 0); + combClassBMP = CFUniCharGetUnicodePropertyDataForPlane(kCFUniCharCombiningProperty, 0); + } + + CFStringInitInlineBuffer(string, &stringBuffer, CFRangeMake(0, length)); + + // Get composed character sequence first + range = _CFStringInlineBufferGetComposedRange(&stringBuffer, charIndex, type, nonBaseBMP); + + // 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, nonBaseBMP).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; + + do { + currentIndex = otherRange.location + otherRange.length; + 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, nonBaseBMP); + } while ((otherRange.location + otherRange.length) < length); + range.length = currentIndex - range.location; + } + } + } + + // Check if we're part of prefix transcoding hints + if (range.location > 0) { + 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)) { + 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(0, 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; + 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); + + 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) && CFCharacterSetIsLongCharacterMember(theSet, 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 (CFCharacterSetIsCharacterMember(theSet, 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) { + if (ch > CarriageReturn && ch < NextLine) return false; /* Quick test to cover most chars */ + return (ch == NewLine || ch == CarriageReturn || ch == NextLine || ch == LineSeparator || ch == ParaSeparator) ? true : false; +} + +void CFStringGetLineBounds(CFStringRef string, CFRange range, CFIndex *lineBeginIndex, CFIndex *lineEndIndex, CFIndex *contentsEndIndex) { + CFIndex len; + CFStringInlineBuffer buf; + UniChar ch; + + CF_OBJC_FUNCDISPATCH4(__kCFStringTypeID, void, string, "getLineStart:end:contentsEnd:forRange:", lineBeginIndex, lineEndIndex, contentsEndIndex, CFRangeMake(range.location, range.length)); + + __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))) { + 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)) { + 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; + } +} + + +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 CFStringCreateCopy(alloc, 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; + } + + bufPtr = buffer = CFAllocatorAllocate(alloc, canBeEightbit ? ((numChars + 1) * sizeof(uint8_t)) : (numChars * sizeof(UniChar)), 0); + 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 = __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, buffer, __CFStringGetEightBitStringEncoding(), alloc) : + CFStringCreateWithCharactersNoCopy(alloc, 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 = 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, ((char *)__CFStrContents(string) + __CFStrSkipAnyLengthByte(string)), __CFStrLength(string)); + } + } + + if (alloc == NULL) alloc = __CFGetDefaultAllocator(); + + if (encoding == kCFStringEncodingUnicode) { + guessedByteLength = (length + 1) * sizeof(UniChar); + } else if (((guessedByteLength = CFStringGetMaximumSizeForEncoding(length, encoding)) > length) && !CF_IS_OBJC(__kCFStringTypeID, string)) { // Multi byte encoding +#if defined(__MACH__) || defined(__LINUX__) || defined(__FREEBSD__) + if (__CFStrIsUnicode(string)) { + guessedByteLength = CFStringEncodingByteLengthForCharacters(encoding, kCFStringEncodingPrependBOM, __CFStrContents(string), __CFStrLength(string)); + } else { +#endif + result = __CFStringEncodeByteStream(string, 0, length, true, encoding, lossByte, NULL, 0x7FFFFFFF, &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, ((char *)__CFStrContents(string) + __CFStrSkipAnyLengthByte(string)), __CFStrLength(string)); + } +#if defined(__MACH__) || defined(__LINUX__) || defined(__FREEBSD__) + } +#endif + } + bytes = 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, (char const *)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, 0x7fffffff, NULL) == len) return __CFStringGetEightBitStringEncoding(); + if ((__CFStringGetEightBitStringEncoding() != __CFStringGetSystemEncoding()) && (__CFStringEncodeByteStream(str, 0, len, false, __CFStringGetSystemEncoding(), 0, NULL, 0x7fffffff, 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(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 = 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(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, pStr + 1, (CFIndex)*pStr, encoding); +} + +void CFStringAppendCString(CFMutableStringRef str, const char *cStr, CFStringEncoding encoding) { + __CFStringAppendBytes(str, cStr, 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; + 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 = CFAllocatorReallocate(NULL, 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(NULL, 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 + if ((unsigned)range.location + (unsigned)range.length > (unsigned)__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 _CFExecutableLinkedOnOrAfter(CFSystemVersionJaguar) ? true : false; +} + + + +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 char *langCode; + Boolean isEightBit = __CFStrIsEightBit(string); + + CF_OBJC_FUNCDISPATCH1(__kCFStringTypeID, void, string, "_cfLowercase:", locale); + + __CFAssertIsStringAndMutable(string); + + length = __CFStrLength(string); + + langCode = 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 || (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 char *langCode; + Boolean isEightBit = __CFStrIsEightBit(string); + + CF_OBJC_FUNCDISPATCH1(__kCFStringTypeID, void, string, "_cfUppercase:", locale); + + __CFAssertIsStringAndMutable(string); + + length = __CFStrLength(string); + + langCode = 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 char *langCode; + Boolean isEightBit = __CFStrIsEightBit(string); + Boolean isLastCased = false; + static const uint8_t *caseIgnorableForBMP = NULL; + + CF_OBJC_FUNCDISPATCH1(__kCFStringTypeID, void, string, "_cfCapitalize:", locale); + + __CFAssertIsStringAndMutable(string); + + length = __CFStrLength(string); + + if (NULL == caseIgnorableForBMP) caseIgnorableForBMP = CFUniCharGetBitmapPtrForPlane(kCFUniCharCaseIgnorableCharacterSet, 0); + + langCode = 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; + + 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 (CFUniCharIsMemberOf(currentChar, kCFUniCharCanonicalDecomposableCharacterSet) && !CFUniCharIsMemberOf(currentChar, kCFUniCharNonBaseCharacterSet)) { + 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 (CFUniCharIsMemberOf(currentChar, kCFUniCharNonBaseCharacterSet)) { + 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 (CFUniCharIsMemberOf(currentChar, kCFUniCharDecomposableCharacterSet)) { // 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 (!CFUniCharIsMemberOf(currentChar, kCFUniCharNonBaseCharacterSet)) break; + if (currentChar & 0xFFFF0000) { + contents += 2; + currentLength += 2; + } else { + ++contents; + ++currentLength; + } + if (mappedLength == allocatedLength) { + allocatedLength += MAX_DECOMP_BUF; + if (mappedCharacters == buffer) { + mappedCharacters = (UTF32Char *)CFAllocatorAllocate(NULL, allocatedLength * sizeof(UTF32Char), 0); + memmove(mappedCharacters, buffer, MAX_DECOMP_BUF * sizeof(UTF32Char)); + } else { + mappedCharacters = (UTF32Char *)CFAllocatorReallocate(NULL, mappedCharacters, allocatedLength * sizeof(UTF32Char), 0); + } + } + if (CFUniCharIsMemberOf(currentChar, kCFUniCharDecomposableCharacterSet)) { // 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(NULL, allocatedLength * sizeof(UTF32Char), 0); + memmove(mappedCharacters, buffer, MAX_DECOMP_BUF * sizeof(UTF32Char)); + } else { + mappedCharacters = (UTF32Char *)CFAllocatorReallocate(NULL, mappedCharacters, allocatedLength * sizeof(UTF32Char), 0); + } + } + } + mappedLength = newLength; + } + + if (theForm & kCFStringNormalizationFormC) { + if (mappedLength > 1) { + CFIndex consumedLength = 1; + UTF32Char nextChar; + UTF32Char *currentBase = mappedCharacters; + uint8_t currentClass, lastClass = 0; + const uint8_t *bmpClassTable = CFUniCharGetUnicodePropertyDataForPlane(kCFUniCharCombiningProperty, 0); + bool didCombine = false; + + currentChar = *mappedCharacters; + + while (consumedLength < mappedLength) { + nextChar = mappedCharacters[consumedLength]; + currentClass = (nextChar & 0xFFFF0000 ? CFUniCharGetUnicodeProperty(nextChar, kCFUniCharCombiningProperty) : CFUniCharGetCombiningPropertyForCharacter(nextChar, bmpClassTable)); + + 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 (!CFUniCharIsMemberOf(nextChar, kCFUniCharNonBaseCharacterSet)) { + *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; + lastClass = 0; + } + } + ++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; + } + } + } + } + + 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(NULL, mappedCharacters); + } +} + +#define POSIX_SEPARATOR "/" + +CF_INLINE void __CFStringReplacePathSeparator(CFMutableStringRef string, const char from, const char to) { + uint8_t *contents = (uint8_t*)__CFStrContents(string) + __CFStrSkipAnyLengthByte(string); + CFIndex length = __CFStrLength2(string, contents); + bool isUnicode = __CFStrIsUnicode(string); + CFIndex idx; + + for (idx = 0;idx < length;idx++) { + if ((isUnicode ? ((UniChar*)contents)[idx] : ((uint8_t*)contents)[idx]) == from) { + if (isUnicode) { + ((UniChar*)contents)[idx] = to; + } else { + ((uint8_t*)contents)[idx] = to; + } + } + } +} + +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 longlongValue; + double doubleValue; + void *pointerValue; + } value; +} CFPrintValue; + +enum { + CFFormatDefaultSize = 0, + CFFormatSize1 = 1, + CFFormatSize2 = 2, + CFFormatSize4 = 3, + CFFormatSize8 = 4, + CFFormatSize16 = 5, /* unused */ +}; + +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 */ +}; + +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 = CFFormatSize4; + break; + case 'q': + 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; + return; + case 'e': case 'E': case 'f': case 'g': case 'G': + spec->type = CFFormatDoubleType; + spec->size = CFFormatSize8; + return; + case 'n': case 'p': /* %n is not handled correctly currently */ + spec->type = CFFormatPointerType; + spec->size = CFFormatSize4; + return; + case 's': + spec->type = CFFormatCharsType; + spec->size = CFFormatSize4; + return; + case 'S': + spec->type = CFFormatUnicharsType; + spec->size = CFFormatSize4; + return; + case 'C': + spec->type = CFFormatSingleUnicharType; + spec->size = CFFormatSize2; + return; + case 'P': + spec->type = CFFormatPascalCharsType; + spec->size = CFFormatSize4; + return; + case '@': + spec->type = CFFormatCFType; + spec->size = CFFormatSize4; + 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 = number - 1; // Arg numbers start from 1 + } else if (-2 == spec->widthArgNum) { + spec->widthArgNum = number - 1; // Arg numbers start from 1 + } else { + spec->mainArgNum = 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; + } + } +} + +#if defined(__MACOS8__) +static int snprintf (char *b, size_t n, const char * f, ...) { + int retval; + va_list args; + va_start (args, f); + retval = vsprintf(b, f, args); + va_end(args); + return retval; +} +#elif defined(__WIN32__) +static int snprintf (char *b, size_t n, const char * f, ...) { + int retval; + va_list args; + va_start (args, f); + retval = _vsnprintf(b, n, f, args); + va_end(args); + return retval; +} +#endif + +/* ??? 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); +} + +#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); \ + } \ + }} + +void _CFStringAppendFormatAndArgumentsAux(CFMutableStringRef outputString, CFStringRef (*copyDescFunc)(void *, CFDictionaryRef), 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; + + 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 = __CFStrContents(formatString); + if (cformat) cformat += __CFStrSkipAnyLengthByte(formatString); + } else { + uformat = __CFStrContents(formatString); + } + } + if (!cformat && !uformat) { + formatChars = (formatLen > FORMAT_BUFFER_LEN) ? 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) ? 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; + +// printf("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) ? 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.longlongValue = (int64_t)(char)va_arg(args, int); + } else if (CFFormatSize2 == values[argNum].size) { + values[argNum].value.longlongValue = (int64_t)(short)va_arg(args, int); + } else if (CFFormatSize4 == values[argNum].size) { + values[argNum].value.longlongValue = (int64_t)va_arg(args, long); + } else if (CFFormatSize8 == values[argNum].size) { + values[argNum].value.longlongValue = (int64_t)va_arg(args, int64_t); + } else { + values[argNum].value.longlongValue = (int64_t)va_arg(args, int); + } + break; + case CFFormatDoubleType: + 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; + } + } + 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.longlongValue; + hasWidth = true; + } + if (-1 != specs[curSpec].precArgNum) { + precision = (SInt32)values[specs[curSpec].precArgNum].value.longlongValue; + 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: { + int8_t formatBuffer[128]; + int8_t buffer[256 + width + precision]; + 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.longlongValue) + } else { + SNPRINTF(SInt32, values[specs[curSpec].mainArgNum].value.longlongValue) + } + break; + case CFFormatPointerType: + SNPRINTF(void *, values[specs[curSpec].mainArgNum].value.pointerValue) + break; + + case CFFormatDoubleType: + SNPRINTF(double, values[specs[curSpec].mainArgNum].value.doubleValue) + // See if we need to localize the decimal point + if (formatOptions) { // We have a localization dictionary + CFStringRef decimalSeparator = CFDictionaryGetValue(formatOptions, kCFNSDecimalSeparatorKey); + 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, buffer, __CFStringGetEightBitStringEncoding()); + CFStringAppend(outputString, decimalSeparator); + CFStringAppendCString(outputString, buffer + decimalPointLoc + 1, __CFStringGetEightBitStringEncoding()); + appended = true; + } + } + } + break; + } + if (!appended) CFStringAppendCString(outputString, buffer, __CFStringGetEightBitStringEncoding()); + } + break; + case CFFormatLiteralType: + if (cformat) { + __CFStringAppendBytes(outputString, 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 = 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 = 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 = 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 = values[specs[curSpec].mainArgNum].value.longlongValue; + CFStringAppendCharacters(outputString, &ch, 1); + break; + case CFFormatUnicharsType: + //??? need to handle width, precision, and padding arguments + up = 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) { + printf ("(null)\n"); + return; + } + + if (CF_IS_OBJC(__kCFStringTypeID, str)) { + printf ("This is an NSString, not CFString\n"); + return; + } + + alloc = CFGetAllocator(str); + + printf ("\nLength %d\nIsEightBit %d\n", (int)__CFStrLength(str), __CFStrIsEightBit(str)); + printf ("HasLengthByte %d\nHasNullByte %d\nInlineContents %d\n", + __CFStrHasLengthByte(str), __CFStrHasNullByte(str), __CFStrIsInline(str)); + + printf ("Allocator "); + if (alloc != kCFAllocatorSystemDefault) { + printf ("%p\n", (void *)alloc); + } else { + printf ("SystemDefault\n"); + } + printf ("Mutable %d\n", __CFStrIsMutable(str)); + if (!__CFStrIsMutable(str) && __CFStrHasContentsDeallocator(str)) { + if (__CFStrContentsDeallocator(str)) printf ("ContentsDeallocatorFunc %p\n", (void *)__CFStrContentsDeallocator(str)); + else printf ("ContentsDeallocatorFunc None\n"); + } else if (__CFStrIsMutable(str) && __CFStrHasContentsAllocator(str)) { + printf ("ExternalContentsAllocator %p\n", (void *)__CFStrContentsAllocator((CFMutableStringRef)str)); + } + + if (__CFStrIsMutable(str)) { + printf ("CurrentCapacity %d\n%sCapacity %d\n", (int)__CFStrCapacity(str), __CFStrIsFixed(str) ? "Fixed" : "Desired", (int)__CFStrDesiredCapacity(str)); + } + printf ("Contents %p\n", (void *)__CFStrContents(str)); +} + + diff --git a/String.subproj/CFString.h b/String.subproj/CFString.h new file mode 100644 index 0000000..23446be --- /dev/null +++ b/String.subproj/CFString.h @@ -0,0 +1,716 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFString.h + Copyright (c) 1998-2003, Apple, Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFSTRING__) +#define __COREFOUNDATION_CFSTRING__ 1 + +#include +#include +#include +#include +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +/* +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) +typedef 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 */ +} 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. +*/ + +/* These 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); + +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); + +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 of 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); + +/* This one goes the other way by creating a CFString from a bag of bytes. +This is much like CFStringCreateWithPascalString or CFStringCreateWithCString, +except the length is supplied explicitly. In addition, you can 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); + +/* 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 */ + +/*** 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. +*/ +typedef 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 */ +} 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. + 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 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); + +/* CFStringFindWithOptions() returns the found range in the CFRange * argument; you can pass NULL for simple discovery check. + If stringToFind is the empty string (zero length), nothing is found. + Ignores the kCFCompareNumerically option. +*/ +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 could be more than 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 LS, PS). + All return values are "optional" (provide NULL if you don't want them) + lineStartIndex: 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 - lineStartIndex is the number of chars in the line, including the line separators + contentsEndIndex - lineStartIndex 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); + +/*** 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. +*/ +typedef enum { + kCFStringNormalizationFormD = 0, // Canonical Decomposition + kCFStringNormalizationFormKD, // Compatibility Decomposition + kCFStringNormalizationFormC, // Canonical Decomposition followed by Canonical Composition + kCFStringNormalizationFormKC // Compatibility Decomposition followed by Canonical Composition +} 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 + +/* 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 +UInt32 CFStringConvertEncodingToNSStringEncoding(CFStringEncoding encoding); + +CF_EXPORT +CFStringEncoding CFStringConvertNSStringEncodingToEncoding(UInt32 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 */ + +#if defined(__cplusplus) +} +#endif + +#endif /* !__COREFOUNDATION_CFSTRING__ */ + diff --git a/String.subproj/CFStringEncodings.c b/String.subproj/CFStringEncodings.c new file mode 100644 index 0000000..a82057c --- /dev/null +++ b/String.subproj/CFStringEncodings.c @@ -0,0 +1,685 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFStringEncodings.c + Copyright 1999-2002, Apple, Inc. All rights reserved. + Responsibility: Aki Inoue +*/ + +#include "CFInternal.h" +#include +#include +#include "CFUtilities.h" +#include +#include "CFStringEncodingConverterExt.h" +#include "CFUniChar.h" +#include "CFUnicodeDecomposition.h" + +static UInt32 __CFWantsToUseASCIICompatibleConversion = (UInt32)-1; +CF_INLINE UInt32 __CFGetASCIICompatibleFlag(void) { + if (__CFWantsToUseASCIICompatibleConversion == (UInt32)-1) { + __CFWantsToUseASCIICompatibleConversion = false; + } + return (__CFWantsToUseASCIICompatibleConversion ? kCFStringEncodingASCIICompatibleConversion : 0); +} + +void _CFStringEncodingSetForceASCIICompatibility(Boolean flag) { + __CFWantsToUseASCIICompatibleConversion = (flag ? (UInt32)true : (UInt32)false); +} + +Boolean (*__CFCharToUniCharFunc)(UInt32 flags, uint8_t ch, UniChar *unicodeChar) = NULL; + +// To avoid early initialization issues, we just initialize this here +// This should not be const as it is changed +UniChar __CFCharToUniCharTable[256] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, +112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, +128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, +144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, +160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, +176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, +192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, +208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, +224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, +240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 +}; + +void __CFSetCharToUniCharFunc(Boolean (*func)(UInt32 flags, UInt8 ch, UniChar *unicodeChar)) { + if (__CFCharToUniCharFunc != func) { + int ch; + __CFCharToUniCharFunc = func; + if (func) { + for (ch = 128; ch < 256; ch++) { + UniChar uch; + __CFCharToUniCharTable[ch] = (__CFCharToUniCharFunc(0, ch, &uch) ? uch : 0xFFFD); + } + } else { // If we have no __CFCharToUniCharFunc, assume 128..255 return the value as-is + for (ch = 128; ch < 256; ch++) __CFCharToUniCharTable[ch] = ch; + } + } +} + +__private_extern__ void __CFStrConvertBytesToUnicode(const uint8_t *bytes, UniChar *buffer, CFIndex numChars) { + CFIndex idx; + for (idx = 0; idx < numChars; idx++) buffer[idx] = __CFCharToUniCharTable[bytes[idx]]; +} + + +/* The minimum length the output buffers should be in the above functions +*/ +#define kCFCharConversionBufferLength 512 + + +#define MAX_LOCAL_CHARS (sizeof(buffer->localBuffer) / sizeof(uint8_t)) +#define MAX_LOCAL_UNICHARS (sizeof(buffer->localBuffer) / sizeof(UniChar)) + +#if defined(__BIG_ENDIAN__) +#define SHOULD_SWAP(BOM) (BOM == 0xFFFE) +#else +#define SHOULD_SWAP(BOM) (BOM != 0xFEFF) +#endif + +/* Convert a byte stream to ASCII (7-bit!) or Unicode, with a CFVarWidthCharBuffer struct on the stack. false return indicates an error occured during the conversion. The caller needs to free the returned buffer in either ascii or unicode (indicated by isASCII), if shouldFreeChars is true. +9/18/98 __CFStringDecodeByteStream now avoids to allocate buffer if buffer->chars is not NULL +Added useClientsMemoryPtr; if not-NULL, and the provided memory can be used as is, this is set to true +__CFStringDecodeByteStream2() is kept around for any internal clients who might be using it; it should be deprecated +!!! converterFlags is only used for the UTF8 converter at this point +*/ +Boolean __CFStringDecodeByteStream2(const uint8_t *bytes, UInt32 len, CFStringEncoding encoding, Boolean alwaysUnicode, CFVarWidthCharBuffer *buffer, Boolean *useClientsMemoryPtr) { + return __CFStringDecodeByteStream3(bytes, len, encoding, alwaysUnicode, buffer, useClientsMemoryPtr, 0); +} + +enum { + __NSNonLossyErrorMode = -1, + __NSNonLossyASCIIMode = 0, + __NSNonLossyBackslashMode = 1, + __NSNonLossyHexInitialMode = __NSNonLossyBackslashMode + 1, + __NSNonLossyHexFinalMode = __NSNonLossyHexInitialMode + 4, + __NSNonLossyOctalInitialMode = __NSNonLossyHexFinalMode + 1, + __NSNonLossyOctalFinalMode = __NSNonLossyHexFinalMode + 3 +}; + +Boolean __CFStringDecodeByteStream3(const uint8_t *bytes, UInt32 len, CFStringEncoding encoding, Boolean alwaysUnicode, CFVarWidthCharBuffer *buffer, Boolean *useClientsMemoryPtr, UInt32 converterFlags) { + UInt32 idx; + const UniChar *uniChars = (const UniChar *)bytes; + const uint8_t *chars = (const uint8_t *)bytes; + const uint8_t *end = chars + len; + uint16_t bom; + Boolean allASCII = false; + + if (useClientsMemoryPtr) *useClientsMemoryPtr = false; + + buffer->isASCII = !alwaysUnicode; + buffer->shouldFreeChars = false; + buffer->numChars = 0; + if (0 == len) return true; + + buffer->allocator = (buffer->allocator ? buffer->allocator : __CFGetDefaultAllocator()); + switch (encoding) { + case kCFStringEncodingUnicode: + bom = (*uniChars == 0xfffe || *uniChars == 0xfeff) ? (*uniChars++) : 0; + /* If the byte order mark is missing, we assume big endian... */ + len = len / 2 - (0 == bom ? 0 : 1); + + if (buffer->isASCII) { // Let's see if we can reduce the Unicode down to ASCII... + if (SHOULD_SWAP(bom)) { + for (idx = 0; idx < len; idx++) if ((uniChars[idx] & 0x80ff) != 0) {buffer->isASCII = false; break;} + } else { + for (idx = 0; idx < len; idx++) if (uniChars[idx] > 127) {buffer->isASCII = false; break;} + } + } + + if (buffer->isASCII) { + buffer->numChars = len; + buffer->shouldFreeChars = !buffer->chars.ascii && (len <= MAX_LOCAL_CHARS) ? false : true; + buffer->chars.ascii = (buffer->chars.ascii ? buffer->chars.ascii : (len <= MAX_LOCAL_CHARS) ? (uint8_t *)buffer->localBuffer : CFAllocatorAllocate(buffer->allocator, len * sizeof(uint8_t), 0)); + if (SHOULD_SWAP(bom)) { // !!! Can be somewhat trickier here and use a single loop with a properly inited ptr + for (idx = 0; idx < len; idx++) buffer->chars.ascii[idx] = (uniChars[idx] >> 8); + } else { + for (idx = 0; idx < len; idx++) buffer->chars.ascii[idx] = uniChars[idx]; + } + } else { + buffer->numChars = len; + if (useClientsMemoryPtr && (bom == 0) && !SHOULD_SWAP(bom)) { // If the caller is ready to deal with no-copy situation, and the situation is possible, indicate it... + *useClientsMemoryPtr = true; + buffer->shouldFreeChars = false; + buffer->chars.unicode = (UniChar *)bytes; + } else { + buffer->shouldFreeChars = !buffer->chars.unicode && (len <= MAX_LOCAL_UNICHARS) ? false : true; + buffer->chars.unicode = (buffer->chars.unicode ? buffer->chars.unicode : (len <= MAX_LOCAL_UNICHARS) ? (UniChar *)buffer->localBuffer : CFAllocatorAllocate(buffer->allocator, len * sizeof(UniChar), 0)); + if (SHOULD_SWAP(bom)) { + for (idx = 0; idx < len; idx++) buffer->chars.unicode[idx] = CFSwapInt16(uniChars[idx]); + } else { + memmove(buffer->chars.unicode, uniChars, len * sizeof(UniChar)); + } + } + } + return true; + + case kCFStringEncodingNonLossyASCII: { + UTF16Char currentValue = 0; + uint8_t character; + int8_t mode = __NSNonLossyASCIIMode; + + buffer->isASCII = false; + buffer->shouldFreeChars = !buffer->chars.unicode && (len <= MAX_LOCAL_UNICHARS) ? false : true; + buffer->chars.unicode = (buffer->chars.unicode ? buffer->chars.unicode : (len <= MAX_LOCAL_UNICHARS) ? (UniChar *)buffer->localBuffer : CFAllocatorAllocate(buffer->allocator, len * sizeof(UniChar), 0)); + buffer->numChars = 0; + + while (chars < end) { + character = (*chars++); + + switch (mode) { + case __NSNonLossyASCIIMode: + if (character == '\\') { + mode = __NSNonLossyBackslashMode; + } else if (character < 0x80) { + currentValue = character; + } else { + mode = __NSNonLossyErrorMode; + } + break; + + case __NSNonLossyBackslashMode: + if ((character == 'U') || (character == 'u')) { + mode = __NSNonLossyHexInitialMode; + currentValue = 0; + } else if ((character >= '0') && (character <= '9')) { + mode = __NSNonLossyOctalInitialMode; + currentValue = character - '0'; + } else if (character == '\\') { + mode = __NSNonLossyASCIIMode; + currentValue = character; + } else { + mode = __NSNonLossyErrorMode; + } + break; + + default: + if (mode < __NSNonLossyHexFinalMode) { + if ((character >= '0') && (character <= '9')) { + currentValue = (currentValue << 4) | (character - '0'); + if (++mode == __NSNonLossyHexFinalMode) mode = __NSNonLossyASCIIMode; + } else { + if (character >= 'a') character -= ('a' - 'A'); + if ((character >= 'A') && (character <= 'F')) { + currentValue = (currentValue << 4) | ((character - 'A') + 10); + if (++mode == __NSNonLossyHexFinalMode) mode = __NSNonLossyASCIIMode; + } else { + mode = __NSNonLossyErrorMode; + } + } + } else { + if ((character >= '0') && (character <= '9')) { + currentValue = (currentValue << 3) | (character - '0'); + if (++mode == __NSNonLossyOctalFinalMode) mode = __NSNonLossyASCIIMode; + } else { + mode = __NSNonLossyErrorMode; + } + } + break; + } + + if (mode == __NSNonLossyASCIIMode) { + buffer->chars.unicode[buffer->numChars++] = currentValue; + } else if (mode == __NSNonLossyErrorMode) { + return false; + } + } + return (mode == __NSNonLossyASCIIMode); + } + + case kCFStringEncodingUTF8: + if ((len >= 3) && (chars[0] == 0xef) && (chars[1] == 0xbb) && (chars[2] == 0xbf)) { // If UTF8 BOM, skip + chars += 3; + len -= 3; + if (0 == len) return true; + } + allASCII = !alwaysUnicode; + if (allASCII) { + for (idx = 0; idx < len; idx++) { + if (128 <= chars[idx]) { + allASCII = false; + break; + } + } + } + buffer->isASCII = allASCII; + if (allASCII) { + buffer->numChars = len; + buffer->shouldFreeChars = !buffer->chars.ascii && (len <= MAX_LOCAL_CHARS) ? false : true; + buffer->chars.ascii = (buffer->chars.ascii ? buffer->chars.ascii : (len <= MAX_LOCAL_CHARS) ? (uint8_t *)buffer->localBuffer : CFAllocatorAllocate(buffer->allocator, len * sizeof(uint8_t), 0)); + memmove(buffer->chars.ascii, chars, len * sizeof(uint8_t)); + } else { + UInt32 numDone; + static CFStringEncodingToUnicodeProc __CFFromUTF8 = NULL; + + if (!__CFFromUTF8) { + const CFStringEncodingConverter *converter = CFStringEncodingGetConverter(kCFStringEncodingUTF8); + __CFFromUTF8 = (CFStringEncodingToUnicodeProc)converter->toUnicode; + } + + buffer->shouldFreeChars = !buffer->chars.unicode && (len <= MAX_LOCAL_UNICHARS) ? false : true; + buffer->chars.unicode = (buffer->chars.unicode ? buffer->chars.unicode : (len <= MAX_LOCAL_UNICHARS) ? (UniChar *)buffer->localBuffer : CFAllocatorAllocate(buffer->allocator, len * sizeof(UniChar), 0)); + buffer->numChars = 0; + while (chars < end) { + numDone = 0; + chars += __CFFromUTF8(converterFlags, chars, end - chars, &(buffer->chars.unicode[buffer->numChars]), len - buffer->numChars, &numDone); + + if (0 == numDone) { + if (buffer->shouldFreeChars) CFAllocatorDeallocate(buffer->allocator, buffer->chars.unicode); + buffer->isASCII = !alwaysUnicode; + buffer->shouldFreeChars = false; + buffer->chars.ascii = NULL; + buffer->numChars = 0; + return false; + } + buffer->numChars += numDone; + } + } + return true; + + default: + if (CFStringEncodingIsValidEncoding(encoding)) { + const CFStringEncodingConverter *converter = CFStringEncodingGetConverter(encoding); + Boolean isASCIISuperset = __CFStringEncodingIsSupersetOfASCII(encoding); + + if (!converter) return false; + + if (converter->encodingClass == kCFStringEncodingConverterCheapEightBit) { + allASCII = !alwaysUnicode && isASCIISuperset; + if (allASCII) { + for (idx = 0; idx < len; idx++) { + if (128 <= chars[idx]) { + allASCII = false; + break; + } + } + } + buffer->isASCII = allASCII; + if (allASCII) { + buffer->numChars = len; + buffer->shouldFreeChars = !buffer->chars.ascii && (len <= MAX_LOCAL_CHARS) ? false : true; + buffer->chars.ascii = (buffer->chars.ascii ? buffer->chars.ascii : (len <= MAX_LOCAL_CHARS) ? (uint8_t *)buffer->localBuffer : CFAllocatorAllocate(buffer->allocator, len * sizeof(uint8_t), 0)); + memmove(buffer->chars.ascii, chars, len * sizeof(uint8_t)); + } else { + buffer->shouldFreeChars = !buffer->chars.unicode && (len <= MAX_LOCAL_UNICHARS) ? false : true; + buffer->chars.unicode = (buffer->chars.unicode ? buffer->chars.unicode : (len <= MAX_LOCAL_UNICHARS) ? (UniChar *)buffer->localBuffer : CFAllocatorAllocate(buffer->allocator, len * sizeof(UniChar), 0)); + buffer->numChars = len; + if (kCFStringEncodingASCII == encoding || kCFStringEncodingISOLatin1 == encoding) { + for (idx = 0; idx < len; idx++) buffer->chars.unicode[idx] = (UniChar)chars[idx]; + } else { + for (idx = 0; idx < len; idx++) + if (chars[idx] < 0x80 && isASCIISuperset) + buffer->chars.unicode[idx] = (UniChar)chars[idx]; + else if (!((CFStringEncodingCheapEightBitToUnicodeProc)converter->toUnicode)(0, chars[idx], buffer->chars.unicode + idx)) + return false; + } + } + return true; + } else { + allASCII = !alwaysUnicode && isASCIISuperset; + if (allASCII) { + for (idx = 0; idx < len; idx++) + if (128 <= chars[idx]) { + allASCII = false; + break; + } + } + buffer->isASCII = allASCII; + if (allASCII) { + buffer->numChars = len; + buffer->shouldFreeChars = !buffer->chars.ascii && (len <= MAX_LOCAL_CHARS) ? false : true; + buffer->chars.ascii = (buffer->chars.ascii ? buffer->chars.ascii : (len <= MAX_LOCAL_CHARS) ? (uint8_t *)buffer->localBuffer : CFAllocatorAllocate(buffer->allocator, len * sizeof(uint8_t), 0)); + memmove(buffer->chars.ascii, chars, len * sizeof(uint8_t)); + } else { + UInt32 guessedLength = CFStringEncodingCharLengthForBytes(encoding, 0, bytes, len); + static UInt32 lossyFlag = (UInt32)-1; + + buffer->shouldFreeChars = !buffer->chars.unicode && (guessedLength <= MAX_LOCAL_UNICHARS) ? false : true; + buffer->chars.unicode = (buffer->chars.unicode ? buffer->chars.unicode : (guessedLength <= MAX_LOCAL_UNICHARS) ? (UniChar *)buffer->localBuffer : CFAllocatorAllocate(buffer->allocator, guessedLength * sizeof(UniChar), 0)); + + if (lossyFlag == (UInt32)-1) lossyFlag = (_CFExecutableLinkedOnOrAfter(CFSystemVersionPanther) ? 0 : kCFStringEncodingAllowLossyConversion); + + if (CFStringEncodingBytesToUnicode(encoding, lossyFlag|__CFGetASCIICompatibleFlag(), bytes, len, NULL, buffer->chars.unicode, (guessedLength > MAX_LOCAL_UNICHARS ? guessedLength : MAX_LOCAL_UNICHARS), &(buffer->numChars))) { + if (buffer->shouldFreeChars) CFAllocatorDeallocate(buffer->allocator, buffer->chars.unicode); + buffer->isASCII = !alwaysUnicode; + buffer->shouldFreeChars = false; + buffer->chars.ascii = NULL; + buffer->numChars = 0; + return false; + } + } + return true; + } + } else { + return false; + } + } +} + + +/* Create a byte stream from a CFString backing. Can 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 char specified by lossByte; if 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, indicated by usedBufLen). + Does not zero-terminate. If you want to create Pascal or C string, allow one extra byte at start or end. + + Note: This function is intended to work through CFString functions, so it should work + with NSStrings as well as CFStrings. +*/ +CFIndex __CFStringEncodeByteStream(CFStringRef string, CFIndex rangeLoc, CFIndex rangeLen, Boolean generatingExternalFile, CFStringEncoding encoding, char lossByte, uint8_t *buffer, CFIndex max, CFIndex *usedBufLen) { + CFIndex totalBytesWritten = 0; /* Number of written bytes */ + CFIndex numCharsProcessed = 0; /* Number of processed chars */ + const UniChar *unichars; + + if (encoding == kCFStringEncodingUTF8 && (unichars = CFStringGetCharactersPtr(string))) { + static CFStringEncodingToBytesProc __CFToUTF8 = NULL; + + if (!__CFToUTF8) { + const CFStringEncodingConverter *utf8Converter = CFStringEncodingGetConverter(kCFStringEncodingUTF8); + __CFToUTF8 = (CFStringEncodingToBytesProc)utf8Converter->toBytes; + } + numCharsProcessed = __CFToUTF8((generatingExternalFile ? kCFStringEncodingPrependBOM : 0), unichars + rangeLoc, rangeLen, buffer, (buffer ? max : 0), &totalBytesWritten); + + } else if (encoding == kCFStringEncodingNonLossyASCII) { + const char *hex = "0123456789abcdef"; + UniChar ch; + CFStringInlineBuffer buf; + CFStringInitInlineBuffer(string, &buf, CFRangeMake(rangeLoc, rangeLen)); + while (numCharsProcessed < rangeLen) { + CFIndex reqLength; /* Required number of chars to encode this UniChar */ + CFIndex cnt; + char tmp[6]; + ch = CFStringGetCharacterFromInlineBuffer(&buf, numCharsProcessed); + if ((ch >= ' ' && ch <= '~' && ch != '\\') || (ch == '\n' || ch == '\r' || ch == '\t')) { + reqLength = 1; + tmp[0] = ch; + } else { + if (ch == '\\') { + tmp[1] = '\\'; + reqLength = 2; + } else if (ch < 256) { /* \nnn; note that this is not NEXTSTEP encoding but a (small) UniChar */ + tmp[1] = '0' + (ch >> 6); + tmp[2] = '0' + ((ch >> 3) & 7); + tmp[3] = '0' + (ch & 7); + reqLength = 4; + } else { /* \Unnnn */ + tmp[1] = 'u'; // Changed to small+u in order to be aligned with Java + tmp[2] = hex[(ch >> 12) & 0x0f]; + tmp[3] = hex[(ch >> 8) & 0x0f]; + tmp[4] = hex[(ch >> 4) & 0x0f]; + tmp[5] = hex[ch & 0x0f]; + reqLength = 6; + } + tmp[0] = '\\'; + } + if (buffer) { + if (totalBytesWritten + reqLength > max) break; /* Doesn't fit.. +.*/ + for (cnt = 0; cnt < reqLength; cnt++) { + buffer[totalBytesWritten + cnt] = tmp[cnt]; + } + } + totalBytesWritten += reqLength; + numCharsProcessed++; + } + } else if (encoding == kCFStringEncodingUnicode) { + CFIndex extraForBOM = generatingExternalFile ? sizeof(UniChar) : 0; + numCharsProcessed = rangeLen; + if (buffer && (numCharsProcessed * (CFIndex)sizeof(UniChar) + extraForBOM > max)) { + numCharsProcessed = (max > extraForBOM) ? ((max - extraForBOM) / sizeof(UniChar)) : 0; + } + totalBytesWritten = (numCharsProcessed * sizeof(UniChar)) + extraForBOM; + if (buffer) { + if (generatingExternalFile) { /* Generate BOM */ +#if defined(__BIG_ENDIAN__) + *buffer++ = 0xfe; *buffer++ = 0xff; +#else + *buffer++ = 0xff; *buffer++ = 0xfe; +#endif + } + CFStringGetCharacters(string, CFRangeMake(rangeLoc, numCharsProcessed), (UniChar *)buffer); + } + } else { + CFIndex numChars; + UInt32 flags; + const unsigned char *cString = NULL; + + if (!CF_IS_OBJC(CFStringGetTypeID(), string) && __CFStringEncodingIsSupersetOfASCII(encoding)) { // Checking for NSString to avoid infinite recursion + const unsigned char *ptr; + if ((cString = CFStringGetCStringPtr(string, __CFStringGetEightBitStringEncoding()))) { + ptr = (cString += rangeLoc); + if (__CFStringGetEightBitStringEncoding() == encoding) { + numCharsProcessed = (rangeLen < max || buffer == NULL ? rangeLen : max); + if (buffer) memmove(buffer, cString, numCharsProcessed); + if (usedBufLen) *usedBufLen = numCharsProcessed; + return numCharsProcessed; + } + while (*ptr < 0x80 && rangeLen > 0) { + ++ptr; + --rangeLen; + } + numCharsProcessed = ptr - cString; + if (buffer) { + numCharsProcessed = (numCharsProcessed < max ? numCharsProcessed : max); + memmove(buffer, cString, numCharsProcessed); + buffer += numCharsProcessed; + max -= numCharsProcessed; + } + if (!rangeLen || (buffer && (max == 0))) { + if (usedBufLen) *usedBufLen = numCharsProcessed; + return numCharsProcessed; + } + rangeLoc += numCharsProcessed; + totalBytesWritten += numCharsProcessed; + } + if (!cString && (cString = CFStringGetPascalStringPtr(string, __CFStringGetEightBitStringEncoding()))) { + ptr = (cString += (rangeLoc + 1)); + if (__CFStringGetEightBitStringEncoding() == encoding) { + numCharsProcessed = (rangeLen < max || buffer == NULL ? rangeLen : max); + if (buffer) memmove(buffer, cString, numCharsProcessed); + if (usedBufLen) *usedBufLen = numCharsProcessed; + return numCharsProcessed; + } + while (*ptr < 0x80 && rangeLen > 0) { + ++ptr; + --rangeLen; + } + numCharsProcessed = ptr - cString; + if (buffer) { + numCharsProcessed = (numCharsProcessed < max ? numCharsProcessed : max); + memmove(buffer, cString, numCharsProcessed); + buffer += numCharsProcessed; + max -= numCharsProcessed; + } + if (!rangeLen || (buffer && (max == 0))) { + if (usedBufLen) *usedBufLen = numCharsProcessed; + return numCharsProcessed; + } + rangeLoc += numCharsProcessed; + totalBytesWritten += numCharsProcessed; + } + } + + if (!buffer) max = 0; + + // Special case for Foundation. When lossByte == 0xFF && encoding kCFStringEncodingASCII, we do the default ASCII fallback conversion + flags = (lossByte ? ((unsigned char)lossByte == 0xFF && encoding == kCFStringEncodingASCII ? kCFStringEncodingAllowLossyConversion : CFStringEncodingLossyByteToMask(lossByte)) : 0) | (generatingExternalFile ? kCFStringEncodingPrependBOM : 0) | __CFGetASCIICompatibleFlag(); + + if (!cString && (cString = (const char*)CFStringGetCharactersPtr(string))) { // Must be Unicode string + if (CFStringEncodingIsValidEncoding(encoding)) { // Converter available in CF + CFStringEncodingUnicodeToBytes(encoding, flags, (const UniChar*)cString + rangeLoc, rangeLen, &numCharsProcessed, buffer, max, &totalBytesWritten); + } else { + return 0; + } + } else { + UniChar charBuf[kCFCharConversionBufferLength]; + UInt32 currentLength; + UInt32 usedLen; + uint32_t lastUsedLen = 0, lastNumChars = 0; + uint32_t result; + Boolean isCFBuiltin = CFStringEncodingIsValidEncoding(encoding); +#define MAX_DECOMP_LEN (6) + + while (rangeLen > 0) { + currentLength = (rangeLen > kCFCharConversionBufferLength ? kCFCharConversionBufferLength : rangeLen); + CFStringGetCharacters(string, CFRangeMake(rangeLoc, currentLength), charBuf); + + // could be in the middle of surrogate pair; back up. + if ((rangeLen > kCFCharConversionBufferLength) && CFUniCharIsSurrogateHighCharacter(charBuf[kCFCharConversionBufferLength - 1])) --currentLength; + + if (isCFBuiltin) { // Converter available in CF + if ((result = CFStringEncodingUnicodeToBytes(encoding, flags, charBuf, currentLength, &numChars, buffer, max, &usedLen)) != kCFStringEncodingConversionSuccess) { + if (kCFStringEncodingInvalidInputStream == result) { + CFRange composedRange; + // Check the tail + if ((rangeLen > kCFCharConversionBufferLength) && ((currentLength - numChars) < MAX_DECOMP_LEN)) { + composedRange = CFStringGetRangeOfComposedCharactersAtIndex(string, rangeLoc + currentLength); + + if ((composedRange.length <= MAX_DECOMP_LEN) && (composedRange.location < (rangeLoc + numChars))) { + result = CFStringEncodingUnicodeToBytes(encoding, flags, charBuf, composedRange.location - rangeLoc, &numChars, buffer, max, &usedLen); + } + } + + // Check the head + if ((kCFStringEncodingConversionSuccess != result) && (lastNumChars > 0) && (numChars < MAX_DECOMP_LEN)) { + composedRange = CFStringGetRangeOfComposedCharactersAtIndex(string, rangeLoc); + + if ((composedRange.length <= MAX_DECOMP_LEN) && (composedRange.location < rangeLoc)) { + // Try if the composed range can be converted + CFStringGetCharacters(string, composedRange, charBuf); + + if (CFStringEncodingUnicodeToBytes(encoding, flags, charBuf, composedRange.length, &numChars, NULL, 0, &usedLen) == kCFStringEncodingConversionSuccess) { // OK let's try the last run + CFIndex lastRangeLoc = rangeLoc - lastNumChars; + + currentLength = composedRange.location - lastRangeLoc; + CFStringGetCharacters(string, CFRangeMake(lastRangeLoc, currentLength), charBuf); + + if ((result = CFStringEncodingUnicodeToBytes(encoding, flags, charBuf, currentLength, &numChars, (max ? buffer - lastUsedLen : NULL), (max ? max + lastUsedLen : 0), &usedLen)) == kCFStringEncodingConversionSuccess) { // OK let's try the last run + // Looks good. back up + totalBytesWritten -= lastUsedLen; + numCharsProcessed -= lastNumChars; + + rangeLoc = lastRangeLoc; + rangeLen += lastNumChars; + + if (max) { + buffer -= lastUsedLen; + max += lastUsedLen; + } + } + } + } + } + } + + if (kCFStringEncodingConversionSuccess != result) { // really failed + totalBytesWritten += usedLen; + numCharsProcessed += numChars; + break; + } + } + } else { + return 0; + } + + totalBytesWritten += usedLen; + numCharsProcessed += numChars; + + rangeLoc += numChars; + rangeLen -= numChars; + if (max) { + buffer += usedLen; + max -= usedLen; + if (max <= 0) break; + } + lastUsedLen = usedLen; lastNumChars = numChars; + flags &= ~kCFStringEncodingPrependBOM; + } + } + } + if (usedBufLen) *usedBufLen = totalBytesWritten; + return numCharsProcessed; +} + +#define MAX_STACK_BUFFER_LEN (255) +CF_EXPORT Boolean _CFStringGetFileSystemRepresentation(CFStringRef string, uint8_t *buffer, CFIndex maxBufLen) { +#if defined(__MACH__) + const UTF16Char *characters = CFStringGetCharactersPtr(string); + uint32_t usedBufLen; + + if (NULL == characters) { + CFIndex length = CFStringGetLength(string); + + if (length > MAX_STACK_BUFFER_LEN) { + UTF16Char charactersBuffer[MAX_STACK_BUFFER_LEN]; + CFRange range = CFRangeMake(0, MAX_STACK_BUFFER_LEN); + uint32_t localUsedBufLen; + + usedBufLen = 0; + + while (length > 0) { + CFStringGetCharacters(string, range, charactersBuffer); + if (CFUniCharIsSurrogateHighCharacter(charactersBuffer[range.length - 1])) --range.length; // Backup for a high surrogate + + if (!CFUniCharDecompose(charactersBuffer, range.length, NULL, (void *)buffer, maxBufLen - usedBufLen, &localUsedBufLen, true, kCFUniCharUTF8Format, true)) return false; + buffer += localUsedBufLen; + usedBufLen += localUsedBufLen; + + length -= range.length; + range.location += range.length; + range.length = (length < MAX_STACK_BUFFER_LEN ? length : MAX_STACK_BUFFER_LEN); + } + } else { + UTF16Char charactersBuffer[MAX_STACK_BUFFER_LEN]; // C99 Variable array + + CFStringGetCharacters(string, CFRangeMake(0, length), charactersBuffer); + if (!CFUniCharDecompose(charactersBuffer, length, NULL, (void *)buffer, maxBufLen, &usedBufLen, true, kCFUniCharUTF8Format, true)) return false; + buffer += usedBufLen; + } + } else { + if (!CFUniCharDecompose(characters, CFStringGetLength(string), NULL, (void *)buffer, maxBufLen, &usedBufLen, true, kCFUniCharUTF8Format, true)) return false; + buffer += usedBufLen; + } + + if (usedBufLen < (uint32_t)maxBufLen) { // Since the filename has its own limit, this is ok for now + *buffer = '\0'; + return true; + } else { + return false; + } +#else __MACH__ + return CFStringGetCString(string, buffer, maxBufLen, CFStringFileSystemEncoding()); +#endif __MACH__ +} diff --git a/String.subproj/CFStringScanner.c b/String.subproj/CFStringScanner.c new file mode 100644 index 0000000..6baafe7 --- /dev/null +++ b/String.subproj/CFStringScanner.c @@ -0,0 +1,274 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFStringScanner.c + Copyright 1999-2002, Apple, Inc. All rights reserved. + Responsibility: Ali Ozer +*/ + +#include "CFInternal.h" +#include +#if !defined(__MACOS8__) +#include +#endif +#include +#include +#include + +CF_INLINE Boolean __CFCharacterIsADigit(UniChar ch) { + return (ch >= '0' && ch <= '9') ? true : false; +} + +/* Returns -1 on illegal value */ +CF_INLINE SInt32 __CFCharacterNumericOrHexValue (UniChar ch) { + if (ch >= '0' && ch <= '9') { + return ch - '0'; + } else if (ch >= 'A' && ch <= 'F') { + return ch + 10 - 'A'; + } else if (ch >= 'a' && ch <= 'f') { + return ch + 10 - 'a'; + } else { + return -1; + } +} + +/* Returns -1 on illegal value */ +CF_INLINE SInt32 __CFCharacterNumericValue(UniChar ch) { + return (ch >= '0' && ch <= '9') ? (ch - '0') : -1; +} + +CF_INLINE UniChar __CFStringGetFirstNonSpaceCharacterFromInlineBuffer(CFStringInlineBuffer *buf, SInt32 *indexPtr) { + UniChar ch; + while (__CFIsWhitespace(ch = __CFStringGetCharacterFromInlineBufferAux(buf, *indexPtr))) (*indexPtr)++; + return ch; +} + +/* result is int64_t or int, depending on doLonglong +*/ +__private_extern__ Boolean __CFStringScanInteger(CFStringInlineBuffer *buf, CFDictionaryRef locale, SInt32 *indexPtr, Boolean doLonglong, void *result) { + Boolean doingLonglong = false; /* Set to true if doLonglong, and we overflow an int... */ + Boolean neg = false; + int intResult = 0; + register int64_t longlongResult = 0; /* ??? int64_t is slow when not in regs; I hope this does the right thing. */ + UniChar ch; + + ch = __CFStringGetFirstNonSpaceCharacterFromInlineBuffer(buf, indexPtr); + + if (ch == '-' || ch == '+') { + neg = (ch == '-'); + (*indexPtr)++; + ch = __CFStringGetFirstNonSpaceCharacterFromInlineBuffer(buf, indexPtr); + } + + if (! __CFCharacterIsADigit(ch)) return false; /* No digits, bail out... */ + do { + if (doingLonglong) { + if ((longlongResult >= LLONG_MAX / 10) && ((longlongResult > LLONG_MAX / 10) || (__CFCharacterNumericValue(ch) - (neg ? 1 : 0) >= LLONG_MAX - longlongResult * 10))) { + /* ??? This might not handle LLONG_MIN correctly... */ + longlongResult = neg ? LLONG_MIN : LLONG_MAX; + neg = false; + while (__CFCharacterIsADigit(ch = __CFStringGetCharacterFromInlineBufferAux(buf, ++(*indexPtr)))); /* Skip remaining digits */ + } else { + longlongResult = longlongResult * 10 + __CFCharacterNumericValue(ch); + ch = __CFStringGetCharacterFromInlineBufferAux(buf, ++(*indexPtr)); + } + } else { + if ((intResult >= INT_MAX / 10) && ((intResult > INT_MAX / 10) || (__CFCharacterNumericValue(ch) - (neg ? 1 : 0) >= INT_MAX - intResult * 10))) { + // Overflow, check for int64_t... + if (doLonglong) { + longlongResult = intResult; + doingLonglong = true; + } else { + /* ??? This might not handle INT_MIN correctly... */ + intResult = neg ? INT_MIN : INT_MAX; + neg = false; + while (__CFCharacterIsADigit(ch = __CFStringGetCharacterFromInlineBufferAux(buf, ++(*indexPtr)))); /* Skip remaining digits */ + } + } else { + intResult = intResult * 10 + __CFCharacterNumericValue(ch); + ch = __CFStringGetCharacterFromInlineBufferAux(buf, ++(*indexPtr)); + } + } + } while (__CFCharacterIsADigit(ch)); + + if (result) { + if (doLonglong) { + if (!doingLonglong) longlongResult = intResult; + *(int64_t *)result = neg ? -longlongResult : longlongResult; + } else { + *(int *)result = neg ? -intResult : intResult; + } + } + + return true; +} + +__private_extern__ Boolean __CFStringScanHex(CFStringInlineBuffer *buf, SInt32 *indexPtr, unsigned *result) { + UInt32 value = 0; + SInt32 curDigit; + UniChar ch; + + ch = __CFStringGetFirstNonSpaceCharacterFromInlineBuffer(buf, indexPtr); + /* Ignore the optional "0x" or "0X"; if it's followed by a non-hex, just parse the "0" and leave pointer at "x" */ + if (ch == '0') { + ch = __CFStringGetCharacterFromInlineBufferAux(buf, ++(*indexPtr)); + if (ch == 'x' || ch == 'X') ch = __CFStringGetCharacterFromInlineBufferAux(buf, ++(*indexPtr)); + curDigit = __CFCharacterNumericOrHexValue(ch); + if (curDigit == -1) { + (*indexPtr)--; /* Go back over the "x" or "X" */ + if (result) *result = 0; + return true; /* We just saw "0" */ + } + } else { + curDigit = __CFCharacterNumericOrHexValue(ch); + if (curDigit == -1) return false; + } + + do { + if (value > (UINT_MAX >> 4)) { + value = UINT_MAX; /* We do this over and over again, but it's an error case anyway */ + } else { + value = (value << 4) + curDigit; + } + curDigit = __CFCharacterNumericOrHexValue(__CFStringGetCharacterFromInlineBufferAux(buf, ++(*indexPtr))); + } while (curDigit != -1); + + if (result) *result = value; + return true; +} + +// Packed array of Boolean +static const char __CFNumberSet[16] = { + 0X00, // 0, 0, 0, 0, 0, 0, 0, 0, // nul soh stx etx eot enq ack bel + 0X00, // 0, 0, 0, 0, 0, 0, 0, 0, // bs ht nl vt np cr so si + 0X00, // 0, 0, 0, 0, 0, 0, 0, 0, // dle dc1 dc2 dc3 dc4 nak syn etb + 0X00, // 0, 0, 0, 0, 0, 0, 0, 0, // can em sub esc fs gs rs us + 0X00, // 0, 0, 0, 0, 0, 0, 0, 0, // sp ! " # $ % & ' + 0X28, // 0, 0, 0, 1, 0, 1, 0, 0, // ( ) * + , - . / + 0XFF, // 1, 1, 1, 1, 1, 1, 1, 1, // 0 1 2 3 4 5 6 7 + 0X03, // 1, 1, 0, 0, 0, 0, 0, 0, // 8 9 : ; < = > ? + 0X20, // 0, 0, 0, 0, 0, 1, 0, 0, // @ A B C D E F G + 0X00, // 0, 0, 0, 0, 0, 0, 0, 0, // H I J K L M N O + 0X00, // 0, 0, 0, 0, 0, 0, 0, 0, // P Q R S T U V W + 0X00, // 0, 0, 0, 0, 0, 0, 0, 0, // X Y Z [ \ ] ^ _ + 0X20, // 0, 0, 0, 0, 0, 1, 0, 0, // ` a b c d e f g + 0X00, // 0, 0, 0, 0, 0, 0, 0, 0, // h i j k l m n o + 0X00, // 0, 0, 0, 0, 0, 0, 0, 0, // p q r s t u v w + 0X00, // 0, 0, 0, 0, 0, 0, 0, 0 // x y z { | } ~ del +}; + +__private_extern__ Boolean __CFStringScanDouble(CFStringInlineBuffer *buf, CFDictionaryRef locale, SInt32 *indexPtr, double *resultPtr) { + #define STACK_BUFFER_SIZE 256 + #define ALLOC_CHUNK_SIZE 256 // first and subsequent malloc size. Should be greater than STACK_BUFFER_SIZE + char localCharBuffer[STACK_BUFFER_SIZE]; + char *charPtr = localCharBuffer; + char *endCharPtr; + UniChar decimalChar = '.'; + SInt32 numChars = 0; + SInt32 capacity = STACK_BUFFER_SIZE; // in chars + double result; + UniChar ch; + CFAllocatorRef tmpAlloc = NULL; + +#if 0 + if (locale != NULL) { + CFStringRef decimalSeparator = [locale objectForKey: NSDecimalSeparator]; + if (decimalSeparator != nil) decimalChar = [decimalSeparator characterAtIndex:0]; + } +#endif + ch = __CFStringGetFirstNonSpaceCharacterFromInlineBuffer(buf, indexPtr); + // At this point indexPtr points at the first non-space char +#if 0 +#warning need to allow, case insensitively, all of: "nan", "inf", "-inf", "+inf", "-infinity", "+infinity", "infinity"; +#warning -- strtod() will actually do most or all of that for us +#define BITSFORDOUBLENAN ((uint64_t)0x7ff8000000000000) +#define BITSFORDOUBLEPOSINF ((uint64_t)0x7ff0000000000000) +#define BITSFORDOUBLENEGINF ((uint64_t)0xfff0000000000000) + if ('N' == ch || 'n' == ch) { // check for "NaN", case insensitively + UniChar next1 = __CFStringGetCharacterFromInlineBufferAux(buf, *indexPtr + 1); + UniChar next2 = __CFStringGetCharacterFromInlineBufferAux(buf, *indexPtr + 2); + if (('a' == next1 || 'A' == next1) && + ('N' == next2 || 'n' == next2)) { + *indexPtr += 3; + if (resultPtr) *(uint64_t *)resultPtr = BITSFORDOUBLENAN; + return true; + } + } + if ('I' == ch || 'i' == ch) { // check for "Inf", case insensitively + UniChar next1 = __CFStringGetCharacterFromInlineBufferAux(buf, *indexPtr + 1); + UniChar next2 = __CFStringGetCharacterFromInlineBufferAux(buf, *indexPtr + 2); + if (('n' == next1 || 'N' == next1) && + ('f' == next2 || 'F' == next2)) { + *indexPtr += 3; + if (resultPtr) *(uint64_t *)resultPtr = BITSFORDOUBLEPOSINF; + return true; + } + } + if ('+' == ch || '-' == ch) { // check for "+/-Inf", case insensitively + UniChar next1 = __CFStringGetCharacterFromInlineBufferAux(buf, *indexPtr + 1); + UniChar next2 = __CFStringGetCharacterFromInlineBufferAux(buf, *indexPtr + 2); + UniChar next3 = __CFStringGetCharacterFromInlineBufferAux(buf, *indexPtr + 3); + if (('I' == next1 || 'i' == next1) && + ('n' == next2 || 'N' == next2) && + ('f' == next3 || 'F' == next3)) { + *indexPtr += 4; + if (resultPtr) *(uint64_t *)resultPtr = ('-' == ch) ? BITSFORDOUBLENEGINF : BITSFORDOUBLEPOSINF; + return true; + } + } +#endif 0 + do { + if (ch >= 128 || (__CFNumberSet[ch >> 3] & (1 << (ch & 7))) == 0) { + // Not in __CFNumberSet + if (ch != decimalChar) break; + ch = '.'; // Replace the decimal character with something strtod will understand + } + if (numChars >= capacity - 1) { + capacity += ALLOC_CHUNK_SIZE; + if (tmpAlloc == NULL) tmpAlloc = __CFGetDefaultAllocator(); + if (charPtr == localCharBuffer) { + charPtr = CFAllocatorAllocate(tmpAlloc, capacity * sizeof(char), 0); + memmove(charPtr, localCharBuffer, numChars * sizeof(char)); + } else { + charPtr = CFAllocatorReallocate(tmpAlloc, charPtr, capacity * sizeof(char), 0); + } + } + charPtr[numChars++] = (char)ch; + ch = __CFStringGetCharacterFromInlineBufferAux(buf, *indexPtr + numChars); + } while (true); + charPtr[numChars] = 0; // Null byte for strtod + result = strtod(charPtr, &endCharPtr); + if (tmpAlloc) CFAllocatorDeallocate(tmpAlloc, charPtr); + if (charPtr == endCharPtr) return false; + *indexPtr += (endCharPtr - charPtr); + if (resultPtr) *resultPtr = result; // only store result if we succeed + + return true; +} + + +#undef STACK_BUFFER_SIZE +#undef ALLOC_CHUNK_SIZE + + diff --git a/String.subproj/CFStringUtilities.c b/String.subproj/CFStringUtilities.c new file mode 100644 index 0000000..51acaba --- /dev/null +++ b/String.subproj/CFStringUtilities.c @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFStringUtilities.c + Copyright 1999-2002, Apple, Inc. All rights reserved. + Responsibility: Aki Inoue +*/ + +#include "CFInternal.h" +#include "CFStringEncodingConverterExt.h" +#include "CFUniChar.h" +#include +#if defined(__MACH__) || defined(__LINUX__) +#include +#elif defined(__WIN32__) +#include +#include +#endif + + + +Boolean CFStringIsEncodingAvailable(CFStringEncoding theEncoding) { + switch (theEncoding) { + case kCFStringEncodingASCII: // Built-in encodings + case kCFStringEncodingMacRoman: + case kCFStringEncodingUnicode: + case kCFStringEncodingUTF8: + case kCFStringEncodingNonLossyASCII: + case kCFStringEncodingWindowsLatin1: + case kCFStringEncodingNextStepLatin: + return true; + + default: + return CFStringEncodingIsValidEncoding(theEncoding); + } +} + +const CFStringEncoding* CFStringGetListOfAvailableEncodings() { + return CFStringEncodingListOfAvailableEncodings(); +} + +CFStringRef CFStringGetNameOfEncoding(CFStringEncoding theEncoding) { + static CFMutableDictionaryRef mappingTable = NULL; + CFStringRef theName = mappingTable ? CFDictionaryGetValue(mappingTable, (const void*)theEncoding) : NULL; + + if (!theName) { + if (theEncoding == kCFStringEncodingUnicode) { + theName = CFSTR("Unicode (UTF-16)"); + } else if (theEncoding == kCFStringEncodingUTF8) { + theName = CFSTR("Unicode (UTF-8)"); + } else if (theEncoding == kCFStringEncodingNonLossyASCII) { + theName = CFSTR("Non-lossy ASCII"); + } else { + const uint8_t *encodingName = CFStringEncodingName(theEncoding); + + if (encodingName) { + theName = CFStringCreateWithCString(NULL, encodingName, kCFStringEncodingASCII); + } + + if (theName) { + if (!mappingTable) mappingTable = CFDictionaryCreateMutable(NULL, 0, (const CFDictionaryKeyCallBacks *)NULL, &kCFTypeDictionaryValueCallBacks); + + CFDictionaryAddValue(mappingTable, (const void*)theEncoding, (const void*)theName); + CFRelease(theName); + } + } + } + + return theName; +} + +CFStringEncoding CFStringConvertIANACharSetNameToEncoding(CFStringRef charsetName) { + static CFMutableDictionaryRef mappingTable = NULL; + CFStringEncoding result = kCFStringEncodingInvalidId; + CFMutableStringRef lowerCharsetName = CFStringCreateMutableCopy(NULL, 0, charsetName); + + /* Create lowercase copy */ + CFStringLowercase(lowerCharsetName, NULL); + + /* Check for common encodings first */ + if (CFStringCompare(lowerCharsetName, CFSTR("utf-8"), kCFCompareCaseInsensitive) == kCFCompareEqualTo) { + CFRelease(lowerCharsetName); + return kCFStringEncodingUTF8; + } else if (CFStringCompare(lowerCharsetName, CFSTR("iso-8859-1"), kCFCompareCaseInsensitive) == kCFCompareEqualTo) { + CFRelease(lowerCharsetName); + return kCFStringEncodingISOLatin1; + } else if (CFStringCompare(lowerCharsetName, CFSTR("utf-16be"), kCFCompareCaseInsensitive) == kCFCompareEqualTo) { + CFRelease(lowerCharsetName); + return kCFStringEncodingUnicode; + } + + if (mappingTable == NULL) { + CFMutableDictionaryRef table = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, (const CFDictionaryValueCallBacks *)NULL); + const CFStringEncoding *encodings = CFStringGetListOfAvailableEncodings(); + + while (*encodings != kCFStringEncodingInvalidId) { + const char **nameList = CFStringEncodingCanonicalCharsetNames(*encodings); + + if (nameList) { + while (*nameList) { + CFStringRef name = CFStringCreateWithCString(NULL, *nameList++, kCFStringEncodingASCII); + + if (name) { + CFDictionaryAddValue(table, (const void*)name, (const void*)*encodings); + CFRelease(name); + } + } + } + encodings++; + } + // Adding Unicode (UCS-2) names + CFDictionaryAddValue(table, (const void*)CFSTR("unicode-1-1"), (const void*)kCFStringEncodingUnicode); + CFDictionaryAddValue(table, (const void*)CFSTR("utf-16"), (const void*)kCFStringEncodingUnicode); + CFDictionaryAddValue(table, (const void*)CFSTR("iso-10646-ucs-2"), (const void*)kCFStringEncodingUnicode); + + mappingTable = table; + } + + if (CFDictionaryContainsKey(mappingTable, (const void*)lowerCharsetName)) { + result = (CFStringEncoding)CFDictionaryGetValue(mappingTable, (const void*)lowerCharsetName); + } + + CFRelease(lowerCharsetName); + + return result; +} + +CFStringRef CFStringConvertEncodingToIANACharSetName(CFStringEncoding encoding) { + static CFMutableDictionaryRef mappingTable = NULL; + CFStringRef theName = mappingTable ? (CFStringRef)CFDictionaryGetValue(mappingTable, (const void*)encoding) : NULL; + + if (!theName) { + if (encoding == kCFStringEncodingUnicode) { + theName = CFSTR("UTF-16BE"); + } else { + const char **nameList = CFStringEncodingCanonicalCharsetNames(encoding); + + if (nameList && *nameList) { + CFMutableStringRef upperCaseName; + + theName = CFStringCreateWithCString(NULL, *nameList, kCFStringEncodingASCII); + if (theName) { + upperCaseName = CFStringCreateMutableCopy(NULL, 0, theName); + CFStringUppercase(upperCaseName, 0); + CFRelease(theName); + theName = upperCaseName; + } + } + } + + if (theName) { + if (!mappingTable) mappingTable = CFDictionaryCreateMutable(NULL, 0, (const CFDictionaryKeyCallBacks *)NULL, &kCFTypeDictionaryValueCallBacks); + + CFDictionaryAddValue(mappingTable, (const void*)encoding, (const void*)theName); + CFRelease(theName); + } + } + + return theName; +} + +enum { + NSASCIIStringEncoding = 1, /* 0..127 only */ + NSNEXTSTEPStringEncoding = 2, + NSJapaneseEUCStringEncoding = 3, + NSUTF8StringEncoding = 4, + NSISOLatin1StringEncoding = 5, + NSSymbolStringEncoding = 6, + NSNonLossyASCIIStringEncoding = 7, + NSShiftJISStringEncoding = 8, + NSISOLatin2StringEncoding = 9, + NSUnicodeStringEncoding = 10, + NSWindowsCP1251StringEncoding = 11, /* Cyrillic; same as AdobeStandardCyrillic */ + NSWindowsCP1252StringEncoding = 12, /* WinLatin1 */ + NSWindowsCP1253StringEncoding = 13, /* Greek */ + NSWindowsCP1254StringEncoding = 14, /* Turkish */ + NSWindowsCP1250StringEncoding = 15, /* WinLatin2 */ + NSISO2022JPStringEncoding = 21, /* ISO 2022 Japanese encoding for e-mail */ + NSMacOSRomanStringEncoding = 30, + + NSProprietaryStringEncoding = 65536 /* Installation-specific encoding */ +}; + +#define NSENCODING_MASK (1 << 31) + +UInt32 CFStringConvertEncodingToNSStringEncoding(CFStringEncoding theEncoding) { + if (theEncoding == kCFStringEncodingUTF8) { + return NSUTF8StringEncoding; + } else { + theEncoding &= 0xFFF; + } + switch (theEncoding) { + case kCFStringEncodingASCII: return NSASCIIStringEncoding; + case kCFStringEncodingNextStepLatin: return NSNEXTSTEPStringEncoding; + case kCFStringEncodingISOLatin1: return NSISOLatin1StringEncoding; + case kCFStringEncodingNonLossyASCII: return NSNonLossyASCIIStringEncoding; + case kCFStringEncodingUnicode: return NSUnicodeStringEncoding; + case kCFStringEncodingWindowsLatin1: return NSWindowsCP1252StringEncoding; + case kCFStringEncodingMacRoman: return NSMacOSRomanStringEncoding; + default: + return NSENCODING_MASK | theEncoding; + } +} + +CFStringEncoding CFStringConvertNSStringEncodingToEncoding(UInt32 theEncoding) { + switch (theEncoding) { + case NSASCIIStringEncoding: return kCFStringEncodingASCII; + case NSNEXTSTEPStringEncoding: return kCFStringEncodingNextStepLatin; + case NSUTF8StringEncoding: return kCFStringEncodingUTF8; + case NSISOLatin1StringEncoding: return kCFStringEncodingISOLatin1; + case NSNonLossyASCIIStringEncoding: return kCFStringEncodingNonLossyASCII; + case NSUnicodeStringEncoding: return kCFStringEncodingUnicode; + case NSWindowsCP1252StringEncoding: return kCFStringEncodingWindowsLatin1; + case NSMacOSRomanStringEncoding: return kCFStringEncodingMacRoman; + default: + return ((theEncoding & NSENCODING_MASK) ? theEncoding & ~NSENCODING_MASK : kCFStringEncodingInvalidId); + } +} + +#define MACCODEPAGE_BASE (10000) +#define ISO8859CODEPAGE_BASE (28590) + +static const uint16_t _CFToDOSCodePageList[] = { + 437, -1, -1, -1, -1, 737, 775, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x400 + 850, 851, 852, 855, 857, 860, 861, 862, 863, 864, 865, 866, 869, 874, -1, 01, // 0x410 + 932, 936, 949 , 950, // 0x420 +}; + +static const uint16_t _CFToWindowsCodePageList[] = { + 1252, 1250, 1251, 1253, 1254, 1255, 1256, 1257, 1361, +}; + +UInt32 CFStringConvertEncodingToWindowsCodepage(CFStringEncoding theEncoding) { + if (theEncoding == kCFStringEncodingUTF8) { + return 65001; + } else { + theEncoding &= 0xFFF; + } + return kCFStringEncodingInvalidId; +} + +static const struct { + uint16_t acp; + uint16_t encoding; +} _CFACPToCFTable[] = { + {437,0x0400}, + {737,0x0405}, + {775,0x0406}, + {850,0x0410}, + {851,0x0411}, + {852,0x0412}, + {855,0x0413}, + {857,0x0414}, + {860,0x0415}, + {861,0x0416}, + {862,0x0417}, + {863,0x0418}, + {864,0x0419}, + {865,0x041A}, + {866,0x041B}, + {869,0x041C}, + {874,0x041D}, + {932,0x0420}, + {936,0x0421}, + {949,0x0422}, + {950,0x0423}, + {1250,0x0501}, + {1251,0x0502}, + {1252,0x0500}, + {1253,0x0503}, + {1254,0x0504}, + {1255,0x0505}, + {1256,0x0506}, + {1257,0x0507}, + {1361,0x0510}, + {0xFFFF,0xFFFF}, +}; + +static SInt32 bsearchEncoding(unsigned short target) { + const unsigned int *start, *end, *divider; + unsigned int size = sizeof(_CFACPToCFTable) / sizeof(UInt32); + + start = (const unsigned int*)_CFACPToCFTable; end = (const unsigned int*)_CFACPToCFTable + (size - 1); + while (start <= end) { + divider = start + ((end - start) / 2); + + if (*(const unsigned short*)divider == target) return *((const unsigned short*)divider + 1); + else if (*(const unsigned short*)divider > target) end = divider - 1; + else if (*(const unsigned short*)(divider + 1) > target) return *((const unsigned short*)divider + 1); + else start = divider + 1; + } + return (kCFStringEncodingInvalidId); +} + +CFStringEncoding CFStringConvertWindowsCodepageToEncoding(UInt32 theEncoding) { + if (theEncoding == 0 || theEncoding == 1) { // ID for default (system) codepage + return CFStringGetSystemEncoding(); + } else if (theEncoding < MACCODEPAGE_BASE) { // MS CodePage + return bsearchEncoding(theEncoding); + } else if (theEncoding < 20000) { // MAC ScriptCode + return theEncoding - MACCODEPAGE_BASE; + } else if ((theEncoding - ISO8859CODEPAGE_BASE) < 10) { // ISO8859 range + return (theEncoding - ISO8859CODEPAGE_BASE) + 0x200; + } else { + switch (theEncoding) { + case 65001: return kCFStringEncodingUTF8; + case 20127: return kCFStringEncodingASCII; + default: return kCFStringEncodingInvalidId; + } + } +} + +CFStringEncoding CFStringGetMostCompatibleMacStringEncoding(CFStringEncoding encoding) { + CFStringEncoding macEncoding; + + macEncoding = CFStringEncodingGetScriptCodeForEncoding(encoding); + + return macEncoding; +} + diff --git a/StringEncodings.subproj/CFBuiltinConverters.c b/StringEncodings.subproj/CFBuiltinConverters.c new file mode 100644 index 0000000..2c9c947 --- /dev/null +++ b/StringEncodings.subproj/CFBuiltinConverters.c @@ -0,0 +1,1197 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @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 + +/* Precomposition */ +static const UInt32 __CFLatin1CombiningCharBitmap[] = { // 0x300 ~ 0x35FF + 0xFBB94010, 0x01800000, 0x0000000, +}; + +Boolean CFStringEncodingIsValidCombiningCharacterForLatin1(UniChar character) { + return ((character >= 0x300) && (character < 0x360) && (__CFLatin1CombiningCharBitmap[(character - 0x300) / 32] & (1 << (31 - ((character - 0x300) % 32)))) ? true : false); +} + +UniChar CFStringEncodingPrecomposeLatinCharacter(const UniChar *character, UInt32 numChars, UInt32 *usedChars) { + if (numChars > 0) { + UTF32Char ch = *(character++), nextCh, composedChar; + UInt32 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 Boolean __CFToASCII(UInt32 flags, UniChar character, uint8_t *byte) { + if (character < 0x80) { + *byte = (uint8_t)character; + } else if (character == ParagraphSeparator) { + *byte = ASCIINewLine; + } else { + return false; + } + return true; +} + +static Boolean __CFFromASCII(UInt32 flags, uint8_t byte, UniChar *character) { + if (byte < 0x80) { + *character = (UniChar)byte; + return true; + } else { + return false; + } +} + + +__private_extern__ CFStringEncodingConverter __CFConverterASCII = { + __CFToASCII, __CFFromASCII, 1, 1, kCFStringEncodingConverterCheapEightBit, + NULL, NULL, NULL, NULL, NULL, NULL, +}; + +/* ISO Latin 1 (8859-1) */ +static Boolean __CFToISOLatin1(UInt32 flags, UniChar character, uint8_t *byte) { + if (character <= 0xFF) { + *byte = (uint8_t)character; + } else if (character == ParagraphSeparator) { + *byte = ASCIINewLine; + } else { + return false; + } + + return true; +} + +static Boolean __CFFromISOLatin1(UInt32 flags, uint8_t byte, UniChar *character) { + *character = (UniChar)byte; + return true; +} + +static UInt32 __CFToISOLatin1Precompose(UInt32 flags, const UniChar *character, UInt32 numChars, uint8_t *bytes, UInt32 maxByteLen, UInt32 *usedByteLen) { + uint8_t byte; + UInt32 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__ CFStringEncodingConverter __CFConverterISOLatin1 = { + __CFToISOLatin1, __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 Boolean __CFToMacRoman(UInt32 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 Boolean __CFFromMacRoman(UInt32 flags, uint8_t byte, UniChar *character) { + *character = (byte < 0x80 ? (UniChar)byte : macRoman_to_uni[byte - 0x80]); + return true; +} + +static UInt32 __CFToMacRomanPrecompose(UInt32 flags, const UniChar *character, UInt32 numChars, uint8_t *bytes, UInt32 maxByteLen, UInt32 *usedByteLen) { + uint8_t byte; + UInt32 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__ CFStringEncodingConverter __CFConverterMacRoman = { + __CFToMacRoman, __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 Boolean __CFToWinLatin1(UInt32 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 unsigned short 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 Boolean __CFFromWinLatin1(UInt32 flags, uint8_t byte, UniChar *character) { + *character = (byte < 0x80 || byte > 0x9F ? (UniChar)byte : cp1252_to_uni[byte - 0x80]); + return (*character != 0xFFFD); +} + +static UInt32 __CFToWinLatin1Precompose(UInt32 flags, const UniChar *character, UInt32 numChars, uint8_t *bytes, UInt32 maxByteLen, UInt32 *usedByteLen) { + uint8_t byte; + UInt32 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__ CFStringEncodingConverter __CFConverterWinLatin1 = { + __CFToWinLatin1, __CFFromWinLatin1, 1, 1, kCFStringEncodingConverterCheapEightBit, + NULL, NULL, NULL, NULL, __CFToWinLatin1Precompose, CFStringEncodingIsValidCombiningCharacterForLatin1, +}; + +/* NEXTSTEP Encoding */ +#define NUM_NEXTSTEP_FROM_UNI 128 + +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 }, + { 0x2029, 0x0a }, /* ParagraphSeparator -> ASCIINewLine */ + { 0x2030, 0xbd }, + { 0x2039, 0xac }, + { 0x203a, 0xad }, + { 0x2044, 0xa4 }, + { 0xfb01, 0xae }, + { 0xfb02, 0xaf }, + { 0xfffd, 0xff }, +}; + +static Boolean __CFToNextStepLatin(UInt32 flags, UniChar character, uint8_t *byte) { + if (character < 0x80) { + *byte = (uint8_t)character; + 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 Boolean __CFFromNextStepLatin(UInt32 flags, uint8_t byte, UniChar *character) { + return ((*character = (byte < 0x80 ? (UniChar)byte : NSToPrecompUnicodeTable[byte - 0x80])) != 0xFFFD); +} + +static UInt32 __CFToNextStepLatinPrecompose(UInt32 flags, const UniChar *character, UInt32 numChars, uint8_t *bytes, UInt32 maxByteLen, UInt32 *usedByteLen) { + uint8_t byte; + UInt32 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__ CFStringEncodingConverter __CFConverterNextStepLatin = { + __CFToNextStepLatin, __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 kReplacementCharacter = 0x0000FFFDUL; +static const UInt32 kMaximumUCS2 = 0x0000FFFFUL; +static const UInt32 kMaximumUTF16 = 0x0010FFFFUL; +static const UInt32 kMaximumUCS4 = 0x7FFFFFFFUL; + +static const int halfShift = 10; +static const UInt32 halfBase = 0x0010000UL; +static const UInt32 halfMask = 0x3FFUL; +static const UInt32 kSurrogateHighStart = 0xD800UL; +static const UInt32 kSurrogateHighEnd = 0xDBFFUL; +static const UInt32 kSurrogateLowStart = 0xDC00UL; +static const UInt32 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 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 ch, uint8_t *bytes, UInt32 maxByteLen) { + uint16_t bytesToWrite = __CFUTF8BytesToWriteForCharacter(ch); + const UInt32 byteMask = 0xBF; + const UInt32 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 UInt32 __CFToUTF8(UInt32 flags, const UniChar *characters, UInt32 numChars, uint8_t *bytes, UInt32 maxByteLen, UInt32 *usedByteLen) { + uint16_t bytesWritten; + UInt32 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; + 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, int length) { + uint8_t a; + const uint8_t *srcptr = source+length; + switch (length) { + default: return false; + /* Everything else falls through when "true"... */ + case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; + case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; + case 2: if ((a = (*--srcptr)) > 0xBF) return false; + switch (*source) { + /* no fall-through in this inner switch */ + case 0xE0: if (a < 0xA0) return false; break; + case 0xF0: if (a < 0x90) return false; break; + case 0xF4: if (a > 0x8F) return false; break; + default: if (a < 0x80) return false; + } + case 1: if (*source >= 0x80 && *source < 0xC2) return false; + if (*source > 0xF4) return false; + } + return true; +} + +static UInt32 __CFFromUTF8(UInt32 flags, const uint8_t *bytes, UInt32 numBytes, UniChar *characters, UInt32 maxCharLen, UInt32 *usedCharLen) { + const uint8_t *source = bytes; + uint16_t extraBytesToRead; + UInt32 theUsedCharLen = 0; + UInt32 ch; + Boolean isHFSPlus = (flags & kCFStringEncodingUseHFSPlusCanonical ? true : false); + Boolean needsToDecompose = (flags & kCFStringEncodingUseCanonical || isHFSPlus ? true : false); + Boolean strictUTF8 = (flags & kCFStringEncodingLenientUTF8Conversion ? false : true); + UTF32Char decomposed[MAX_DECOMPOSED_LENGTH]; + int32_t 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 ((strictUTF8 && !__CFIsLegalUTF8(source, extraBytesToRead + 1)) || (extraBytesToRead > 3)) { + 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, (uint32_t *)&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, (uint32_t *)&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 UInt32 __CFToUTF8Len(UInt32 flags, const UniChar *characters, UInt32 numChars) { + UInt32 bytesToWrite = 0; + UInt32 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 UInt32 __CFFromUTF8Len(UInt32 flags, const uint8_t *source, UInt32 numBytes) { + uint16_t extraBytesToRead; + UInt32 theUsedCharLen = 0; + UInt32 ch; + Boolean isHFSPlus = (flags & kCFStringEncodingUseHFSPlusCanonical ? true : false); + Boolean needsToDecompose = (flags & kCFStringEncodingUseCanonical || isHFSPlus ? true : false); + Boolean strictUTF8 = (flags & kCFStringEncodingLenientUTF8Conversion ? false : true); + UTF32Char decomposed[MAX_DECOMPOSED_LENGTH]; + int32_t 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 ((strictUTF8 && !__CFIsLegalUTF8(source, extraBytesToRead + 1)) || (extraBytesToRead > 3)) { + 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__ CFStringEncodingConverter __CFConverterUTF8 = { + __CFToUTF8, __CFFromUTF8, 6, 2, kCFStringEncodingConverterStandard, + __CFToUTF8Len, __CFFromUTF8Len, NULL, NULL, NULL, NULL, +}; diff --git a/StringEncodings.subproj/CFStringEncodingConverter.c b/StringEncodings.subproj/CFStringEncodingConverter.c new file mode 100644 index 0000000..a380f76 --- /dev/null +++ b/StringEncodings.subproj/CFStringEncodingConverter.c @@ -0,0 +1,980 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFStringEncodingConverter.c + Copyright 1998-2002, Apple, Inc. All rights reserved. + Responsibility: Aki Inoue +*/ + +#include "CFInternal.h" +#include +#include +#include "CFUniChar.h" +#include "CFUtilities.h" +#include "CFUnicodeDecomposition.h" +#include "CFStringEncodingConverterExt.h" +#include "CFStringEncodingConverterPriv.h" +#include +#if !defined(__MACOS8__) +#ifdef __WIN32__ +#include +#else // Mach, HP-UX, Solaris +#include +#endif +#endif __MACOS8__ + + +/* Macros +*/ +#define TO_BYTE(conv,flags,chars,numChars,bytes,max,used) (conv->_toBytes ? conv->toBytes(conv,flags,chars,numChars,bytes,max,used) : ((CFStringEncodingToBytesProc)conv->toBytes)(flags,chars,numChars,bytes,max,used)) +#define TO_UNICODE(conv,flags,bytes,numBytes,chars,max,used) (conv->_toUnicode ? (flags & (kCFStringEncodingUseCanonical|kCFStringEncodingUseHFSPlusCanonical) ? conv->toCanonicalUnicode(conv,flags,bytes,numBytes,chars,max,used) : conv->toUnicode(conv,flags,bytes,numBytes,chars,max,used)) : ((CFStringEncodingToUnicodeProc)conv->toUnicode)(flags,bytes,numBytes,chars,max,used)) + +#define LineSeparator 0x2028 +#define ParagraphSeparator 0x2029 +#define ASCIINewLine 0x0a +#define kSurrogateHighStart 0xD800 +#define kSurrogateHighEnd 0xDBFF +#define kSurrogateLowStart 0xDC00 +#define kSurrogateLowEnd 0xDFFF + +/* Mapping 128..255 to lossy ASCII +*/ +static const struct { + unsigned char chars[4]; +} _toLossyASCIITable[] = { + {{' ', 0, 0, 0}}, // NO-BREAK SPACE + {{'!', 0, 0, 0}}, // INVERTED EXCLAMATION MARK + {{'c', 0, 0, 0}}, // CENT SIGN + {{'L', 0, 0, 0}}, // POUND SIGN + {{'$', 0, 0, 0}}, // CURRENCY SIGN + {{'Y', 0, 0, 0}}, // YEN SIGN + {{'|', 0, 0, 0}}, // BROKEN BAR + {{0, 0, 0, 0}}, // SECTION SIGN + {{0, 0, 0, 0}}, // DIAERESIS + {{'(', 'C', ')', 0}}, // COPYRIGHT SIGN + {{'a', 0, 0, 0}}, // FEMININE ORDINAL INDICATOR + {{'<', '<', 0, 0}}, // LEFT-POINTING DOUBLE ANGLE QUOTATION MARK + {{0, 0, 0, 0}}, // NOT SIGN + {{'-', 0, 0, 0}}, // SOFT HYPHEN + {{'(', 'R', ')', 0}}, // REGISTERED SIGN + {{0, 0, 0, 0}}, // MACRON + {{0, 0, 0, 0}}, // DEGREE SIGN + {{'+', '-', 0, 0}}, // PLUS-MINUS SIGN + {{'2', 0, 0, 0}}, // SUPERSCRIPT TWO + {{'3', 0, 0, 0}}, // SUPERSCRIPT THREE + {{0, 0, 0, 0}}, // ACUTE ACCENT + {{0, 0, 0, 0}}, // MICRO SIGN + {{0, 0, 0, 0}}, // PILCROW SIGN + {{0, 0, 0, 0}}, // MIDDLE DOT + {{0, 0, 0, 0}}, // CEDILLA + {{'1', 0, 0, 0}}, // SUPERSCRIPT ONE + {{'o', 0, 0, 0}}, // MASCULINE ORDINAL INDICATOR + {{'>', '>', 0, 0}}, // RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK + {{'1', '/', '4', 0}}, // VULGAR FRACTION ONE QUARTER + {{'1', '/', '2', 0}}, // VULGAR FRACTION ONE HALF + {{'3', '/', '4', 0}}, // VULGAR FRACTION THREE QUARTERS + {{'?', 0, 0, 0}}, // INVERTED QUESTION MARK + {{'A', 0, 0, 0}}, // LATIN CAPITAL LETTER A WITH GRAVE + {{'A', 0, 0, 0}}, // LATIN CAPITAL LETTER A WITH ACUTE + {{'A', 0, 0, 0}}, // LATIN CAPITAL LETTER A WITH CIRCUMFLEX + {{'A', 0, 0, 0}}, // LATIN CAPITAL LETTER A WITH TILDE + {{'A', 0, 0, 0}}, // LATIN CAPITAL LETTER A WITH DIAERESIS + {{'A', 0, 0, 0}}, // LATIN CAPITAL LETTER A WITH RING ABOVE + {{'A', 'E', 0, 0}}, // LATIN CAPITAL LETTER AE + {{'C', 0, 0, 0}}, // LATIN CAPITAL LETTER C WITH CEDILLA + {{'E', 0, 0, 0}}, // LATIN CAPITAL LETTER E WITH GRAVE + {{'E', 0, 0, 0}}, // LATIN CAPITAL LETTER E WITH ACUTE + {{'E', 0, 0, 0}}, // LATIN CAPITAL LETTER E WITH CIRCUMFLEX + {{'E', 0, 0, 0}}, // LATIN CAPITAL LETTER E WITH DIAERESIS + {{'I', 0, 0, 0}}, // LATIN CAPITAL LETTER I WITH GRAVE + {{'I', 0, 0, 0}}, // LATIN CAPITAL LETTER I WITH ACUTE + {{'I', 0, 0, 0}}, // LATIN CAPITAL LETTER I WITH CIRCUMFLEX + {{'I', 0, 0, 0}}, // LATIN CAPITAL LETTER I WITH DIAERESIS + {{'T', 'H', 0, 0}}, // LATIN CAPITAL LETTER ETH (Icelandic) + {{'N', 0, 0, 0}}, // LATIN CAPITAL LETTER N WITH TILDE + {{'O', 0, 0, 0}}, // LATIN CAPITAL LETTER O WITH GRAVE + {{'O', 0, 0, 0}}, // LATIN CAPITAL LETTER O WITH ACUTE + {{'O', 0, 0, 0}}, // LATIN CAPITAL LETTER O WITH CIRCUMFLEX + {{'O', 0, 0, 0}}, // LATIN CAPITAL LETTER O WITH TILDE + {{'O', 0, 0, 0}}, // LATIN CAPITAL LETTER O WITH DIAERESIS + {{'X', 0, 0, 0}}, // MULTIPLICATION SIGN + {{'O', 0, 0, 0}}, // LATIN CAPITAL LETTER O WITH STROKE + {{'U', 0, 0, 0}}, // LATIN CAPITAL LETTER U WITH GRAVE + {{'U', 0, 0, 0}}, // LATIN CAPITAL LETTER U WITH ACUTE + {{'U', 0, 0, 0}}, // LATIN CAPITAL LETTER U WITH CIRCUMFLEX + {{'U', 0, 0, 0}}, // LATIN CAPITAL LETTER U WITH DIAERESIS + {{'Y', 0, 0, 0}}, // LATIN CAPITAL LETTER Y WITH ACUTE + {{'t', 'h', 0, 0}}, // LATIN CAPITAL LETTER THORN (Icelandic) + {{'s', 0, 0, 0}}, // LATIN SMALL LETTER SHARP S (German) + {{'a', 0, 0, 0}}, // LATIN SMALL LETTER A WITH GRAVE + {{'a', 0, 0, 0}}, // LATIN SMALL LETTER A WITH ACUTE + {{'a', 0, 0, 0}}, // LATIN SMALL LETTER A WITH CIRCUMFLEX + {{'a', 0, 0, 0}}, // LATIN SMALL LETTER A WITH TILDE + {{'a', 0, 0, 0}}, // LATIN SMALL LETTER A WITH DIAERESIS + {{'a', 0, 0, 0}}, // LATIN SMALL LETTER A WITH RING ABOVE + {{'a', 'e', 0, 0}}, // LATIN SMALL LETTER AE + {{'c', 0, 0, 0}}, // LATIN SMALL LETTER C WITH CEDILLA + {{'e', 0, 0, 0}}, // LATIN SMALL LETTER E WITH GRAVE + {{'e', 0, 0, 0}}, // LATIN SMALL LETTER E WITH ACUTE + {{'e', 0, 0, 0}}, // LATIN SMALL LETTER E WITH CIRCUMFLEX + {{'e', 0, 0, 0}}, // LATIN SMALL LETTER E WITH DIAERESIS + {{'i', 0, 0, 0}}, // LATIN SMALL LETTER I WITH GRAVE + {{'i', 0, 0, 0}}, // LATIN SMALL LETTER I WITH ACUTE + {{'i', 0, 0, 0}}, // LATIN SMALL LETTER I WITH CIRCUMFLEX + {{'i', 0, 0, 0}}, // LATIN SMALL LETTER I WITH DIAERESIS + {{'T', 'H', 0, 0}}, // LATIN SMALL LETTER ETH (Icelandic) + {{'n', 0, 0, 0}}, // LATIN SMALL LETTER N WITH TILDE + {{'o', 0, 0, 0}}, // LATIN SMALL LETTER O WITH GRAVE + {{'o', 0, 0, 0}}, // LATIN SMALL LETTER O WITH ACUTE + {{'o', 0, 0, 0}}, // LATIN SMALL LETTER O WITH CIRCUMFLEX + {{'o', 0, 0, 0}}, // LATIN SMALL LETTER O WITH TILDE + {{'o', 0, 0, 0}}, // LATIN SMALL LETTER O WITH DIAERESIS + {{'/', 0, 0, 0}}, // DIVISION SIGN + {{'o', 0, 0, 0}}, // LATIN SMALL LETTER O WITH STROKE + {{'u', 0, 0, 0}}, // LATIN SMALL LETTER U WITH GRAVE + {{'u', 0, 0, 0}}, // LATIN SMALL LETTER U WITH ACUTE + {{'u', 0, 0, 0}}, // LATIN SMALL LETTER U WITH CIRCUMFLEX + {{'u', 0, 0, 0}}, // LATIN SMALL LETTER U WITH DIAERESIS + {{'y', 0, 0, 0}}, // LATIN SMALL LETTER Y WITH ACUTE + {{'t', 'h', 0, 0}}, // LATIN SMALL LETTER THORN (Icelandic) + {{'y', 0, 0, 0}}, // LATIN SMALL LETTER Y WITH DIAERESIS +}; + +CF_INLINE UInt32 __CFToASCIILatin1Fallback(UniChar character, UInt8 *bytes, UInt32 maxByteLen) { + const char *losChars = (const unsigned char*)_toLossyASCIITable + (character - 0xA0) * sizeof(unsigned char[4]); + unsigned int numBytes = 0; + int idx, max = (maxByteLen && (maxByteLen < 4) ? maxByteLen : 4); + + for (idx = 0;idx < max;idx++) { + if (losChars[idx]) { + if (maxByteLen) bytes[idx] = losChars[idx]; + ++numBytes; + } else { + break; + } + } + + return numBytes; +} + +static UInt32 __CFDefaultToBytesFallbackProc(const UniChar *characters, UInt32 numChars, uint8_t *bytes, UInt32 maxByteLen, UInt32 *usedByteLen) { + if (*characters < 0xA0) { // 0x80 to 0x9F maps to ASCII C0 range + if (maxByteLen) *bytes = (UInt8)(*characters - 0x80); + *usedByteLen = 1; + return 1; + } else if (*characters < 0x100) { + *usedByteLen = __CFToASCIILatin1Fallback(*characters, bytes, maxByteLen); + return 1; + } else if (*characters >= kSurrogateHighStart && *characters <= kSurrogateLowEnd) { + if (maxByteLen) *bytes = '?'; + *usedByteLen = 1; + return (numChars > 1 && *characters <= kSurrogateLowStart && *(characters + 1) >= kSurrogateLowStart && *(characters + 1) <= kSurrogateLowEnd ? 2 : 1); + } else if (CFUniCharIsMemberOf(*characters, kCFUniCharWhitespaceCharacterSet)) { + if (maxByteLen) *bytes = ' '; + *usedByteLen = 1; + return 1; + } else if (CFUniCharIsMemberOf(*characters, kCFUniCharWhitespaceAndNewlineCharacterSet)) { + if (maxByteLen) *bytes = ASCIINewLine; + *usedByteLen = 1; + return 1; + } else if (!CFUniCharIsMemberOf(*characters, kCFUniCharLetterCharacterSet)) { + *usedByteLen = 0; + return 1; + } else if (CFUniCharIsMemberOf(*characters, kCFUniCharDecomposableCharacterSet)) { + UTF32Char decomposed[MAX_DECOMPOSED_LENGTH]; + + (void)CFUniCharDecomposeCharacter(*characters, decomposed, MAX_DECOMPOSED_LENGTH); + if (*decomposed < 0x80) { + if (maxByteLen) *bytes = (UInt8)(*decomposed); + *usedByteLen = 1; + return 1; + } else { + UTF16Char theChar = *decomposed; + + return __CFDefaultToBytesFallbackProc(&theChar, 1, bytes, maxByteLen, usedByteLen); + } + } else { + if (maxByteLen) *bytes = '?'; + *usedByteLen = 1; + return 1; + } +} + +static UInt32 __CFDefaultToUnicodeFallbackProc(const uint8_t *bytes, UInt32 numBytes, UniChar *characters, UInt32 maxCharLen, UInt32 *usedCharLen) { + if (maxCharLen) *characters = (UniChar)'?'; + *usedCharLen = 1; + return 1; +} + +#define TO_BYTE_FALLBACK(conv,chars,numChars,bytes,max,used) (conv->toBytesFallback(chars,numChars,bytes,max,used)) +#define TO_UNICODE_FALLBACK(conv,bytes,numBytes,chars,max,used) (conv->toUnicodeFallback(bytes,numBytes,chars,max,used)) + +#define EXTRA_BASE (0x0F00) + +/* Wrapper funcs for non-standard converters +*/ +static UInt32 __CFToBytesCheapEightBitWrapper(const void *converter, UInt32 flags, const UniChar *characters, UInt32 numChars, uint8_t *bytes, UInt32 maxByteLen, UInt32 *usedByteLen) { + UInt32 processedCharLen = 0; + UInt32 length = (maxByteLen && (maxByteLen < numChars) ? maxByteLen : numChars); + uint8_t byte; + + while (processedCharLen < length) { + if (!((CFStringEncodingCheapEightBitToBytesProc)((const _CFEncodingConverter*)converter)->_toBytes)(flags, characters[processedCharLen], &byte)) break; + + if (maxByteLen) bytes[processedCharLen] = byte; + processedCharLen++; + } + + *usedByteLen = processedCharLen; + return processedCharLen; +} + +static UInt32 __CFToUnicodeCheapEightBitWrapper(const void *converter, UInt32 flags, const uint8_t *bytes, UInt32 numBytes, UniChar *characters, UInt32 maxCharLen, UInt32 *usedCharLen) { + UInt32 processedByteLen = 0; + UInt32 length = (maxCharLen && (maxCharLen < numBytes) ? maxCharLen : numBytes); + UniChar character; + + while (processedByteLen < length) { + if (!((CFStringEncodingCheapEightBitToUnicodeProc)((const _CFEncodingConverter*)converter)->_toUnicode)(flags, bytes[processedByteLen], &character)) break; + + if (maxCharLen) characters[processedByteLen] = character; + processedByteLen++; + } + + *usedCharLen = processedByteLen; + return processedByteLen; +} + +static UInt32 __CFToCanonicalUnicodeCheapEightBitWrapper(const void *converter, UInt32 flags, const uint8_t *bytes, UInt32 numBytes, UniChar *characters, UInt32 maxCharLen, UInt32 *usedCharLen) { + UInt32 processedByteLen = 0; + UInt32 theUsedCharLen = 0; + UTF32Char charBuffer[MAX_DECOMPOSED_LENGTH]; + UInt32 usedLen; + UniChar character; + bool isHFSPlus = (flags & kCFStringEncodingUseHFSPlusCanonical ? true : false); + + while ((processedByteLen < numBytes) && (!maxCharLen || (theUsedCharLen < maxCharLen))) { + if (!((CFStringEncodingCheapEightBitToUnicodeProc)((const _CFEncodingConverter*)converter)->_toUnicode)(flags, bytes[processedByteLen], &character)) break; + + if (CFUniCharIsDecomposableCharacter(character, isHFSPlus)) { + uint32_t idx; + + usedLen = CFUniCharDecomposeCharacter(character, charBuffer, MAX_DECOMPOSED_LENGTH); + *usedCharLen = theUsedCharLen; + + for (idx = 0;idx < usedLen;idx++) { + if (charBuffer[idx] > 0xFFFF) { // Non-BMP + if (theUsedCharLen + 2 > maxCharLen) return processedByteLen; + theUsedCharLen += 2; + if (maxCharLen) { + charBuffer[idx] = charBuffer[idx] - 0x10000; + *(characters++) = (charBuffer[idx] >> 10) + 0xD800UL; + *(characters++) = (charBuffer[idx] & 0x3FF) + 0xDC00UL; + } + } else { + if (theUsedCharLen + 1 > maxCharLen) return processedByteLen; + ++theUsedCharLen; + *(characters++) = charBuffer[idx]; + } + } + } else { + if (maxCharLen) *(characters++) = character; + ++theUsedCharLen; + } + processedByteLen++; + } + + *usedCharLen = theUsedCharLen; + return processedByteLen; +} + +static UInt32 __CFToBytesStandardEightBitWrapper(const void *converter, UInt32 flags, const UniChar *characters, UInt32 numChars, uint8_t *bytes, UInt32 maxByteLen, UInt32 *usedByteLen) { + UInt32 processedCharLen = 0; + uint8_t byte; + UInt32 usedLen; + + *usedByteLen = 0; + + while (numChars && (!maxByteLen || (*usedByteLen < maxByteLen))) { + if (!(usedLen = ((CFStringEncodingStandardEightBitToBytesProc)((const _CFEncodingConverter*)converter)->_toBytes)(flags, characters, numChars, &byte))) break; + + if (maxByteLen) bytes[*usedByteLen] = byte; + (*usedByteLen)++; + characters += usedLen; + numChars -= usedLen; + processedCharLen += usedLen; + } + + return processedCharLen; +} + +static UInt32 __CFToUnicodeStandardEightBitWrapper(const void *converter, UInt32 flags, const uint8_t *bytes, UInt32 numBytes, UniChar *characters, UInt32 maxCharLen, UInt32 *usedCharLen) { + UInt32 processedByteLen = 0; +#if defined(__MACOS8__) || defined(__WIN32__) + UniChar charBuffer[20]; // Dynamic stack allocation is GNU specific +#else + UniChar charBuffer[((const _CFEncodingConverter*)converter)->maxLen]; +#endif + UInt32 usedLen; + + *usedCharLen = 0; + + while ((processedByteLen < numBytes) && (!maxCharLen || (*usedCharLen < maxCharLen))) { + if (!(usedLen = ((CFStringEncodingCheapEightBitToUnicodeProc)((const _CFEncodingConverter*)converter)->_toUnicode)(flags, bytes[processedByteLen], charBuffer))) break; + + if (maxCharLen) { + uint16_t idx; + + if (*usedCharLen + usedLen > maxCharLen) break; + + for (idx = 0;idx < usedLen;idx++) { + characters[*usedCharLen + idx] = charBuffer[idx]; + } + } + *usedCharLen += usedLen; + processedByteLen++; + } + + return processedByteLen; +} + +static UInt32 __CFToCanonicalUnicodeStandardEightBitWrapper(const void *converter, UInt32 flags, const uint8_t *bytes, UInt32 numBytes, UniChar *characters, UInt32 maxCharLen, UInt32 *usedCharLen) { + UInt32 processedByteLen = 0; +#if defined(__MACOS8__) || defined(__WIN32__) + UniChar charBuffer[20]; // Dynamic stack allocation is GNU specific +#else + UniChar charBuffer[((const _CFEncodingConverter*)converter)->maxLen]; +#endif + UTF32Char decompBuffer[MAX_DECOMPOSED_LENGTH]; + UInt32 usedLen; + UInt32 decompedLen; + UInt32 idx, decompIndex; + bool isHFSPlus = (flags & kCFStringEncodingUseHFSPlusCanonical ? true : false); + UInt32 theUsedCharLen = 0; + + while ((processedByteLen < numBytes) && (!maxCharLen || (theUsedCharLen < maxCharLen))) { + if (!(usedLen = ((CFStringEncodingCheapEightBitToUnicodeProc)((const _CFEncodingConverter*)converter)->_toUnicode)(flags, bytes[processedByteLen], charBuffer))) break; + + for (idx = 0;idx < usedLen;idx++) { + if (CFUniCharIsDecomposableCharacter(charBuffer[idx], isHFSPlus)) { + decompedLen = CFUniCharDecomposeCharacter(charBuffer[idx], decompBuffer, MAX_DECOMPOSED_LENGTH); + *usedCharLen = theUsedCharLen; + + for (decompIndex = 0;decompIndex < decompedLen;decompIndex++) { + if (decompBuffer[decompIndex] > 0xFFFF) { // Non-BMP + if (theUsedCharLen + 2 > maxCharLen) return processedByteLen; + theUsedCharLen += 2; + if (maxCharLen) { + charBuffer[idx] = charBuffer[idx] - 0x10000; + *(characters++) = (charBuffer[idx] >> 10) + 0xD800UL; + *(characters++) = (charBuffer[idx] & 0x3FF) + 0xDC00UL; + } + } else { + if (theUsedCharLen + 1 > maxCharLen) return processedByteLen; + ++theUsedCharLen; + *(characters++) = charBuffer[idx]; + } + } + } else { + if (maxCharLen) *(characters++) = charBuffer[idx]; + ++theUsedCharLen; + } + } + processedByteLen++; + } + + *usedCharLen = theUsedCharLen; + return processedByteLen; +} + +static UInt32 __CFToBytesCheapMultiByteWrapper(const void *converter, UInt32 flags, const UniChar *characters, UInt32 numChars, uint8_t *bytes, UInt32 maxByteLen, UInt32 *usedByteLen) { + UInt32 processedCharLen = 0; +#if defined(__MACOS8__) || defined(__WIN32__) + uint8_t byteBuffer[20]; // Dynamic stack allocation is GNU specific +#else + uint8_t byteBuffer[((const _CFEncodingConverter*)converter)->maxLen]; +#endif + UInt32 usedLen; + + *usedByteLen = 0; + + while ((processedCharLen < numChars) && (!maxByteLen || (*usedByteLen < maxByteLen))) { + if (!(usedLen = ((CFStringEncodingCheapMultiByteToBytesProc)((const _CFEncodingConverter*)converter)->_toBytes)(flags, characters[processedCharLen], byteBuffer))) break; + + if (maxByteLen) { + uint16_t idx; + + if (*usedByteLen + usedLen > maxByteLen) break; + + for (idx = 0;idx _toUnicode)(flags, bytes, numBytes, &character))) break; + + if (maxCharLen) *(characters++) = character; + (*usedCharLen)++; + processedByteLen += usedLen; + bytes += usedLen; + numBytes -= usedLen; + } + + return processedByteLen; +} + +static UInt32 __CFToCanonicalUnicodeCheapMultiByteWrapper(const void *converter, UInt32 flags, const uint8_t *bytes, UInt32 numBytes, UniChar *characters, UInt32 maxCharLen, UInt32 *usedCharLen) { + UInt32 processedByteLen = 0; + UTF32Char charBuffer[MAX_DECOMPOSED_LENGTH]; + UniChar character; + UInt32 usedLen; + UInt32 decomposedLen; + UInt32 theUsedCharLen = 0; + bool isHFSPlus = (flags & kCFStringEncodingUseHFSPlusCanonical ? true : false); + + while (numBytes && (!maxCharLen || (theUsedCharLen < maxCharLen))) { + if (!(usedLen = ((CFStringEncodingCheapMultiByteToUnicodeProc)((const _CFEncodingConverter*)converter)->_toUnicode)(flags, bytes, numBytes, &character))) break; + + if (CFUniCharIsDecomposableCharacter(character, isHFSPlus)) { + uint32_t idx; + + decomposedLen = CFUniCharDecomposeCharacter(character, charBuffer, MAX_DECOMPOSED_LENGTH); + *usedCharLen = theUsedCharLen; + + for (idx = 0;idx < decomposedLen;idx++) { + if (charBuffer[idx] > 0xFFFF) { // Non-BMP + if (theUsedCharLen + 2 > maxCharLen) return processedByteLen; + theUsedCharLen += 2; + if (maxCharLen) { + charBuffer[idx] = charBuffer[idx] - 0x10000; + *(characters++) = (charBuffer[idx] >> 10) + 0xD800UL; + *(characters++) = (charBuffer[idx] & 0x3FF) + 0xDC00UL; + } + } else { + if (theUsedCharLen + 1 > maxCharLen) return processedByteLen; + ++theUsedCharLen; + *(characters++) = charBuffer[idx]; + } + } + } else { + if (maxCharLen) *(characters++) = character; + ++theUsedCharLen; + } + + processedByteLen += usedLen; + bytes += usedLen; + numBytes -= usedLen; + } + *usedCharLen = theUsedCharLen; + return processedByteLen; +} + +/* static functions +*/ +static _CFConverterEntry __CFConverterEntryASCII = { + kCFStringEncodingASCII, NULL, + "Western (ASCII)", {"us-ascii", "ascii", "iso-646-us", NULL}, NULL, NULL, NULL, NULL, + kCFStringEncodingMacRoman // We use string encoding's script range here +}; + +static _CFConverterEntry __CFConverterEntryISOLatin1 = { + kCFStringEncodingISOLatin1, NULL, + "Western (ISO Latin 1)", {"iso-8859-1", "latin1","iso-latin-1", NULL}, NULL, NULL, NULL, NULL, + kCFStringEncodingMacRoman // We use string encoding's script range here +}; + +static _CFConverterEntry __CFConverterEntryMacRoman = { + kCFStringEncodingMacRoman, NULL, + "Western (Mac OS Roman)", {"macintosh", "mac", "x-mac-roman", NULL}, NULL, NULL, NULL, NULL, + kCFStringEncodingMacRoman // We use string encoding's script range here +}; + +static _CFConverterEntry __CFConverterEntryWinLatin1 = { + kCFStringEncodingWindowsLatin1, NULL, + "Western (Windows Latin 1)", {"windows-1252", "cp1252", "windows latin1", NULL}, NULL, NULL, NULL, NULL, + kCFStringEncodingMacRoman // We use string encoding's script range here +}; + +static _CFConverterEntry __CFConverterEntryNextStepLatin = { + kCFStringEncodingNextStepLatin, NULL, + "Western (NextStep)", {"x-nextstep", NULL, NULL, NULL}, NULL, NULL, NULL, NULL, + kCFStringEncodingMacRoman // We use string encoding's script range here +}; + +static _CFConverterEntry __CFConverterEntryUTF8 = { + kCFStringEncodingUTF8, NULL, + "UTF-8", {"utf-8", "unicode-1-1-utf8", NULL, NULL}, NULL, NULL, NULL, NULL, + kCFStringEncodingUnicode // We use string encoding's script range here +}; + +CF_INLINE _CFConverterEntry *__CFStringEncodingConverterGetEntry(UInt32 encoding) { + switch (encoding) { + case kCFStringEncodingInvalidId: + case kCFStringEncodingASCII: + return &__CFConverterEntryASCII; + + case kCFStringEncodingISOLatin1: + return &__CFConverterEntryISOLatin1; + + case kCFStringEncodingMacRoman: + return &__CFConverterEntryMacRoman; + + case kCFStringEncodingWindowsLatin1: + return &__CFConverterEntryWinLatin1; + + case kCFStringEncodingNextStepLatin: + return &__CFConverterEntryNextStepLatin; + + case kCFStringEncodingUTF8: + return &__CFConverterEntryUTF8; + + default: return NULL; + } +} + +CF_INLINE _CFEncodingConverter *__CFEncodingConverterFromDefinition(const CFStringEncodingConverter *definition) { +#define NUM_OF_ENTRIES_CYCLE (10) + static CFSpinLock_t _indexLock = 0; + static UInt32 _currentIndex = 0; + static UInt32 _allocatedSize = 0; + static _CFEncodingConverter *_allocatedEntries = NULL; + _CFEncodingConverter *converter; + + + __CFSpinLock(&_indexLock); + if ((_currentIndex + 1) >= _allocatedSize) { + _currentIndex = 0; + _allocatedSize = 0; + _allocatedEntries = NULL; + } + if (_allocatedEntries == NULL) { // Not allocated yet + _allocatedEntries = (_CFEncodingConverter *)CFAllocatorAllocate(NULL, sizeof(_CFEncodingConverter) * NUM_OF_ENTRIES_CYCLE, 0); + _allocatedSize = NUM_OF_ENTRIES_CYCLE; + converter = &(_allocatedEntries[_currentIndex]); + } else { + converter = &(_allocatedEntries[++_currentIndex]); + } + __CFSpinUnlock(&_indexLock); + + switch (definition->encodingClass) { + case kCFStringEncodingConverterStandard: + converter->toBytes = definition->toBytes; + converter->toUnicode = definition->toUnicode; + converter->toCanonicalUnicode = definition->toUnicode; + converter->_toBytes = NULL; + converter->_toUnicode = NULL; + converter->maxLen = 2; + break; + + case kCFStringEncodingConverterCheapEightBit: + converter->toBytes = __CFToBytesCheapEightBitWrapper; + converter->toUnicode = __CFToUnicodeCheapEightBitWrapper; + converter->toCanonicalUnicode = __CFToCanonicalUnicodeCheapEightBitWrapper; + converter->_toBytes = definition->toBytes; + converter->_toUnicode = definition->toUnicode; + converter->maxLen = 1; + break; + + case kCFStringEncodingConverterStandardEightBit: + converter->toBytes = __CFToBytesStandardEightBitWrapper; + converter->toUnicode = __CFToUnicodeStandardEightBitWrapper; + converter->toCanonicalUnicode = __CFToCanonicalUnicodeStandardEightBitWrapper; + converter->_toBytes = definition->toBytes; + converter->_toUnicode = definition->toUnicode; + converter->maxLen = definition->maxDecomposedCharLen; + break; + + case kCFStringEncodingConverterCheapMultiByte: + converter->toBytes = __CFToBytesCheapMultiByteWrapper; + converter->toUnicode = __CFToUnicodeCheapMultiByteWrapper; + converter->toCanonicalUnicode = __CFToCanonicalUnicodeCheapMultiByteWrapper; + converter->_toBytes = definition->toBytes; + converter->_toUnicode = definition->toUnicode; + converter->maxLen = definition->maxBytesPerChar; + break; + + case kCFStringEncodingConverterPlatformSpecific: + converter->toBytes = NULL; + converter->toUnicode = NULL; + converter->toCanonicalUnicode = NULL; + converter->_toBytes = NULL; + converter->_toUnicode = NULL; + converter->maxLen = 0; + converter->toBytesLen = NULL; + converter->toUnicodeLen = NULL; + converter->toBytesFallback = NULL; + converter->toUnicodeFallback = NULL; + converter->toBytesPrecompose = NULL; + converter->isValidCombiningChar = NULL; + return converter; + + default: // Shouln't be here + return NULL; + } + + converter->toBytesLen = (definition->toBytesLen ? definition->toBytesLen : (CFStringEncodingToBytesLenProc)(UInt32)definition->maxBytesPerChar); + converter->toUnicodeLen = (definition->toUnicodeLen ? definition->toUnicodeLen : (CFStringEncodingToUnicodeLenProc)(UInt32)definition->maxDecomposedCharLen); + converter->toBytesFallback = (definition->toBytesFallback ? definition->toBytesFallback : __CFDefaultToBytesFallbackProc); + converter->toUnicodeFallback = (definition->toUnicodeFallback ? definition->toUnicodeFallback : __CFDefaultToUnicodeFallbackProc); + converter->toBytesPrecompose = (definition->toBytesPrecompose ? definition->toBytesPrecompose : NULL); + converter->isValidCombiningChar = (definition->isValidCombiningChar ? definition->isValidCombiningChar : NULL); + + return converter; +} + +CF_INLINE const CFStringEncodingConverter *__CFStringEncodingConverterGetDefinition(_CFConverterEntry *entry) { + if (!entry) return NULL; + + switch (entry->encoding) { + case kCFStringEncodingASCII: + return &__CFConverterASCII; + + case kCFStringEncodingISOLatin1: + return &__CFConverterISOLatin1; + + case kCFStringEncodingMacRoman: + return &__CFConverterMacRoman; + + case kCFStringEncodingWindowsLatin1: + return &__CFConverterWinLatin1; + + case kCFStringEncodingNextStepLatin: + return &__CFConverterNextStepLatin; + + case kCFStringEncodingUTF8: + return &__CFConverterUTF8; + + default: + return NULL; + } +} + +static const _CFEncodingConverter *__CFGetConverter(UInt32 encoding) { + _CFConverterEntry *entry = __CFStringEncodingConverterGetEntry(encoding); + + if (!entry) return NULL; + + if (!entry->converter) { + const CFStringEncodingConverter *definition = __CFStringEncodingConverterGetDefinition(entry); + + if (definition) { + entry->converter = __CFEncodingConverterFromDefinition(definition); + entry->toBytesFallback = definition->toBytesFallback; + entry->toUnicodeFallback = definition->toUnicodeFallback; + } + } + + return (_CFEncodingConverter *)entry->converter; +} + +/* Public API +*/ +UInt32 CFStringEncodingUnicodeToBytes(UInt32 encoding, UInt32 flags, const UniChar *characters, UInt32 numChars, UInt32 *usedCharLen, uint8_t *bytes, UInt32 maxByteLen, UInt32 *usedByteLen) { + if (encoding == kCFStringEncodingUTF8) { + static CFStringEncodingToBytesProc __CFToUTF8 = NULL; + uint32_t convertedCharLen; + uint32_t usedLen; + + + if ((flags & kCFStringEncodingUseCanonical) || (flags & kCFStringEncodingUseHFSPlusCanonical)) { + (void)CFUniCharDecompose(characters, numChars, &convertedCharLen, (void *)bytes, maxByteLen, &usedLen, true, kCFUniCharUTF8Format, (flags & kCFStringEncodingUseHFSPlusCanonical ? true : false)); + } else { + if (!__CFToUTF8) { + const CFStringEncodingConverter *utf8Converter = CFStringEncodingGetConverter(kCFStringEncodingUTF8); + __CFToUTF8 = (CFStringEncodingToBytesProc)utf8Converter->toBytes; + } + convertedCharLen = __CFToUTF8(0, characters, numChars, bytes, maxByteLen, (UInt32 *)&usedLen); + } + if (usedCharLen) *usedCharLen = convertedCharLen; + if (usedByteLen) *usedByteLen = usedLen; + + if (convertedCharLen == numChars) { + return kCFStringEncodingConversionSuccess; + } else if (maxByteLen && (maxByteLen == usedLen)) { + return kCFStringEncodingInsufficientOutputBufferLength; + } else { + return kCFStringEncodingInvalidInputStream; + } + } else { + const _CFEncodingConverter *converter = __CFGetConverter(encoding); + UInt32 usedLen = 0; + UInt32 localUsedByteLen; + UInt32 theUsedByteLen = 0; + UInt32 theResult = kCFStringEncodingConversionSuccess; + CFStringEncodingToBytesPrecomposeProc toBytesPrecompose = NULL; + CFStringEncodingIsValidCombiningCharacterProc isValidCombiningChar = NULL; + + if (!converter) return kCFStringEncodingConverterUnavailable; + + if (flags & kCFStringEncodingSubstituteCombinings) { + if (!(flags & kCFStringEncodingAllowLossyConversion)) isValidCombiningChar = converter->isValidCombiningChar; + } else { + isValidCombiningChar = converter->isValidCombiningChar; + if (!(flags & kCFStringEncodingIgnoreCombinings)) { + toBytesPrecompose = converter->toBytesPrecompose; + flags |= kCFStringEncodingComposeCombinings; + } + } + + + while ((usedLen < numChars) && (!maxByteLen || (theUsedByteLen < maxByteLen))) { + if ((usedLen += TO_BYTE(converter, flags, characters + usedLen, numChars - usedLen, bytes + theUsedByteLen, (maxByteLen ? maxByteLen - theUsedByteLen : 0), &localUsedByteLen)) < numChars) { + UInt32 dummy; + + if (isValidCombiningChar && (usedLen > 0) && isValidCombiningChar(characters[usedLen])) { + if (toBytesPrecompose) { + UInt32 localUsedLen = usedLen; + + while (isValidCombiningChar(characters[--usedLen])); + theUsedByteLen += localUsedByteLen; + if (converter->maxLen > 1) { + TO_BYTE(converter, flags, characters + usedLen, localUsedLen - usedLen, NULL, 0, &localUsedByteLen); + theUsedByteLen -= localUsedByteLen; + } else { + theUsedByteLen--; + } + if ((localUsedLen = toBytesPrecompose(flags, characters + usedLen, numChars - usedLen, bytes + theUsedByteLen, (maxByteLen ? maxByteLen - theUsedByteLen : 0), &localUsedByteLen)) > 0) { + usedLen += localUsedLen; + if ((usedLen < numChars) && isValidCombiningChar(characters[usedLen])) { // There is a non-base char not combined remaining + theUsedByteLen += localUsedByteLen; + theResult = kCFStringEncodingInvalidInputStream; + break; + } + } else if (flags & kCFStringEncodingAllowLossyConversion) { + uint8_t lossyByte = CFStringEncodingMaskToLossyByte(flags); + + if (lossyByte) { + while (isValidCombiningChar(characters[++usedLen])); + localUsedByteLen = 1; + if (maxByteLen) *(bytes + theUsedByteLen) = lossyByte; + } else { + ++usedLen; + usedLen += TO_BYTE_FALLBACK(converter, characters + usedLen, numChars - usedLen, bytes + theUsedByteLen, (maxByteLen ? maxByteLen - theUsedByteLen : 0), &localUsedByteLen); + } + } else { + theResult = kCFStringEncodingInvalidInputStream; + break; + } + } else if (maxByteLen && ((maxByteLen == theUsedByteLen + localUsedByteLen) || TO_BYTE(converter, flags, characters + usedLen, numChars - usedLen, NULL, 0, &dummy))) { // buffer was filled up + theUsedByteLen += localUsedByteLen; + theResult = kCFStringEncodingInsufficientOutputBufferLength; + break; + } else if (flags & kCFStringEncodingIgnoreCombinings) { + while ((++usedLen < numChars) && isValidCombiningChar(characters[usedLen])); + } else { + uint8_t lossyByte = CFStringEncodingMaskToLossyByte(flags); + + theUsedByteLen += localUsedByteLen; + if (lossyByte) { + ++usedLen; + localUsedByteLen = 1; + if (maxByteLen) *(bytes + theUsedByteLen) = lossyByte; + } else { + usedLen += TO_BYTE_FALLBACK(converter, characters + usedLen, numChars - usedLen, bytes + theUsedByteLen, (maxByteLen ? maxByteLen - theUsedByteLen : 0), &localUsedByteLen); + } + } + } else if (maxByteLen && ((maxByteLen == theUsedByteLen + localUsedByteLen) || TO_BYTE(converter, flags, characters + usedLen, numChars - usedLen, NULL, 0, &dummy))) { // buffer was filled up + theUsedByteLen += localUsedByteLen; + + if (flags & kCFStringEncodingAllowLossyConversion && !CFStringEncodingMaskToLossyByte(flags)) { + UInt32 localUsedLen; + + localUsedByteLen = 0; + while ((usedLen < numChars) && !localUsedByteLen && (localUsedLen = TO_BYTE_FALLBACK(converter, characters + usedLen, numChars - usedLen, NULL, 0, &localUsedByteLen))) usedLen += localUsedLen; + } + if (usedLen < numChars) theResult = kCFStringEncodingInsufficientOutputBufferLength; + break; + } else if (flags & kCFStringEncodingAllowLossyConversion) { + uint8_t lossyByte = CFStringEncodingMaskToLossyByte(flags); + + theUsedByteLen += localUsedByteLen; + if (lossyByte) { + ++usedLen; + localUsedByteLen = 1; + if (maxByteLen) *(bytes + theUsedByteLen) = lossyByte; + } else { + usedLen += TO_BYTE_FALLBACK(converter, characters + usedLen, numChars - usedLen, bytes + theUsedByteLen, (maxByteLen ? maxByteLen - theUsedByteLen : 0), &localUsedByteLen); + } + } else { + theUsedByteLen += localUsedByteLen; + theResult = kCFStringEncodingInvalidInputStream; + break; + } + } + theUsedByteLen += localUsedByteLen; + } + + if (usedLen < numChars && maxByteLen && theResult == kCFStringEncodingConversionSuccess) { + if (flags & kCFStringEncodingAllowLossyConversion && !CFStringEncodingMaskToLossyByte(flags)) { + UInt32 localUsedLen; + + localUsedByteLen = 0; + while ((usedLen < numChars) && !localUsedByteLen && (localUsedLen = TO_BYTE_FALLBACK(converter, characters + usedLen, numChars - usedLen, NULL, 0, &localUsedByteLen))) usedLen += localUsedLen; + } + if (usedLen < numChars) theResult = kCFStringEncodingInsufficientOutputBufferLength; + } + if (usedByteLen) *usedByteLen = theUsedByteLen; + if (usedCharLen) *usedCharLen = usedLen; + + return theResult; + } +} + +UInt32 CFStringEncodingBytesToUnicode(UInt32 encoding, UInt32 flags, const uint8_t *bytes, UInt32 numBytes, UInt32 *usedByteLen, UniChar *characters, UInt32 maxCharLen, UInt32 *usedCharLen) { + const _CFEncodingConverter *converter = __CFGetConverter(encoding); + UInt32 usedLen = 0; + UInt32 theUsedCharLen = 0; + UInt32 localUsedCharLen; + UInt32 theResult = kCFStringEncodingConversionSuccess; + + if (!converter) return kCFStringEncodingConverterUnavailable; + + + while ((usedLen < numBytes) && (!maxCharLen || (theUsedCharLen < maxCharLen))) { + if ((usedLen += TO_UNICODE(converter, flags, bytes + usedLen, numBytes - usedLen, characters + theUsedCharLen, (maxCharLen ? maxCharLen - theUsedCharLen : 0), &localUsedCharLen)) < numBytes) { + UInt32 tempUsedCharLen; + + if (maxCharLen && ((maxCharLen == theUsedCharLen + localUsedCharLen) || ((flags & (kCFStringEncodingUseCanonical|kCFStringEncodingUseHFSPlusCanonical)) && TO_UNICODE(converter, flags, bytes + usedLen, numBytes - usedLen, NULL, 0, &tempUsedCharLen)))) { // buffer was filled up + theUsedCharLen += localUsedCharLen; + theResult = kCFStringEncodingInsufficientOutputBufferLength; + break; + } else if (flags & kCFStringEncodingAllowLossyConversion) { + theUsedCharLen += localUsedCharLen; + usedLen += TO_UNICODE_FALLBACK(converter, bytes + usedLen, numBytes - usedLen, characters + theUsedCharLen, (maxCharLen ? maxCharLen - theUsedCharLen : 0), &localUsedCharLen); + } else { + theUsedCharLen += localUsedCharLen; + theResult = kCFStringEncodingInvalidInputStream; + break; + } + } + theUsedCharLen += localUsedCharLen; + } + + if (usedLen < numBytes && maxCharLen && theResult == kCFStringEncodingConversionSuccess) { + theResult = kCFStringEncodingInsufficientOutputBufferLength; + } + if (usedCharLen) *usedCharLen = theUsedCharLen; + if (usedByteLen) *usedByteLen = usedLen; + + return theResult; +} + +__private_extern__ Boolean CFStringEncodingIsValidEncoding(UInt32 encoding) { + return (CFStringEncodingGetConverter(encoding) ? true : false); +} + +__private_extern__ const char *CFStringEncodingName(UInt32 encoding) { + _CFConverterEntry *entry = __CFStringEncodingConverterGetEntry(encoding); + if (entry) return entry->encodingName; + return NULL; +} + +__private_extern__ const char **CFStringEncodingCanonicalCharsetNames(UInt32 encoding) { + _CFConverterEntry *entry = __CFStringEncodingConverterGetEntry(encoding); + if (entry) return entry->ianaNames; + return NULL; +} + +__private_extern__ UInt32 CFStringEncodingGetScriptCodeForEncoding(CFStringEncoding encoding) { + _CFConverterEntry *entry = __CFStringEncodingConverterGetEntry(encoding); + + return (entry ? entry->scriptCode : (encoding == kCFStringEncodingUnicode ? kCFStringEncodingUnicode : (encoding < 0xFF ? encoding : kCFStringEncodingInvalidId))); +} + +__private_extern__ UInt32 CFStringEncodingCharLengthForBytes(UInt32 encoding, UInt32 flags, const uint8_t *bytes, UInt32 numBytes) { + const _CFEncodingConverter *converter = __CFGetConverter(encoding); + + if (converter) { + UInt32 switchVal = (UInt32)(converter->toUnicodeLen); + + if (switchVal < 0xFFFF) + return switchVal * numBytes; + else + return converter->toUnicodeLen(flags, bytes, numBytes); + } + + return 0; +} + +__private_extern__ UInt32 CFStringEncodingByteLengthForCharacters(UInt32 encoding, UInt32 flags, const UniChar *characters, UInt32 numChars) { + const _CFEncodingConverter *converter = __CFGetConverter(encoding); + + if (converter) { + UInt32 switchVal = (UInt32)(converter->toBytesLen); + + if (switchVal < 0xFFFF) + return switchVal * numChars; + else + return converter->toBytesLen(flags, characters, numChars); + } + + return 0; +} + +__private_extern__ void CFStringEncodingRegisterFallbackProcedures(UInt32 encoding, CFStringEncodingToBytesFallbackProc toBytes, CFStringEncodingToUnicodeFallbackProc toUnicode) { + _CFConverterEntry *entry = __CFStringEncodingConverterGetEntry(encoding); + + if (entry && __CFGetConverter(encoding)) { + ((_CFEncodingConverter*)entry->converter)->toBytesFallback = (toBytes ? toBytes : entry->toBytesFallback); + ((_CFEncodingConverter*)entry->converter)->toUnicodeFallback = (toUnicode ? toUnicode : entry->toUnicodeFallback); + } +} + +__private_extern__ const CFStringEncodingConverter *CFStringEncodingGetConverter(UInt32 encoding) { + return __CFStringEncodingConverterGetDefinition(__CFStringEncodingConverterGetEntry(encoding)); +} + +static const UInt32 __CFBuiltinEncodings[] = { + kCFStringEncodingMacRoman, + kCFStringEncodingWindowsLatin1, + kCFStringEncodingISOLatin1, + kCFStringEncodingNextStepLatin, + kCFStringEncodingASCII, + kCFStringEncodingUTF8, + /* These two are available only in CFString-level */ + kCFStringEncodingUnicode, + kCFStringEncodingNonLossyASCII, + kCFStringEncodingInvalidId, +}; + + +__private_extern__ const UInt32 *CFStringEncodingListOfAvailableEncodings(void) { + return __CFBuiltinEncodings; +} + diff --git a/StringEncodings.subproj/CFStringEncodingConverter.h b/StringEncodings.subproj/CFStringEncodingConverter.h new file mode 100644 index 0000000..e5a3b5b --- /dev/null +++ b/StringEncodings.subproj/CFStringEncodingConverter.h @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFStringEncodingConverter.h + Copyright (c) 1998-2003, Apple, Inc. All rights reserved. +*/ + +#ifndef __CFSTRINGENCODINGCONVERTER__ +#define __CFSTRINGENCODINGCONVERTER__ 1 + +#include + + +#if defined(__cplusplus) +extern "C" { +#endif + +/* Values for flags argument for the conversion functions below. These can be combined, but the three NonSpacing behavior flags are exclusive. +*/ +enum { + kCFStringEncodingAllowLossyConversion = 1, // Uses fallback functions to substitutes non mappable chars + kCFStringEncodingBasicDirectionLeftToRight = (1 << 1), // Converted with original direction left-to-right. + kCFStringEncodingBasicDirectionRightToLeft = (1 << 2), // Converted with original direction right-to-left. + kCFStringEncodingSubstituteCombinings = (1 << 3), // Uses fallback function to combining chars. + kCFStringEncodingComposeCombinings = (1 << 4), // Checks mappable precomposed equivalents for decomposed sequences. This is the default behavior. + kCFStringEncodingIgnoreCombinings = (1 << 5), // Ignores combining chars. + kCFStringEncodingUseCanonical = (1 << 6), // Always use canonical form + kCFStringEncodingUseHFSPlusCanonical = (1 << 7), // Always use canonical form but leaves 0x2000 ranges + kCFStringEncodingPrependBOM = (1 << 8), // Prepend BOM sequence (i.e. ISO2022KR) + kCFStringEncodingDisableCorporateArea = (1 << 9), // Disable the usage of 0xF8xx area for Apple proprietary chars in converting to UniChar, resulting loosely mapping. + kCFStringEncodingASCIICompatibleConversion = (1 << 10), // This flag forces strict ASCII compatible converion. i.e. MacJapanese 0x5C maps to Unicode 0x5C. + kCFStringEncodingLenientUTF8Conversion = (1 << 11) // 10.1 (Puma) compatible lenient UTF-8 conversion. +}; + +/* Return values for CFStringEncodingUnicodeToBytes & CFStringEncodingBytesToUnicode functions +*/ +enum { + kCFStringEncodingConversionSuccess = 0, + kCFStringEncodingInvalidInputStream = 1, + kCFStringEncodingInsufficientOutputBufferLength = 2, + kCFStringEncodingConverterUnavailable = 3 +}; + +/* Macro to shift lossByte argument. +*/ +#define CFStringEncodingLossyByteToMask(lossByte) ((UInt32)(lossByte << 24)|kCFStringEncodingAllowLossyConversion) +#define CFStringEncodingMaskToLossyByte(flags) ((UInt8)(flags >> 24)) + +/* Converts characters into the specified encoding. Returns the constants defined above. +If maxByteLen is 0, bytes is ignored. You can pass lossyByte by passing the value in flags argument. +i.e. CFStringEncodingUnicodeToBytes(encoding, CFStringEncodingLossyByteToMask(lossByte), ....) +*/ +extern UInt32 CFStringEncodingUnicodeToBytes(UInt32 encoding, UInt32 flags, const UniChar *characters, UInt32 numChars, UInt32 *usedCharLen, UInt8 *bytes, UInt32 maxByteLen, UInt32 *usedByteLen); + +/* Converts bytes in the specified encoding into unicode. Returns the constants defined above. +maxCharLen & usdCharLen are in UniChar length, not byte length. +If maxCharLen is 0, characters is ignored. +*/ +extern UInt32 CFStringEncodingBytesToUnicode(UInt32 encoding, UInt32 flags, const UInt8 *bytes, UInt32 numBytes, UInt32 *usedByteLen, UniChar *characters, UInt32 maxCharLen, UInt32 *usedCharLen); + +/* Fallback functions used when allowLossy +*/ +typedef UInt32 (*CFStringEncodingToBytesFallbackProc)(const UniChar *characters, UInt32 numChars, UInt8 *bytes, UInt32 maxByteLen, UInt32 *usedByteLen); +typedef UInt32 (*CFStringEncodingToUnicodeFallbackProc)(const UInt8 *bytes, UInt32 numBytes, UniChar *characters, UInt32 maxCharLen, UInt32 *usedCharLen); + +extern Boolean CFStringEncodingIsValidEncoding(UInt32 encoding); + +/* Returns kCFStringEncodingInvalidId terminated encoding list +*/ +extern const UInt32 *CFStringEncodingListOfAvailableEncodings(void); + +extern const char *CFStringEncodingName(UInt32 encoding); + +/* Returns NULL-terminated list of IANA registered canonical names +*/ +extern const char **CFStringEncodingCanonicalCharsetNames(UInt32 encoding); + +/* Returns required length of destination buffer for conversion. These functions are faster than specifying 0 to maxByteLen (maxCharLen), but unnecessarily optimal length +*/ +extern UInt32 CFStringEncodingCharLengthForBytes(UInt32 encoding, UInt32 flags, const UInt8 *bytes, UInt32 numBytes); +extern UInt32 CFStringEncodingByteLengthForCharacters(UInt32 encoding, UInt32 flags, const UniChar *characters, UInt32 numChars); + +/* Can register functions used for lossy conversion. Reregisters default procs if NULL +*/ +extern void CFStringEncodingRegisterFallbackProcedures(UInt32 encoding, CFStringEncodingToBytesFallbackProc toBytes, CFStringEncodingToUnicodeFallbackProc toUnicode); + +#if defined(__cplusplus) +} +#endif + +#endif /* __CFSTRINGENCODINGCONVERTER__ */ + diff --git a/StringEncodings.subproj/CFStringEncodingConverterExt.h b/StringEncodings.subproj/CFStringEncodingConverterExt.h new file mode 100644 index 0000000..4db9b6a --- /dev/null +++ b/StringEncodings.subproj/CFStringEncodingConverterExt.h @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFStringEncodingConverterExt.h + Copyright (c) 1998-2003, Apple, Inc. All rights reserved. +*/ + +#ifndef __CFSTRINGENCODINGCONVERETEREXT_H__ +#define __CFSTRINGENCODINGCONVERETEREXT_H__ 1 + +#include "CFStringEncodingConverter.h" + +#if defined(__cplusplus) +extern "C" { +#endif + +#define MAX_DECOMPOSED_LENGTH (10) + +enum { + kCFStringEncodingConverterStandard = 0, + kCFStringEncodingConverterCheapEightBit = 1, + kCFStringEncodingConverterStandardEightBit = 2, + kCFStringEncodingConverterCheapMultiByte = 3, + kCFStringEncodingConverterPlatformSpecific = 4 // Other fields are ignored +}; + +/* kCFStringEncodingConverterStandard */ +typedef UInt32 (*CFStringEncodingToBytesProc)(UInt32 flags, const UniChar *characters, UInt32 numChars, UInt8 *bytes, UInt32 maxByteLen, UInt32 *usedByteLen); +typedef UInt32 (*CFStringEncodingToUnicodeProc)(UInt32 flags, const UInt8 *bytes, UInt32 numBytes, UniChar *characters, UInt32 maxCharLen, UInt32 *usedCharLen); +/* kCFStringEncodingConverterCheapEightBit */ +typedef Boolean (*CFStringEncodingCheapEightBitToBytesProc)(UInt32 flags, UniChar character, UInt8 *byte); +typedef Boolean (*CFStringEncodingCheapEightBitToUnicodeProc)(UInt32 flags, UInt8 byte, UniChar *character); +/* kCFStringEncodingConverterStandardEightBit */ +typedef UInt16 (*CFStringEncodingStandardEightBitToBytesProc)(UInt32 flags, const UniChar *characters, UInt32 numChars, UInt8 *byte); +typedef UInt16 (*CFStringEncodingStandardEightBitToUnicodeProc)(UInt32 flags, UInt8 byte, UniChar *characters); +/* kCFStringEncodingConverterCheapMultiByte */ +typedef UInt16 (*CFStringEncodingCheapMultiByteToBytesProc)(UInt32 flags, UniChar character, UInt8 *bytes); +typedef UInt16 (*CFStringEncodingCheapMultiByteToUnicodeProc)(UInt32 flags, const UInt8 *bytes, UInt32 numBytes, UniChar *character); + +typedef UInt32 (*CFStringEncodingToBytesLenProc)(UInt32 flags, const UniChar *characters, UInt32 numChars); +typedef UInt32 (*CFStringEncodingToUnicodeLenProc)(UInt32 flags, const UInt8 *bytes, UInt32 numBytes); + +typedef UInt32 (*CFStringEncodingToBytesPrecomposeProc)(UInt32 flags, const UniChar *character, UInt32 numChars, UInt8 *bytes, UInt32 maxByteLen, UInt32 *usedByteLen); +typedef Boolean (*CFStringEncodingIsValidCombiningCharacterProc)(UniChar character); + +typedef struct { + void *toBytes; + void *toUnicode; + UInt16 maxBytesPerChar; + UInt16 maxDecomposedCharLen; + UInt8 encodingClass; + UInt32 :24; + CFStringEncodingToBytesLenProc toBytesLen; + CFStringEncodingToUnicodeLenProc toUnicodeLen; + CFStringEncodingToBytesFallbackProc toBytesFallback; + CFStringEncodingToUnicodeFallbackProc toUnicodeFallback; + CFStringEncodingToBytesPrecomposeProc toBytesPrecompose; + CFStringEncodingIsValidCombiningCharacterProc isValidCombiningChar; +} CFStringEncodingConverter; + + +extern const CFStringEncodingConverter *CFStringEncodingGetConverter(UInt32 encoding); + +enum { + kCFStringEncodingGetConverterSelector = 0, + kCFStringEncodingIsDecomposableCharacterSelector = 1, + kCFStringEncodingDecomposeCharacterSelector = 2, + kCFStringEncodingIsValidLatin1CombiningCharacterSelector = 3, + kCFStringEncodingPrecomposeLatin1CharacterSelector = 4 +}; + +extern const void *CFStringEncodingGetAddressForSelector(UInt32 selector); + +#define BOOTSTRAPFUNC_NAME CFStringEncodingBootstrap +typedef const CFStringEncodingConverter* (*CFStringEncodingBootstrapProc)(UInt32 encoding, const void *getSelector); + +extern UInt32 CFStringEncodingGetScriptCodeForEncoding(CFStringEncoding encoding); + +/* Latin precomposition */ +/* This function does not precompose recursively nor to U+01E0 and U+01E1. +*/ +extern Boolean CFStringEncodingIsValidCombiningCharacterForLatin1(UniChar character); +extern UniChar CFStringEncodingPrecomposeLatinCharacter(const UniChar *character, UInt32 numChars, UInt32 *usedChars); + +/* Convenience functions for converter development */ +typedef struct _CFStringEncodingUnicodeTo8BitCharMap { + UniChar _u; + UInt8 _c; + UInt8 :8; +} CFStringEncodingUnicodeTo8BitCharMap; + +/* Binary searches CFStringEncodingUnicodeTo8BitCharMap */ +CF_INLINE Boolean CFStringEncodingUnicodeTo8BitEncoding(const CFStringEncodingUnicodeTo8BitCharMap *theTable, UInt32 numElem, UniChar character, UInt8 *ch) { + const CFStringEncodingUnicodeTo8BitCharMap *p, *q, *divider; + + if ((character < theTable[0]._u) || (character > theTable[numElem-1]._u)) { + return 0; + } + p = theTable; + q = p + (numElem-1); + while (p <= q) { + divider = p + ((q - p) >> 1); /* divide by 2 */ + if (character < divider->_u) { q = divider - 1; } + else if (character > divider->_u) { p = divider + 1; } + else { *ch = divider->_c; return 1; } + } + return 0; +} + + +#if defined(__cplusplus) +} +#endif + +#endif /* __CFSTRINGENCODINGCONVERETEREXT_H__ */ diff --git a/StringEncodings.subproj/CFStringEncodingConverterPriv.h b/StringEncodings.subproj/CFStringEncodingConverterPriv.h new file mode 100644 index 0000000..7c956e3 --- /dev/null +++ b/StringEncodings.subproj/CFStringEncodingConverterPriv.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFStringEncodingConverterPriv.h + Copyright (c) 1998-2003, Apple, Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFSTRINGENCODINGCONVERTERPRIV__) +#define __COREFOUNDATION_CFSTRINGENCODINGCONVERTERPRIV__ 1 + +#include +#include "CFStringEncodingConverterExt.h" + +#define MAX_IANA_ALIASES (4) + +typedef UInt32 (*_CFToBytesProc)(const void *converter, UInt32 flags, const UniChar *characters, UInt32 numChars, UInt8 *bytes, UInt32 maxByteLen, UInt32 *usedByteLen); +typedef UInt32 (*_CFToUnicodeProc)(const void *converter, UInt32 flags, const UInt8 *bytes, UInt32 numBytes, UniChar *characters, UInt32 maxCharLen, UInt32 *usedCharLen); + +typedef struct { + _CFToBytesProc toBytes; + _CFToUnicodeProc toUnicode; + _CFToUnicodeProc toCanonicalUnicode; + void *_toBytes; // original proc + void *_toUnicode; // original proc + UInt16 maxLen; + UInt16 :16; + CFStringEncodingToBytesLenProc toBytesLen; + CFStringEncodingToUnicodeLenProc toUnicodeLen; + CFStringEncodingToBytesFallbackProc toBytesFallback; + CFStringEncodingToUnicodeFallbackProc toUnicodeFallback; + CFStringEncodingToBytesPrecomposeProc toBytesPrecompose; + CFStringEncodingIsValidCombiningCharacterProc isValidCombiningChar; +} _CFEncodingConverter; + +typedef struct { + UInt32 encoding; + _CFEncodingConverter *converter; + const char *encodingName; + const char *ianaNames[MAX_IANA_ALIASES]; + const char *loadablePath; + CFStringEncodingBootstrapProc bootstrap; + CFStringEncodingToBytesFallbackProc toBytesFallback; + CFStringEncodingToUnicodeFallbackProc toUnicodeFallback; + UInt32 scriptCode; +} _CFConverterEntry; + +extern CFStringEncodingConverter __CFConverterASCII; +extern CFStringEncodingConverter __CFConverterISOLatin1; +extern CFStringEncodingConverter __CFConverterMacRoman; +extern CFStringEncodingConverter __CFConverterWinLatin1; +extern CFStringEncodingConverter __CFConverterNextStepLatin; +extern CFStringEncodingConverter __CFConverterUTF8; + + +#endif /* ! __COREFOUNDATION_CFSTRINGENCODINGCONVERTERPRIV__ */ + diff --git a/StringEncodings.subproj/CFUniChar.c b/StringEncodings.subproj/CFUniChar.c new file mode 100644 index 0000000..c891c0a --- /dev/null +++ b/StringEncodings.subproj/CFUniChar.c @@ -0,0 +1,1161 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFUniChar.c + Copyright 2001-2002, Apple, Inc. All rights reserved. + Responsibility: Aki Inoue +*/ + +#include +#include "CFInternal.h" +#include "CFUniChar.h" +#include "CFStringEncodingConverterExt.h" +#include "CFUnicodeDecomposition.h" +#include "CFUniCharPriv.h" +#if defined(__MACOS8__) +#include +#elif defined(__WIN32__) +#include +#include +#include +#include +#elif defined(__MACH__) || defined(__LINUX__) || defined(__FREEBSD__) +#if defined(__MACH__) +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#endif + +#if defined(__MACOS8__) +#define MAXPATHLEN FILENAME_MAX +#elif defined WIN32 +#define MAXPATHLEN MAX_PATH +#endif + +// Memory map the file +#if !defined(__MACOS8__) + +CF_INLINE void __CFUniCharCharacterSetPath(char *cpath) { + strcpy(cpath, __kCFCharacterSetDir); + strcat(cpath, "/CharacterSets/"); +} + +static bool __CFUniCharLoadBytesFromFile(const char *fileName, const void **bytes) { +#if defined(__WIN32__) + HANDLE bitmapFileHandle; + HANDLE mappingHandle; + + if ((bitmapFileHandle = CreateFile(fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE) return false; + mappingHandle = CreateFileMapping(bitmapFileHandle, NULL, PAGE_READONLY, 0, 0, NULL); + CloseHandle(bitmapFileHandle); + if (!mappingHandle) return false; + + *bytes = MapViewOfFileEx(mappingHandle, FILE_MAP_READ, 0, 0, 0, NULL); + CloseHandle(mappingHandle); + + return (*bytes ? true : false); +#else + struct stat statBuf; + int fd = -1; + + if ((fd = open(fileName, O_RDONLY, 0)) < 0) return false; + +#if defined(__MACH__) + if (fstat(fd, &statBuf) < 0 || map_fd(fd, 0, (vm_offset_t *)bytes, true, (vm_size_t)statBuf.st_size)) { + close(fd); + return false; + } +#else + if (fstat(fd, &statBuf) < 0 || (*bytes = mmap(0, statBuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) == (void *)-1) { + close(fd); + + return false; + } +#endif + close(fd); + + return true; +#endif +} + +static bool __CFUniCharLoadFile(const char *bitmapName, const void **bytes) { + char cpath[MAXPATHLEN]; + + __CFUniCharCharacterSetPath(cpath); + strcat(cpath, bitmapName); + + return __CFUniCharLoadBytesFromFile(cpath, bytes); +} +#endif !defined(__MACOS8__) + +// Bitmap functions +CF_INLINE bool isControl(UTF32Char theChar, uint16_t charset, const void *data) { // ISO Control + if ((theChar <= 0x001F) || (theChar >= 0x007F && theChar <= 0x009F)) return true; + return false; +} + +CF_INLINE bool isWhitespace(UTF32Char theChar, uint16_t charset, const void *data) { // Space + if ((theChar == 0x0020) || (theChar == 0x0009) || (theChar == 0x00A0) || (theChar == 0x1680) || (theChar >= 0x2000 && theChar <= 0x200B) || (theChar == 0x202F) || (theChar == 0x205F) || (theChar == 0x3000)) return true; + return false; +} + +CF_INLINE bool isWhitespaceAndNewLine(UTF32Char theChar, uint16_t charset, const void *data) { // White space + if (isWhitespace(theChar, charset, data) || (theChar >= 0x000A && theChar <= 0x000D) || (theChar == 0x0085) || (theChar == 0x2028) || (theChar == 0x2029)) return true; + return false; +} + +#if defined(__MACOS8__) +/* This structure MUST match the sets in NSRulebook.h The "__CFCSetIsMemberSet()" function is a modified version of the one in Text shlib. +*/ +typedef struct _CFCharSetPrivateStruct { + int issorted; /* 1=sorted or 0=unsorted ; 2=is_property_table */ + int bitrange[4]; /* bitmap (each bit is a 1k range in space of 2^17) */ + int nsingles; /* number of single elements */ + int nranges; /* number of ranges */ + int singmin; /* minimum single element */ + int singmax; /* maximum single element */ + int array[1]; /* actually bunch of singles followed by ranges */ +} CFCharSetPrivateStruct; + +/* Membership function for complex sets +*/ +CF_INLINE bool __CFCSetIsMemberSet(const CFCharSetPrivateStruct *set, UTF16Char theChar) { + int *tmp, *tmp2; + int i, nel; + int *p, *q, *wari; + + if (set->issorted != 1) { + return false; + } + theChar &= 0x0001FFFF; /* range 1-131k */ + if (__CFCSetBitsInRange(theChar, set->bitrange)) { + if (theChar >= set->singmin && theChar <= set->singmax) { + tmp = (int *) &(set->array[0]); + if ((nel = set->nsingles) < __kCFSetBreakeven) { + for (i = 0; i < nel; i++) { + if (*tmp == theChar) return true; + ++tmp; + } + } + else { // this does a binary search + p = tmp; q = tmp + (nel-1); + while (p <= q) { + wari = (p + ((q-p)>>1)); + if (theChar < *wari) q = wari - 1; + else if (theChar > *wari) p = wari + 1; + else return true; + } + } + } + tmp = (int *) &(set->array[0]) + set->nsingles; + if ((nel = set->nranges) < __kCFSetBreakeven) { + i = nel; + tmp2 = tmp+1; + while (i) { + if (theChar <= *tmp2) { + if (theChar >= *tmp) return true; + } + tmp += 2; + tmp2 = tmp+1; + --i; + } + } else { /* binary search the ranges */ + p = tmp; q = tmp + (2*nel-2); + while (p <= q) { + i = (q - p) >> 1; /* >>1 means divide by 2 */ + wari = p + (i & 0xFFFFFFFE); /* &fffffffe make it an even num */ + if (theChar < *wari) q = wari - 2; + else if (theChar > *(wari + 1)) p = wari + 2; + else return true; + } + } + return false; + /* fall through & return zero */ + } + return false; /* not a member */ +} + +/* 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) { + unsigned tmp; + for (tmp = n; tmp <= m; tmp++) CFUniCharAddCharacterToBitmap(tmp, map); +} + +CF_INLINE void __CFCSetMakeSetBitmapFromSet(const CFCharSetPrivateStruct *theSet, uint8_t *map) +{ + int *ip; + UTF16Char ctmp; + int cnt; + + for (cnt = 0; cnt < theSet->nsingles; cnt++) { + ctmp = theSet->array[cnt]; + CFUniCharAddCharacterToBitmap(tmp, map); + } + ip = (int *) (&(theSet->array[0]) + theSet->nsingles); + cnt = theSet->nranges; + while (cnt) { + /* This could be more efficient: turn on whole bytes at a time + when there are such cases as 8 characters in a row... */ + __CFCSetBitmapProcessManyCharacters((unsigned char *)map, *ip, *(ip+1)); + ip += 2; + --cnt; + } +} + +extern const CFCharSetPrivateStruct *_CFdecimalDigitCharacterSetData; +extern const CFCharSetPrivateStruct *_CFletterCharacterSetData; +extern const CFCharSetPrivateStruct *_CFlowercaseLetterCharacterSetData; +extern const CFCharSetPrivateStruct *_CFuppercaseLetterCharacterSetData; +extern const CFCharSetPrivateStruct *_CFnonBaseCharacterSetData; +extern const CFCharSetPrivateStruct *_CFdecomposableCharacterSetData; +extern const CFCharSetPrivateStruct *_CFpunctuationCharacterSetData; +extern const CFCharSetPrivateStruct *_CFalphanumericCharacterSetData; +extern const CFCharSetPrivateStruct *_CFillegalCharacterSetData; +extern const CFCharSetPrivateStruct *_CFhasNonSelfLowercaseMappingData; +extern const CFCharSetPrivateStruct *_CFhasNonSelfUppercaseMappingData; +extern const CFCharSetPrivateStruct *_CFhasNonSelfTitlecaseMappingData; + +#else __MACOS8__ +typedef struct { + uint32_t _numPlanes; + const uint8_t **_planes; +} __CFUniCharBitmapData; + +static char __CFUniCharUnicodeVersionString[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + +static uint32_t __CFUniCharNumberOfBitmaps = 0; +static __CFUniCharBitmapData *__CFUniCharBitmapDataArray = NULL; + +static CFSpinLock_t __CFUniCharBitmapLock = 0; + +#ifndef CF_UNICHAR_BITMAP_FILE +#define CF_UNICHAR_BITMAP_FILE "CFCharacterSetBitmaps.bitmap" +#endif CF_UNICHAR_BITMAP_FILE + +static bool __CFUniCharLoadBitmapData(void) { + uint32_t headerSize; + uint32_t bitmapSize; + int numPlanes; + uint8_t currentPlane; + const void *bytes; + const void *bitmapBase; + const void *bitmap; + int idx, bitmapIndex; + + __CFSpinLock(&__CFUniCharBitmapLock); + + if (__CFUniCharBitmapDataArray || !__CFUniCharLoadFile(CF_UNICHAR_BITMAP_FILE, &bytes)) { + __CFSpinUnlock(&__CFUniCharBitmapLock); + return false; + } + + for (idx = 0;idx < 4 && ((const uint8_t *)bytes)[idx];idx++) { + __CFUniCharUnicodeVersionString[idx * 2] = ((const uint8_t *)bytes)[idx]; + __CFUniCharUnicodeVersionString[idx * 2 + 1] = '.'; + } + __CFUniCharUnicodeVersionString[(idx < 4 ? idx * 2 - 1 : 7)] = '\0'; + + headerSize = CFSwapInt32BigToHost(*((uint32_t *)((char *)bytes + 4))); + + bitmapBase = (char *)bytes + headerSize; + (char *)bytes += (sizeof(uint32_t) * 2); + headerSize -= (sizeof(uint32_t) * 2); + + __CFUniCharNumberOfBitmaps = headerSize / (sizeof(uint32_t) * 2); + + __CFUniCharBitmapDataArray = (__CFUniCharBitmapData *)CFAllocatorAllocate(NULL, sizeof(__CFUniCharBitmapData) * __CFUniCharNumberOfBitmaps, 0); + + for (idx = 0;idx < (int)__CFUniCharNumberOfBitmaps;idx++) { + bitmap = (char *)bitmapBase + CFSwapInt32BigToHost(*(((uint32_t *)bytes)++)); + bitmapSize = CFSwapInt32BigToHost(*(((uint32_t *)bytes)++)); + + numPlanes = bitmapSize / (8 * 1024); + numPlanes = *(const uint8_t *)((char *)bitmap + (((numPlanes - 1) * ((8 * 1024) + 1)) - 1)) + 1; + __CFUniCharBitmapDataArray[idx]._planes = (const uint8_t **)CFAllocatorAllocate(NULL, sizeof(const void *) * numPlanes, NULL); + __CFUniCharBitmapDataArray[idx]._numPlanes = numPlanes; + + currentPlane = 0; + for (bitmapIndex = 0;bitmapIndex < numPlanes;bitmapIndex++) { + if (bitmapIndex == currentPlane) { + __CFUniCharBitmapDataArray[idx]._planes[bitmapIndex] = bitmap; + (char *)bitmap += (8 * 1024); + currentPlane = *(((const uint8_t *)bitmap)++); + } else { + __CFUniCharBitmapDataArray[idx]._planes[bitmapIndex] = NULL; + } + } + } + + __CFSpinUnlock(&__CFUniCharBitmapLock); + + return true; +} + +__private_extern__ const char *__CFUniCharGetUnicodeVersionString(void) { + if (NULL == __CFUniCharBitmapDataArray) __CFUniCharLoadBitmapData(); + return __CFUniCharUnicodeVersionString; +} + +#endif __MACOS8__ + +#define CONTROLSET_HAS_FORMATTER 1 + +bool CFUniCharIsMemberOf(UTF32Char theChar, uint32_t charset) { +#if CONTROLSET_HAS_FORMATTER + if (charset == kCFUniCharControlCharacterSet) charset = kCFUniCharControlAndFormatterCharacterSet; +#endif CONTROLSET_HAS_FORMATTER + + switch (charset) { + case kCFUniCharControlCharacterSet: + return isControl(theChar, charset, NULL); + + case kCFUniCharWhitespaceCharacterSet: + return isWhitespace(theChar, charset, NULL); + + case kCFUniCharWhitespaceAndNewlineCharacterSet: + return isWhitespaceAndNewLine(theChar, charset, NULL); + +#if defined(__MACOS8__) + case kCFUniCharDecimalDigitCharacterSet: +return __CFCSetIsMemberSet((const CFCharSetPrivateStruct *)&_CFdecimalDigitCharacterSetData, theChar); + case kCFUniCharLetterCharacterSet: +return __CFCSetIsMemberSet((const CFCharSetPrivateStruct *)&_CFletterCharacterSetData, theChar); + case kCFUniCharLowercaseLetterCharacterSet: +return __CFCSetIsMemberSet((const CFCharSetPrivateStruct *)&_CFlowercaseLetterCharacterSetData, theChar); + case kCFUniCharUppercaseLetterCharacterSet: +return __CFCSetIsMemberSet((const CFCharSetPrivateStruct *)&_CFuppercaseLetterCharacterSetData, theChar); + case kCFUniCharNonBaseCharacterSet: +return __CFCSetIsMemberSet((const CFCharSetPrivateStruct *)&_CFnonBaseCharacterSetData, theChar); + case kCFUniCharAlphaNumericCharacterSet: +return __CFCSetIsMemberSet((const CFCharSetPrivateStruct *)&_CFalphanumericCharacterSetData, theChar); + case kCFUniCharDecomposableCharacterSet: +return __CFCSetIsMemberSet((const CFCharSetPrivateStruct *)&_CFdecomposableCharacterSetData, theChar); + case kCFUniCharPunctuationCharacterSet: +return __CFCSetIsMemberSet((const CFCharSetPrivateStruct *)&_CFpunctuationCharacterSetData, theChar); + case kCFUniCharIllegalCharacterSet: +return __CFCSetIsMemberSet((const CFCharSetPrivateStruct *)&_CFillegalCharacterSetData, theChar); + case kCFUniCharHasNonSelfLowercaseMapping: +return __CFCSetIsMemberSet((const CFCharSetPrivateStruct *)&_CFhasNonSelfLowercaseMappingData, theChar); + case kCFUniCharHasNonSelfUppercaseMapping: +return __CFCSetIsMemberSet((const CFCharSetPrivateStruct *)&_CFhasNonSelfUppercaseMappingData, theChar); + case kCFUniCharHasNonSelfTitlecaseMapping: +return __CFCSetIsMemberSet((const CFCharSetPrivateStruct *)&_CFhasNonSelfTitlecaseMappingData, theChar); + default: + return false; +#else + default: + if (NULL == __CFUniCharBitmapDataArray) __CFUniCharLoadBitmapData(); + + if ((charset - kCFUniCharDecimalDigitCharacterSet) < __CFUniCharNumberOfBitmaps) { + __CFUniCharBitmapData *data = __CFUniCharBitmapDataArray + (charset - kCFUniCharDecimalDigitCharacterSet); + uint8_t planeNo = (theChar >> 16) & 0xFF; + + // The bitmap data for kCFUniCharIllegalCharacterSet is actually LEGAL set less Plane 14 ~ 16 + if (charset == kCFUniCharIllegalCharacterSet) { + if (planeNo == 0x0E) { // Plane 14 + theChar &= 0xFF; + return (((theChar == 0x01) || ((theChar > 0x1F) && (theChar < 0x80))) ? false : true); + } else if (planeNo == 0x0F || planeNo == 0x10) { // Plane 15 & 16 + return ((theChar & 0xFF) > 0xFFFD ? true : false); + } else { + return (planeNo < data->_numPlanes && data->_planes[planeNo] ? !CFUniCharIsMemberOfBitmap(theChar, data->_planes[planeNo]) : true); + } + } else if (charset == kCFUniCharControlAndFormatterCharacterSet) { + if (planeNo == 0x0E) { // Plane 14 + theChar &= 0xFF; + return (((theChar == 0x01) || ((theChar > 0x1F) && (theChar < 0x80))) ? true : false); + } else { + return (planeNo < data->_numPlanes && data->_planes[planeNo] ? CFUniCharIsMemberOfBitmap(theChar, data->_planes[planeNo]) : false); + } + } else { + return (planeNo < data->_numPlanes && data->_planes[planeNo] ? CFUniCharIsMemberOfBitmap(theChar, data->_planes[planeNo]) : false); + } + } + return false; +#endif + } +} + +const uint8_t *CFUniCharGetBitmapPtrForPlane(uint32_t charset, uint32_t plane) { + if (NULL == __CFUniCharBitmapDataArray) __CFUniCharLoadBitmapData(); + +#if CONTROLSET_HAS_FORMATTER + if (charset == kCFUniCharControlCharacterSet) charset = kCFUniCharControlAndFormatterCharacterSet; +#endif CONTROLSET_HAS_FORMATTER + + if (charset > kCFUniCharWhitespaceAndNewlineCharacterSet && (charset - kCFUniCharDecimalDigitCharacterSet) < __CFUniCharNumberOfBitmaps && charset != kCFUniCharIllegalCharacterSet) { + __CFUniCharBitmapData *data = __CFUniCharBitmapDataArray + (charset - kCFUniCharDecimalDigitCharacterSet); + + return (plane < data->_numPlanes ? data->_planes[plane] : NULL); + } + return NULL; +} + +__private_extern__ uint8_t CFUniCharGetBitmapForPlane(uint32_t charset, uint32_t plane, void *bitmap, bool isInverted) { + const uint8_t *src = CFUniCharGetBitmapPtrForPlane(charset, plane); + int numBytes = (8 * 1024); + + if (src) { + if (isInverted) { + while (numBytes-- > 0) *(((uint8_t *)bitmap)++) = ~(*(src++)); + } else { + while (numBytes-- > 0) *(((uint8_t *)bitmap)++) = *(src++); + } + return kCFUniCharBitmapFilled; + } else if (charset == kCFUniCharIllegalCharacterSet) { + __CFUniCharBitmapData *data = __CFUniCharBitmapDataArray + (charset - kCFUniCharDecimalDigitCharacterSet); + + if (plane < data->_numPlanes && (src = data->_planes[plane])) { + if (isInverted) { + while (numBytes-- > 0) *(((uint8_t *)bitmap)++) = *(src++); + } else { + while (numBytes-- > 0) *(((uint8_t *)bitmap)++) = ~(*(src++)); + } + return kCFUniCharBitmapFilled; + } else if (plane == 0x0E) { // Plane 14 + int idx; + uint8_t asciiRange = (isInverted ? (uint8_t)0xFF : (uint8_t)0); + uint8_t otherRange = (isInverted ? (uint8_t)0 : (uint8_t)0xFF); + + *(((uint8_t *)bitmap)++) = 0x02; // UE0001 LANGUAGE TAG + for (idx = 1;idx < numBytes;idx++) { + *(((uint8_t *)bitmap)++) = ((idx >= (0x20 / 8) && (idx < (0x80 / 8))) ? asciiRange : otherRange); + } + return kCFUniCharBitmapFilled; + } else if (plane == 0x0F || plane == 0x10) { // Plane 15 & 16 + uint32_t value = (isInverted ? 0xFFFFFFFF : 0); + numBytes /= 4; // for 32bit + + while (numBytes-- > 0) *(((uint32_t *)bitmap)++) = value; + *(((uint8_t *)bitmap) - 5) = (isInverted ? 0x3F : 0xC0); // 0xFFFE & 0xFFFF + return kCFUniCharBitmapFilled; + } + return (isInverted ? kCFUniCharBitmapEmpty : kCFUniCharBitmapAll); +#if CONTROLSET_HAS_FORMATTER + } else if ((charset == kCFUniCharControlCharacterSet) && (plane == 0x0E)) { // Language tags + int idx; + uint8_t asciiRange = (isInverted ? (uint8_t)0 : (uint8_t)0xFF); + uint8_t otherRange = (isInverted ? (uint8_t)0xFF : (uint8_t)0); + + *(((uint8_t *)bitmap)++) = 0x02; // UE0001 LANGUAGE TAG + for (idx = 1;idx < numBytes;idx++) { + *(((uint8_t *)bitmap)++) = ((idx >= (0x20 / 8) && (idx < (0x80 / 8))) ? asciiRange : otherRange); + } + return kCFUniCharBitmapFilled; +#endif CONTROLSET_HAS_FORMATTER + } else if (charset < kCFUniCharDecimalDigitCharacterSet) { + if (plane) return (isInverted ? kCFUniCharBitmapAll : kCFUniCharBitmapEmpty); + + if (charset == kCFUniCharControlCharacterSet) { + int idx; + uint8_t nonFillValue = (isInverted ? (uint8_t)0xFF : (uint8_t)0); + uint8_t fillValue = (isInverted ? (uint8_t)0 : (uint8_t)0xFF); + uint8_t *bitmapP = (uint8_t *)bitmap; + + for (idx = 0;idx < numBytes;idx++) { + *(bitmapP++) = (idx < (0x20 / 8) || (idx >= (0x80 / 8) && idx < (0xA0 / 8)) ? fillValue : nonFillValue); + } + + // DEL + if (isInverted) { + CFUniCharRemoveCharacterFromBitmap(0x007F, bitmap); + } else { + CFUniCharAddCharacterToBitmap(0x007F, bitmap); + } + } else { + uint8_t *bitmapBase = (uint8_t *)bitmap; + int idx; + uint8_t nonFillValue = (isInverted ? (uint8_t)0xFF : (uint8_t)0); + + while (numBytes-- > 0) *(((uint8_t *)bitmap)++) = nonFillValue; + + if (charset == kCFUniCharWhitespaceAndNewlineCharacterSet) { + static const UniChar newlines[] = {0x000A, 0x000B, 0x000C, 0x000D, 0x0085, 0x2028, 0x2029}; + + for (idx = 0;idx < (int)(sizeof(newlines) / sizeof(*newlines)); idx++) { + if (isInverted) { + CFUniCharRemoveCharacterFromBitmap(newlines[idx], bitmapBase); + } else { + CFUniCharAddCharacterToBitmap(newlines[idx], bitmapBase); + } + } + } + + if (isInverted) { + CFUniCharRemoveCharacterFromBitmap(0x0009, bitmapBase); + CFUniCharRemoveCharacterFromBitmap(0x0020, bitmapBase); + CFUniCharRemoveCharacterFromBitmap(0x00A0, bitmapBase); + CFUniCharRemoveCharacterFromBitmap(0x1680, bitmapBase); + CFUniCharRemoveCharacterFromBitmap(0x202F, bitmapBase); + CFUniCharRemoveCharacterFromBitmap(0x205F, bitmapBase); + CFUniCharRemoveCharacterFromBitmap(0x3000, bitmapBase); + } else { + CFUniCharAddCharacterToBitmap(0x0009, bitmapBase); + CFUniCharAddCharacterToBitmap(0x0020, bitmapBase); + CFUniCharAddCharacterToBitmap(0x00A0, bitmapBase); + CFUniCharAddCharacterToBitmap(0x1680, bitmapBase); + CFUniCharAddCharacterToBitmap(0x202F, bitmapBase); + CFUniCharAddCharacterToBitmap(0x205F, bitmapBase); + CFUniCharAddCharacterToBitmap(0x3000, bitmapBase); + } + + for (idx = 0x2000;idx <= 0x200B;idx++) { + if (isInverted) { + CFUniCharRemoveCharacterFromBitmap(idx, bitmapBase); + } else { + CFUniCharAddCharacterToBitmap(idx, bitmapBase); + } + } + } + return kCFUniCharBitmapFilled; + } + return (isInverted ? kCFUniCharBitmapAll : kCFUniCharBitmapEmpty); +} + +__private_extern__ uint32_t CFUniCharGetNumberOfPlanes(uint32_t charset) { +#if defined(__MACOS8__) + return 1; +#else __MACOS8__ +#if CONTROLSET_HAS_FORMATTER + if (charset == kCFUniCharControlCharacterSet) return 15; // 0 to 14 +#endif CONTROLSET_HAS_FORMATTER + + if (charset < kCFUniCharDecimalDigitCharacterSet) { + return 1; + } else if (charset == kCFUniCharIllegalCharacterSet) { + return 17; + } else { + uint32_t numPlanes; + + if (NULL == __CFUniCharBitmapDataArray) __CFUniCharLoadBitmapData(); + + numPlanes = __CFUniCharBitmapDataArray[charset - kCFUniCharDecimalDigitCharacterSet]._numPlanes; + + return numPlanes; + } +#endif __MACOS8__ +} + +// Mapping data loading +static const void **__CFUniCharMappingTables = NULL; + +static CFSpinLock_t __CFUniCharMappingTableLock = 0; + +#if defined(__BIG_ENDIAN__) +#define MAPPING_TABLE_FILE "CFUnicodeData-B.mapping" +#else __BIG_ENDIAN__ +#define MAPPING_TABLE_FILE "CFUnicodeData-L.mapping" +#endif __BIG_ENDIAN__ + +__private_extern__ const void *CFUniCharGetMappingData(uint32_t type) { + + __CFSpinLock(&__CFUniCharMappingTableLock); + + if (NULL == __CFUniCharMappingTables) { + const void *bytes; + const void *bodyBase; + int headerSize; + int idx, count; + + if (!__CFUniCharLoadFile(MAPPING_TABLE_FILE, &bytes)) { + __CFSpinUnlock(&__CFUniCharMappingTableLock); + return NULL; + } + + (char *)bytes += 4; // Skip Unicode version + headerSize = *(((uint32_t *)bytes)++); + headerSize -= (sizeof(uint32_t) * 2); + bodyBase = (char *)bytes + headerSize; + + count = headerSize / sizeof(uint32_t); + + __CFUniCharMappingTables = (const void **)CFAllocatorAllocate(NULL, sizeof(const void *) * count, 0); + + for (idx = 0;idx < count;idx++) { + __CFUniCharMappingTables[idx] = (char *)bodyBase + *(((uint32_t *)bytes)++); + } + } + + __CFSpinUnlock(&__CFUniCharMappingTableLock); + + return __CFUniCharMappingTables[type]; +} + +// Case mapping functions +#define DO_SPECIAL_CASE_MAPPING 1 + +static uint32_t *__CFUniCharCaseMappingTableCounts = NULL; +static uint32_t **__CFUniCharCaseMappingTable = NULL; +static const uint32_t **__CFUniCharCaseMappingExtraTable = NULL; + +typedef struct { + uint32_t _key; + uint32_t _value; +} __CFUniCharCaseMappings; + +/* Binary searches CFStringEncodingUnicodeTo8BitCharMap */ +static uint32_t __CFUniCharGetMappedCase(const __CFUniCharCaseMappings *theTable, uint32_t numElem, UTF32Char character) { + const __CFUniCharCaseMappings *p, *q, *divider; + + if ((character < theTable[0]._key) || (character > theTable[numElem-1]._key)) { + return 0; + } + p = theTable; + q = p + (numElem-1); + while (p <= q) { + divider = p + ((q - p) >> 1); /* divide by 2 */ + if (character < divider->_key) { q = divider - 1; } + else if (character > divider->_key) { p = divider + 1; } + else { return divider->_value; } + } + return 0; +} + +#define NUM_CASE_MAP_DATA (kCFUniCharCaseFold + 1) + +static bool __CFUniCharLoadCaseMappingTable(void) { + int idx; + + if (NULL == __CFUniCharMappingTables) (void)CFUniCharGetMappingData(kCFUniCharToLowercase); + if (NULL == __CFUniCharMappingTables) return false; + + __CFSpinLock(&__CFUniCharMappingTableLock); + + if (__CFUniCharCaseMappingTableCounts) { + __CFSpinUnlock(&__CFUniCharMappingTableLock); + return true; + } + + __CFUniCharCaseMappingTableCounts = (uint32_t *)CFAllocatorAllocate(NULL, sizeof(uint32_t) * NUM_CASE_MAP_DATA + sizeof(uint32_t *) * NUM_CASE_MAP_DATA * 2, 0); + __CFUniCharCaseMappingTable = (uint32_t **)((char *)__CFUniCharCaseMappingTableCounts + sizeof(uint32_t) * NUM_CASE_MAP_DATA); + __CFUniCharCaseMappingExtraTable = (const uint32_t **)__CFUniCharCaseMappingTable + NUM_CASE_MAP_DATA; + + for (idx = 0;idx < NUM_CASE_MAP_DATA;idx++) { + __CFUniCharCaseMappingTableCounts[idx] = *((uint32_t *)__CFUniCharMappingTables[idx]) / (sizeof(uint32_t) * 2); + __CFUniCharCaseMappingTable[idx] = ((uint32_t *)__CFUniCharMappingTables[idx]) + 1; + __CFUniCharCaseMappingExtraTable[idx] = (const uint32_t *)((char *)__CFUniCharCaseMappingTable[idx] + *((uint32_t *)__CFUniCharMappingTables[idx])); + } + + __CFSpinUnlock(&__CFUniCharMappingTableLock); + return true; +} + +#if __BIG_ENDIAN__ +#define TURKISH_LANG_CODE (0x7472) // tr +#define LITHUANIAN_LANG_CODE (0x6C74) // lt +#define AZERI_LANG_CODE (0x617A) // az +#else __BIG_ENDIAN__ +#define TURKISH_LANG_CODE (0x7274) // tr +#define LITHUANIAN_LANG_CODE (0x746C) // lt +#define AZERI_LANG_CODE (0x7A61) // az +#endif __BIG_ENDIAN__ + +uint32_t CFUniCharMapCaseTo(UTF32Char theChar, UTF16Char *convertedChar, uint32_t maxLength, uint32_t ctype, uint32_t flags, const uint8_t *langCode) { + __CFUniCharBitmapData *data; + uint8_t planeNo = (theChar >> 16) & 0xFF; + +caseFoldRetry: + +#if DO_SPECIAL_CASE_MAPPING + if (flags & kCFUniCharCaseMapFinalSigma) { + if (theChar == 0x03A3) { // Final sigma + *convertedChar = (ctype == kCFUniCharToLowercase ? 0x03C2 : 0x03A3); + return 1; + } + } + + if (langCode) { + switch (*(uint16_t *)langCode) { + case LITHUANIAN_LANG_CODE: + if (theChar == 0x0307 && (flags & kCFUniCharCaseMapAfter_i)) { + return 0; + } else if (ctype == kCFUniCharToLowercase) { + if (flags & kCFUniCharCaseMapMoreAbove) { + switch (theChar) { + case 0x0049: // LATIN CAPITAL LETTER I + *(convertedChar++) = 0x0069; + *(convertedChar++) = 0x0307; + return 2; + + case 0x004A: // LATIN CAPITAL LETTER J + *(convertedChar++) = 0x006A; + *(convertedChar++) = 0x0307; + return 2; + + case 0x012E: // LATIN CAPITAL LETTER I WITH OGONEK + *(convertedChar++) = 0x012F; + *(convertedChar++) = 0x0307; + return 2; + + default: break; + } + } + switch (theChar) { + case 0x00CC: // LATIN CAPITAL LETTER I WITH GRAVE + *(convertedChar++) = 0x0069; + *(convertedChar++) = 0x0307; + *(convertedChar++) = 0x0300; + return 3; + + case 0x00CD: // LATIN CAPITAL LETTER I WITH ACUTE + *(convertedChar++) = 0x0069; + *(convertedChar++) = 0x0307; + *(convertedChar++) = 0x0301; + return 3; + + case 0x0128: // LATIN CAPITAL LETTER I WITH TILDE + *(convertedChar++) = 0x0069; + *(convertedChar++) = 0x0307; + *(convertedChar++) = 0x0303; + return 3; + + default: break; + } + } + break; + + case TURKISH_LANG_CODE: + case AZERI_LANG_CODE: + if (theChar == 0x0049) { // LATIN CAPITAL LETTER I + *convertedChar = (ctype == kCFUniCharToLowercase ? ((kCFUniCharCaseMapMoreAbove & flags) ? 0x0069 : 0x0131) : 0x0049); + return 1; + } else if ((theChar == 0x0069) || (theChar == 0x0130)) { // LATIN SMALL LETTER I & LATIN CAPITAL LETTER I WITH DOT ABOVE + *convertedChar = (ctype == kCFUniCharToLowercase ? 0x0069 : 0x0130); + return 1; + } else if (theChar == 0x0307 && (kCFUniCharCaseMapAfter_i & flags)) { // COMBINING DOT ABOVE AFTER_i + if (ctype == kCFUniCharToLowercase) { + return 0; + } else { + *convertedChar = 0x0307; + return 1; + } + } + break; + + default: break; + } + } +#endif DO_SPECIAL_CASE_MAPPING + + if (NULL == __CFUniCharBitmapDataArray) __CFUniCharLoadBitmapData(); + + data = __CFUniCharBitmapDataArray + ((ctype + kCFUniCharHasNonSelfLowercaseCharacterSet) - kCFUniCharDecimalDigitCharacterSet); + + if (planeNo < data->_numPlanes && data->_planes[planeNo] && CFUniCharIsMemberOfBitmap(theChar, data->_planes[planeNo]) && (__CFUniCharCaseMappingTableCounts || __CFUniCharLoadCaseMappingTable())) { + uint32_t value = __CFUniCharGetMappedCase((const __CFUniCharCaseMappings *)__CFUniCharCaseMappingTable[ctype], __CFUniCharCaseMappingTableCounts[ctype], theChar); + + if (!value && ctype == kCFUniCharToTitlecase) { + value = __CFUniCharGetMappedCase((const __CFUniCharCaseMappings *)__CFUniCharCaseMappingTable[kCFUniCharToUppercase], __CFUniCharCaseMappingTableCounts[kCFUniCharToUppercase], theChar); + if (value) ctype = kCFUniCharToUppercase; + } + + if (value) { + int count = CFUniCharConvertFlagToCount(value); + + if (count == 1) { + if (value & kCFUniCharNonBmpFlag) { + if (maxLength > 1) { + value = (value & 0xFFFFFF) - 0x10000; + *(convertedChar++) = (value >> 10) + 0xD800UL; + *(convertedChar++) = (value & 0x3FF) + 0xDC00UL; + return 2; + } + } else { + *convertedChar = (UTF16Char)value; + return 1; + } + } else if (count < (int)maxLength) { + const uint32_t *extraMapping = __CFUniCharCaseMappingExtraTable[ctype] + (value & 0xFFFFFF); + + if (value & kCFUniCharNonBmpFlag) { + int copiedLen = 0; + + while (count-- > 0) { + value = *(extraMapping++); + if (value > 0xFFFF) { + if (copiedLen + 2 >= (int)maxLength) break; + value = (value & 0xFFFFFF) - 0x10000; + convertedChar[copiedLen++] = (value >> 10) + 0xD800UL; + convertedChar[copiedLen++] = (value & 0x3FF) + 0xDC00UL; + } else { + if (copiedLen + 1 >= (int)maxLength) break; + convertedChar[copiedLen++] = value; + } + } + if (!count) return copiedLen; + } else { + int idx; + + for (idx = 0;idx < count;idx++) *(convertedChar++) = (UTF16Char)*(extraMapping++); + return count; + } + } + } + } else if (ctype == kCFUniCharCaseFold) { + ctype = kCFUniCharToLowercase; + goto caseFoldRetry; + } + + *convertedChar = theChar; + return 1; +} + +UInt32 CFUniCharMapTo(UniChar theChar, UniChar *convertedChar, UInt32 maxLength, uint16_t ctype, UInt32 flags) { + if (ctype == kCFUniCharCaseFold + 1) { // kCFUniCharDecompose + if (CFUniCharIsDecomposableCharacter(theChar, false)) { + UTF32Char buffer[MAX_DECOMPOSED_LENGTH]; + CFIndex usedLength = CFUniCharDecomposeCharacter(theChar, buffer, MAX_DECOMPOSED_LENGTH); + CFIndex idx; + + for (idx = 0;idx < usedLength;idx++) *(convertedChar++) = buffer[idx]; + return usedLength; + } else { + *convertedChar = theChar; + return 1; + } + } else { + return CFUniCharMapCaseTo(theChar, convertedChar, maxLength, ctype, flags, NULL); + } +} + +CF_INLINE bool __CFUniCharIsMoreAbove(UTF16Char *buffer, uint32_t length) { + UTF32Char currentChar; + uint32_t property; + + while (length-- > 0) { + currentChar = *(buffer)++; + if (CFUniCharIsSurrogateHighCharacter(currentChar) && (length > 0) && CFUniCharIsSurrogateLowCharacter(*(buffer + 1))) { + currentChar = CFUniCharGetLongCharacterForSurrogatePair(currentChar, *(buffer++)); + --length; + } + if (!CFUniCharIsMemberOf(currentChar, kCFUniCharNonBaseCharacterSet)) break; + + property = CFUniCharGetCombiningPropertyForCharacter(currentChar, CFUniCharGetUnicodePropertyDataForPlane(kCFUniCharCombiningProperty, (currentChar >> 16) & 0xFF)); + + if (property == 230) return true; // Above priority + } + return false; +} + +CF_INLINE bool __CFUniCharIsAfter_i(UTF16Char *buffer, uint32_t length) { + UTF32Char currentChar = 0; + uint32_t property; + UTF32Char decomposed[MAX_DECOMPOSED_LENGTH]; + uint32_t decompLength; + uint32_t idx; + + if (length < 1) return 0; + + buffer += length; + while (length-- > 1) { + currentChar = *(--buffer); + if (CFUniCharIsSurrogateLowCharacter(currentChar)) { + if ((length > 1) && CFUniCharIsSurrogateHighCharacter(*(buffer - 1))) { + currentChar = CFUniCharGetLongCharacterForSurrogatePair(*(--buffer), currentChar); + --length; + } else { + break; + } + } + if (!CFUniCharIsMemberOf(currentChar, kCFUniCharNonBaseCharacterSet)) break; + + property = CFUniCharGetCombiningPropertyForCharacter(currentChar, CFUniCharGetUnicodePropertyDataForPlane(kCFUniCharCombiningProperty, (currentChar >> 16) & 0xFF)); + + if (property == 230) return false; // Above priority + } + if (length == 0) { + currentChar = *(--buffer); + } else if (CFUniCharIsSurrogateLowCharacter(currentChar) && CFUniCharIsSurrogateHighCharacter(*(--buffer))) { + currentChar = CFUniCharGetLongCharacterForSurrogatePair(*buffer, currentChar); + } + + decompLength = CFUniCharDecomposeCharacter(currentChar, decomposed, MAX_DECOMPOSED_LENGTH); + currentChar = *decomposed; + + + for (idx = 1;idx < decompLength;idx++) { + currentChar = decomposed[idx]; + property = CFUniCharGetCombiningPropertyForCharacter(currentChar, CFUniCharGetUnicodePropertyDataForPlane(kCFUniCharCombiningProperty, (currentChar >> 16) & 0xFF)); + + if (property == 230) return false; // Above priority + } + return true; +} + +__private_extern__ uint32_t CFUniCharGetConditionalCaseMappingFlags(UTF32Char theChar, UTF16Char *buffer, uint32_t currentIndex, uint32_t length, uint32_t type, const uint8_t *langCode, uint32_t lastFlags) { + if (theChar == 0x03A3) { // GREEK CAPITAL LETTER SIGMA + if ((type == kCFUniCharToLowercase) && (currentIndex > 0)) { + UTF16Char *start = buffer; + UTF16Char *end = buffer + length; + UTF32Char otherChar; + + // First check if we're after a cased character + buffer += (currentIndex - 1); + while (start <= buffer) { + otherChar = *(buffer--); + if (CFUniCharIsSurrogateLowCharacter(otherChar) && (start <= buffer) && CFUniCharIsSurrogateHighCharacter(*buffer)) { + otherChar = CFUniCharGetLongCharacterForSurrogatePair(*(buffer--), otherChar); + } + if (!CFUniCharIsMemberOf(otherChar, kCFUniCharCaseIgnorableCharacterSet)) { + if (!CFUniCharIsMemberOf(otherChar, kCFUniCharUppercaseLetterCharacterSet) && !CFUniCharIsMemberOf(otherChar, kCFUniCharLowercaseLetterCharacterSet)) return 0; // Uppercase set contains titlecase + break; + } + } + + // Next check if we're before a cased character + buffer = start + currentIndex + 1; + while (buffer < end) { + otherChar = *(buffer++); + if (CFUniCharIsSurrogateHighCharacter(otherChar) && (buffer < end) && CFUniCharIsSurrogateLowCharacter(*buffer)) { + otherChar = CFUniCharGetLongCharacterForSurrogatePair(otherChar, *(buffer++)); + } + if (!CFUniCharIsMemberOf(otherChar, kCFUniCharCaseIgnorableCharacterSet)) { + if (CFUniCharIsMemberOf(otherChar, kCFUniCharUppercaseLetterCharacterSet) || CFUniCharIsMemberOf(otherChar, kCFUniCharLowercaseLetterCharacterSet)) return 0; // Uppercase set contains titlecase + break; + } + } + return kCFUniCharCaseMapFinalSigma; + } + } else if (langCode) { + if (*((const uint16_t *)langCode) == LITHUANIAN_LANG_CODE) { + if ((theChar == 0x0307) && ((kCFUniCharCaseMapAfter_i|kCFUniCharCaseMapMoreAbove) & lastFlags) == (kCFUniCharCaseMapAfter_i|kCFUniCharCaseMapMoreAbove)) { + return (__CFUniCharIsAfter_i(buffer, currentIndex) ? kCFUniCharCaseMapAfter_i : 0); + } else if (type == kCFUniCharToLowercase) { + if ((theChar == 0x0049) || (theChar == 0x004A) || (theChar == 0x012E)) { + return (__CFUniCharIsMoreAbove(buffer + (++currentIndex), length - currentIndex) ? kCFUniCharCaseMapMoreAbove : 0); + } + } else if ((theChar == 'i') || (theChar == 'j')) { + return (__CFUniCharIsMoreAbove(buffer + (++currentIndex), length - currentIndex) ? (kCFUniCharCaseMapAfter_i|kCFUniCharCaseMapMoreAbove) : 0); + } + } else if ((*((const uint16_t *)langCode) == TURKISH_LANG_CODE) || (*((const uint16_t *)langCode) == AZERI_LANG_CODE)) { + if (type == kCFUniCharToLowercase) { + if (theChar == 0x0307) { + return (kCFUniCharCaseMapMoreAbove & lastFlags ? kCFUniCharCaseMapAfter_i : 0); + } else if (theChar == 0x0049) { + return (((++currentIndex < length) && (buffer[currentIndex] == 0x0307)) ? kCFUniCharCaseMapMoreAbove : 0); + } + } + } + } + return 0; +} + +// Unicode property database +static __CFUniCharBitmapData *__CFUniCharUnicodePropertyTable = NULL; + +static CFSpinLock_t __CFUniCharPropTableLock = 0; + +#define PROP_DB_FILE "CFUniCharPropertyDatabase.data" + +const void *CFUniCharGetUnicodePropertyDataForPlane(uint32_t propertyType, uint32_t plane) { + + __CFSpinLock(&__CFUniCharPropTableLock); + + if (NULL == __CFUniCharUnicodePropertyTable) { + const void *bytes; + const void *bodyBase; + const void *planeBase; + int headerSize; + int idx, count; + int planeIndex, planeCount; + int planeSize; + + if (!__CFUniCharLoadFile(PROP_DB_FILE, &bytes)) { + __CFSpinUnlock(&__CFUniCharPropTableLock); + return NULL; + } + + (char *)bytes += 4; // Skip Unicode version + headerSize = CFSwapInt32BigToHost(*(((uint32_t *)bytes)++)); + headerSize -= (sizeof(uint32_t) * 2); + bodyBase = (char *)bytes + headerSize; + + count = headerSize / sizeof(uint32_t); + + __CFUniCharUnicodePropertyTable = (__CFUniCharBitmapData *)CFAllocatorAllocate(NULL, sizeof(__CFUniCharBitmapData) * count, 0); + + for (idx = 0;idx < count;idx++) { + planeCount = *((const uint8_t *)bodyBase); + (char *)planeBase = (char *)bodyBase + planeCount + (planeCount % 4 ? 4 - (planeCount % 4) : 0); + __CFUniCharUnicodePropertyTable[idx]._planes = (const uint8_t **)CFAllocatorAllocate(NULL, sizeof(const void *) * planeCount, 0); + + for (planeIndex = 0;planeIndex < planeCount;planeIndex++) { + if ((planeSize = ((const uint8_t *)bodyBase)[planeIndex + 1])) { + __CFUniCharUnicodePropertyTable[idx]._planes[planeIndex] = planeBase; + (char *)planeBase += (planeSize * 256); + } else { + __CFUniCharUnicodePropertyTable[idx]._planes[planeIndex] = NULL; + } + } + + __CFUniCharUnicodePropertyTable[idx]._numPlanes = planeCount; + (char *)bodyBase += (CFSwapInt32BigToHost(*(((uint32_t *)bytes)++))); + } + } + + __CFSpinUnlock(&__CFUniCharPropTableLock); + + return (plane < __CFUniCharUnicodePropertyTable[propertyType]._numPlanes ? __CFUniCharUnicodePropertyTable[propertyType]._planes[plane] : NULL); +} + +__private_extern__ uint32_t CFUniCharGetNumberOfPlanesForUnicodePropertyData(uint32_t propertyType) { + (void)CFUniCharGetUnicodePropertyDataForPlane(propertyType, 0); + return __CFUniCharUnicodePropertyTable[propertyType]._numPlanes; +} + +__private_extern__ uint32_t CFUniCharGetUnicodeProperty(UTF32Char character, uint32_t propertyType) { + if (propertyType == kCFUniCharCombiningProperty) { + return CFUniCharGetCombiningPropertyForCharacter(character, CFUniCharGetUnicodePropertyDataForPlane(propertyType, (character >> 16) & 0xFF)); + } else if (propertyType == kCFUniCharBidiProperty) { + return CFUniCharGetBidiPropertyForCharacter(character, CFUniCharGetUnicodePropertyDataForPlane(propertyType, (character >> 16) & 0xFF)); + } else { + return 0; + } +} + + + +/* + The UTF8 conversion in the following function is derived from ConvertUTF.c +*/ +/* + * 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. + */ +#define UNI_REPLACEMENT_CHAR (0x0000FFFDUL) + +bool CFUniCharFillDestinationBuffer(const UTF32Char *src, uint32_t srcLength, void **dst, uint32_t dstLength, uint32_t *filledLength, uint32_t dstFormat) { + UTF32Char currentChar; + uint32_t usedLength = *filledLength; + + if (dstFormat == kCFUniCharUTF16Format) { + UTF16Char *dstBuffer = (UTF16Char *)*dst; + + while (srcLength-- > 0) { + currentChar = *(src++); + + if (currentChar > 0xFFFF) { // Non-BMP + usedLength += 2; + if (dstLength) { + if (usedLength > dstLength) return false; + currentChar -= 0x10000; + *(dstBuffer++) = (UTF16Char)((currentChar >> 10) + 0xD800UL); + *(dstBuffer++) = (UTF16Char)((currentChar & 0x3FF) + 0xDC00UL); + } + } else { + ++usedLength; + if (dstLength) { + if (usedLength > dstLength) return false; + *(dstBuffer++) = (UTF16Char)currentChar; + } + } + } + + *dst = dstBuffer; + } else if (dstFormat == kCFUniCharUTF8Format) { + uint8_t *dstBuffer = (uint8_t *)*dst; + uint16_t bytesToWrite = 0; + const UTF32Char byteMask = 0xBF; + const UTF32Char byteMark = 0x80; + static const uint8_t firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; + + while (srcLength-- > 0) { + currentChar = *(src++); + + /* Figure out how many bytes the result will require */ + if (currentChar < (UTF32Char)0x80) { + bytesToWrite = 1; + } else if (currentChar < (UTF32Char)0x800) { + bytesToWrite = 2; + } else if (currentChar < (UTF32Char)0x10000) { + bytesToWrite = 3; + } else if (currentChar < (UTF32Char)0x200000) { + bytesToWrite = 4; + } else { + bytesToWrite = 2; + currentChar = UNI_REPLACEMENT_CHAR; + } + + usedLength += bytesToWrite; + + if (dstLength) { + if (usedLength > dstLength) return false; + + dstBuffer += bytesToWrite; + switch (bytesToWrite) { /* note: everything falls through. */ + case 4: *--dstBuffer = (currentChar | byteMark) & byteMask; currentChar >>= 6; + case 3: *--dstBuffer = (currentChar | byteMark) & byteMask; currentChar >>= 6; + case 2: *--dstBuffer = (currentChar | byteMark) & byteMask; currentChar >>= 6; + case 1: *--dstBuffer = currentChar | firstByteMark[bytesToWrite]; + } + dstBuffer += bytesToWrite; + } + } + + *dst = dstBuffer; + } else { + UTF32Char *dstBuffer = (UTF32Char *)*dst; + + while (srcLength-- > 0) { + currentChar = *(src++); + + ++usedLength; + if (dstLength) { + if (usedLength > dstLength) return false; + *(dstBuffer++) = currentChar; + } + } + + *dst = dstBuffer; + } + + *filledLength = usedLength; + + return true; +} diff --git a/StringEncodings.subproj/CFUniChar.h b/StringEncodings.subproj/CFUniChar.h new file mode 100644 index 0000000..508ff54 --- /dev/null +++ b/StringEncodings.subproj/CFUniChar.h @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFUniChar.h + Copyright (c) 1998-2003, Apple, Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFUNICHAR__) +#define __COREFOUNDATION_CFUNICHAR__ 1 + +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +#define kCFUniCharBitShiftForByte (3) +#define kCFUniCharBitShiftForMask (7) + +CF_INLINE Boolean CFUniCharIsSurrogateHighCharacter(UniChar character) { + return ((character >= 0xD800UL) && (character <= 0xDBFFUL) ? true : false); +} + +CF_INLINE Boolean CFUniCharIsSurrogateLowCharacter(UniChar character) { + return ((character >= 0xDC00UL) && (character <= 0xDFFFUL) ? true : false); +} + +CF_INLINE UTF32Char CFUniCharGetLongCharacterForSurrogatePair(UniChar surrogateHigh, UniChar surrogateLow) { + return ((surrogateHigh - 0xD800UL) << 10) + (surrogateLow - 0xDC00UL) + 0x0010000UL; +} + +// The following values coinside TextEncodingFormat format defines in TextCommon.h +enum { + kCFUniCharUTF16Format = 0, + kCFUniCharUTF8Format = 2, + kCFUniCharUTF32Format = 3 +}; + +CF_INLINE bool CFUniCharIsMemberOfBitmap(UTF16Char theChar, const uint8_t *bitmap) { + return (bitmap && (bitmap[(theChar) >> kCFUniCharBitShiftForByte] & (((uint32_t)1) << (theChar & kCFUniCharBitShiftForMask))) ? true : false); +} + +CF_INLINE void CFUniCharAddCharacterToBitmap(UTF16Char theChar, uint8_t *bitmap) { + bitmap[(theChar) >> kCFUniCharBitShiftForByte] |= (((uint32_t)1) << (theChar & kCFUniCharBitShiftForMask)); +} + +CF_INLINE void CFUniCharRemoveCharacterFromBitmap(UTF16Char theChar, uint8_t *bitmap) { + bitmap[(theChar) >> kCFUniCharBitShiftForByte] &= ~(((uint32_t)1) << (theChar & kCFUniCharBitShiftForMask)); +} + +enum { + kCFUniCharControlCharacterSet = 1, + kCFUniCharWhitespaceCharacterSet, + kCFUniCharWhitespaceAndNewlineCharacterSet, + kCFUniCharDecimalDigitCharacterSet, + kCFUniCharLetterCharacterSet, + kCFUniCharLowercaseLetterCharacterSet, + kCFUniCharUppercaseLetterCharacterSet, + kCFUniCharNonBaseCharacterSet, + kCFUniCharCanonicalDecomposableCharacterSet, + kCFUniCharDecomposableCharacterSet = kCFUniCharCanonicalDecomposableCharacterSet, + kCFUniCharAlphaNumericCharacterSet, + kCFUniCharPunctuationCharacterSet, + kCFUniCharIllegalCharacterSet, + kCFUniCharTitlecaseLetterCharacterSet, + kCFUniCharSymbolAndOperatorCharacterSet, + kCFUniCharCompatibilityDecomposableCharacterSet, + kCFUniCharHFSPlusDecomposableCharacterSet, + kCFUniCharStrongRightToLeftCharacterSet, + kCFUniCharHasNonSelfLowercaseCharacterSet, + kCFUniCharHasNonSelfUppercaseCharacterSet, + kCFUniCharHasNonSelfTitlecaseCharacterSet, + kCFUniCharHasNonSelfCaseFoldingCharacterSet, + kCFUniCharHasNonSelfMirrorMappingCharacterSet, + kCFUniCharControlAndFormatterCharacterSet, + kCFUniCharCaseIgnorableCharacterSet +}; + +CF_EXPORT bool CFUniCharIsMemberOf(UTF32Char theChar, uint32_t charset); + +// This function returns NULL for kCFUniCharControlCharacterSet, kCFUniCharWhitespaceCharacterSet, kCFUniCharWhitespaceAndNewlineCharacterSet, & kCFUniCharIllegalCharacterSet +CF_EXPORT const uint8_t *CFUniCharGetBitmapPtrForPlane(uint32_t charset, uint32_t plane); + +enum { + kCFUniCharBitmapFilled = (uint8_t)0, + kCFUniCharBitmapEmpty = (uint8_t)0xFF, + kCFUniCharBitmapAll = (uint8_t)1 +}; + +CF_EXPORT uint8_t CFUniCharGetBitmapForPlane(uint32_t charset, uint32_t plane, void *bitmap, bool isInverted); + +CF_EXPORT uint32_t CFUniCharGetNumberOfPlanes(uint32_t charset); + +enum { + kCFUniCharToLowercase = 0, + kCFUniCharToUppercase, + kCFUniCharToTitlecase, + kCFUniCharCaseFold +}; + +enum { + kCFUniCharCaseMapFinalSigma = (1), + kCFUniCharCaseMapAfter_i = (1 << 1), + kCFUniCharCaseMapMoreAbove = (1 << 2) +}; + +CF_EXPORT uint32_t CFUniCharMapCaseTo(UTF32Char theChar, UTF16Char *convertedChar, uint32_t maxLength, uint32_t ctype, uint32_t flags, const uint8_t *langCode); + +CF_EXPORT uint32_t CFUniCharGetConditionalCaseMappingFlags(UTF32Char theChar, UTF16Char *buffer, uint32_t currentIndex, uint32_t length, uint32_t type, const uint8_t *langCode, uint32_t lastFlags); + +enum { + kCFUniCharBiDiPropertyON = 0, + kCFUniCharBiDiPropertyL, + kCFUniCharBiDiPropertyR, + kCFUniCharBiDiPropertyAN, + kCFUniCharBiDiPropertyEN, + kCFUniCharBiDiPropertyAL, + kCFUniCharBiDiPropertyNSM, + kCFUniCharBiDiPropertyCS, + kCFUniCharBiDiPropertyES, + kCFUniCharBiDiPropertyET, + kCFUniCharBiDiPropertyBN, + kCFUniCharBiDiPropertyS, + kCFUniCharBiDiPropertyWS, + kCFUniCharBiDiPropertyB, + kCFUniCharBiDiPropertyRLO, + kCFUniCharBiDiPropertyRLE, + kCFUniCharBiDiPropertyLRO, + kCFUniCharBiDiPropertyLRE, + kCFUniCharBiDiPropertyPDF +}; + +enum { + kCFUniCharCombiningProperty = 0, + kCFUniCharBidiProperty +}; + +// The second arg 'bitmap' has to be the pointer to a specific plane +CF_INLINE uint8_t CFUniCharGetBidiPropertyForCharacter(UTF16Char character, const uint8_t *bitmap) { + if (bitmap) { + uint8_t value = bitmap[(character >> 8)]; + + if (value != kCFUniCharBiDiPropertyL) { + bitmap = bitmap + 256 + ((value - kCFUniCharBiDiPropertyPDF - 1) * 256); + return bitmap[character % 256]; + } + } + return kCFUniCharBiDiPropertyL; +} + +CF_INLINE uint8_t CFUniCharGetCombiningPropertyForCharacter(UTF16Char character, const uint8_t *bitmap) { + if (bitmap) { + uint8_t value = bitmap[(character >> 8)]; + + if (value) { + bitmap = bitmap + 256 + ((value - 1) * 256); + return bitmap[character % 256]; + } + } + return 0; +} + +CF_EXPORT const void *CFUniCharGetUnicodePropertyDataForPlane(uint32_t propertyType, uint32_t plane); +CF_EXPORT uint32_t CFUniCharGetNumberOfPlanesForUnicodePropertyData(uint32_t propertyType); +CF_EXPORT uint32_t CFUniCharGetUnicodeProperty(UTF32Char character, uint32_t propertyType); + +CF_EXPORT bool CFUniCharFillDestinationBuffer(const UTF32Char *src, uint32_t srcLength, void **dst, uint32_t dstLength, uint32_t *filledLength, uint32_t dstFormat); + +#if defined(__cplusplus) +} +#endif + +#endif /* ! __COREFOUNDATION_CFUNICHAR__ */ + diff --git a/StringEncodings.subproj/CFUniCharPriv.h b/StringEncodings.subproj/CFUniCharPriv.h new file mode 100644 index 0000000..4446e64 --- /dev/null +++ b/StringEncodings.subproj/CFUniCharPriv.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFUniCharPriv.h + Copyright (c) 1998-2003, Apple, Inc. All rights reserved. +*/ + +#if !defined __UCHAR_PRIVATE__ +#define __UCHAR_PRIVATE__ 1 + +#include +#include "CFUniChar.h" + +#define kCFUniCharRecursiveDecompositionFlag (1 << 30) +#define kCFUniCharNonBmpFlag (1 << 31) +#define CFUniCharConvertCountToFlag(count) ((count & 0x1F) << 24) +#define CFUniCharConvertFlagToCount(flag) ((flag >> 24) & 0x1F) + +enum { + kCFUniCharCanonicalDecompMapping = (kCFUniCharCaseFold + 1), + kCFUniCharCanonicalPrecompMapping, + kCFUniCharCompatibilityDecompMapping +}; + +CF_EXPORT const void *CFUniCharGetMappingData(uint32_t type); + +#endif /* __UCHAR_PRIVATE__ */ diff --git a/StringEncodings.subproj/CFUnicodeDecomposition.c b/StringEncodings.subproj/CFUnicodeDecomposition.c new file mode 100644 index 0000000..2aa5d07 --- /dev/null +++ b/StringEncodings.subproj/CFUnicodeDecomposition.c @@ -0,0 +1,581 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFUnicodeDecomposition.c + Copyright 1999-2002, Apple, Inc. All rights reserved. + Responsibility: Aki Inoue +*/ + +#include +#if KERNEL +#include +#include "CFUnicodeDecomposition.h" +#include "CFUniCharNonBaseData.h" +#include "CFUniCharDecompData.h" +#include "CFUniCharCombiningPriorityData.h" +#else KERNEL +#include +#include +#include "CFUniChar.h" +#include "CFUnicodeDecomposition.h" +#include "CFInternal.h" +#include "CFUniCharPriv.h" +#endif KERNEL + +// Canonical Decomposition +#if KERNEL +static const uint32_t __CFUniCharDecompositionTableLength = (sizeof(__CFUniCharDecompositionTable) / (sizeof(uint32_t) * 2)); +// The kernel version of __CFUniCharIsDecomposableCharacter doesn't have exception ranges +#define __CFUniCharIsDecomposableCharacterWithFlag(character,isHFSPlus) (__CFUniCharIsDecomposableCharacter(character)) + +uint8_t **CFUniCharCombiningPriorityTable = __CFUniCharCombiningPriorityTable; +uint8_t **CFUniCharCombiningPriorityExtraTable = __CFUniCharCombiningPriorityExtraTable; +uint8_t CFUniCharNumberOfPlanesForCombiningPriority = sizeof(__CFUniCharCombiningPriorityTable) / sizeof(*__CFUniCharCombiningPriorityTable); +uint8_t **CFUniCharNonBaseBitmap = __CFUniCharNonBaseBitmap; +uint8_t CFUniCharNumberOfPlanesForNonBaseBitmap = sizeof(__CFUniCharNonBaseBitmap) / sizeof(*__CFUniCharNonBaseBitmap); +#else KERNEL +static UTF32Char *__CFUniCharDecompositionTable = NULL; +static uint32_t __CFUniCharDecompositionTableLength = 0; +static UTF32Char *__CFUniCharMultipleDecompositionTable = NULL; + +static const uint8_t *__CFUniCharDecomposableBitmapForBMP = NULL; +static const uint8_t *__CFUniCharHFSPlusDecomposableBitmapForBMP = NULL; +static const uint8_t *__CFUniCharNonBaseBitmapForBMP = NULL; + +static CFSpinLock_t __CFUniCharDecompositionTableLock = 0; + +static const uint8_t **__CFUniCharCombiningPriorityTable = NULL; +static uint8_t __CFUniCharCombiningPriorityTableNumPlane = 0; + +static void __CFUniCharLoadDecompositionTable(void) { + + __CFSpinLock(&__CFUniCharDecompositionTableLock); + + if (NULL == __CFUniCharDecompositionTable) { + const void *bytes = CFUniCharGetMappingData(kCFUniCharCanonicalDecompMapping); + + if (NULL == bytes) { + __CFSpinUnlock(&__CFUniCharDecompositionTableLock); + return; + } + + __CFUniCharDecompositionTableLength = *(((uint32_t *)bytes)++); + __CFUniCharDecompositionTable = (UTF32Char *)bytes; + __CFUniCharMultipleDecompositionTable = (UTF32Char *)((intptr_t)bytes + __CFUniCharDecompositionTableLength); + + __CFUniCharDecompositionTableLength /= (sizeof(uint32_t) * 2); + __CFUniCharDecomposableBitmapForBMP = CFUniCharGetBitmapPtrForPlane(kCFUniCharCanonicalDecomposableCharacterSet, 0); + __CFUniCharHFSPlusDecomposableBitmapForBMP = CFUniCharGetBitmapPtrForPlane(kCFUniCharHFSPlusDecomposableCharacterSet, 0); + __CFUniCharNonBaseBitmapForBMP = CFUniCharGetBitmapPtrForPlane(kCFUniCharNonBaseCharacterSet, 0); + } + + __CFSpinUnlock(&__CFUniCharDecompositionTableLock); +} + +static CFSpinLock_t __CFUniCharCompatibilityDecompositionTableLock = 0; +static UTF32Char *__CFUniCharCompatibilityDecompositionTable = NULL; +static uint32_t __CFUniCharCompatibilityDecompositionTableLength = 0; +static UTF32Char *__CFUniCharCompatibilityMultipleDecompositionTable = NULL; + +static void __CFUniCharLoadCompatibilityDecompositionTable(void) { + + __CFSpinLock(&__CFUniCharCompatibilityDecompositionTableLock); + + if (NULL == __CFUniCharCompatibilityDecompositionTable) { + const void *bytes = CFUniCharGetMappingData(kCFUniCharCompatibilityDecompMapping); + + if (NULL == bytes) { + __CFSpinUnlock(&__CFUniCharCompatibilityDecompositionTableLock); + return; + } + + __CFUniCharCompatibilityDecompositionTableLength = *(((uint32_t *)bytes)++); + __CFUniCharCompatibilityDecompositionTable = (UTF32Char *)bytes; + __CFUniCharCompatibilityMultipleDecompositionTable = (UTF32Char *)((intptr_t)bytes + __CFUniCharCompatibilityDecompositionTableLength); + + __CFUniCharCompatibilityDecompositionTableLength /= (sizeof(uint32_t) * 2); + } + + __CFSpinUnlock(&__CFUniCharCompatibilityDecompositionTableLock); +} + +CF_INLINE bool __CFUniCharIsDecomposableCharacterWithFlag(UTF32Char character, bool isHFSPlus) { + return CFUniCharIsMemberOfBitmap(character, (character < 0x10000 ? (isHFSPlus ? __CFUniCharHFSPlusDecomposableBitmapForBMP : __CFUniCharDecomposableBitmapForBMP) : CFUniCharGetBitmapPtrForPlane(kCFUniCharCanonicalDecomposableCharacterSet, ((character >> 16) & 0xFF)))); +} + +CF_INLINE bool __CFUniCharIsNonBaseCharacter(UTF32Char character) { + return CFUniCharIsMemberOfBitmap(character, (character < 0x10000 ? __CFUniCharNonBaseBitmapForBMP : CFUniCharGetBitmapPtrForPlane(kCFUniCharNonBaseCharacterSet, ((character >> 16) & 0xFF)))); +} +#endif KERNEL + +typedef struct { + uint32_t _key; + uint32_t _value; +} __CFUniCharDecomposeMappings; + +static uint32_t __CFUniCharGetMappedValue(const __CFUniCharDecomposeMappings *theTable, uint32_t numElem, UTF32Char character) { + const __CFUniCharDecomposeMappings *p, *q, *divider; + + if ((character < theTable[0]._key) || (character > theTable[numElem-1]._key)) { + return 0; + } + p = theTable; + q = p + (numElem-1); + while (p <= q) { + divider = p + ((q - p) >> 1); /* divide by 2 */ + if (character < divider->_key) { q = divider - 1; } + else if (character > divider->_key) { p = divider + 1; } + else { return divider->_value; } + } + return 0; +} + +#if KERNEL +#define __CFUniCharGetCombiningPropertyForCharacter(character) __CFUniCharGetCombiningPriority(character) +#else KERNEL +#define __CFUniCharGetCombiningPropertyForCharacter(character) CFUniCharGetCombiningPropertyForCharacter(character, (((character) >> 16) < __CFUniCharCombiningPriorityTableNumPlane ? __CFUniCharCombiningPriorityTable[(character) >> 16] : NULL)) +#endif KERNEL + +static void __CFUniCharPrioritySort(UTF32Char *characters, uint32_t length) { + uint32_t p1, p2; + UTF32Char *ch1, *ch2; + bool changes = true; + UTF32Char *end = characters + length; + +#if !KERNEL + if (NULL == __CFUniCharCombiningPriorityTable) { + __CFSpinLock(&__CFUniCharDecompositionTableLock); + if (NULL == __CFUniCharCombiningPriorityTable) { + uint32_t numPlanes = CFUniCharGetNumberOfPlanesForUnicodePropertyData(kCFUniCharCombiningProperty); + uint32_t idx; + + __CFUniCharCombiningPriorityTable = (const uint8_t **)CFAllocatorAllocate(NULL, sizeof(uint8_t *) * numPlanes, 0); + for (idx = 0;idx < numPlanes;idx++) __CFUniCharCombiningPriorityTable[idx] = CFUniCharGetUnicodePropertyDataForPlane(kCFUniCharCombiningProperty, idx); + __CFUniCharCombiningPriorityTableNumPlane = numPlanes; + } + __CFSpinUnlock(&__CFUniCharDecompositionTableLock); + } +#endif !KERNEL + + if (length < 2) return; + + do { + changes = false; + ch1 = characters; ch2 = characters + 1; + p2 = __CFUniCharGetCombiningPropertyForCharacter(*ch1); + while (ch2 < end) { + p1 = p2; p2 = __CFUniCharGetCombiningPropertyForCharacter(*ch2); + if (p1 > p2) { + UTF32Char tmp = *ch1; *ch1 = *ch2; *ch2 = tmp; + changes = true; + } + ++ch1; ++ch2; + } + } while (changes); +} + +static uint32_t __CFUniCharRecursivelyDecomposeCharacter(UTF32Char character, UTF32Char *convertedChars, uint32_t maxBufferLength) { + uint32_t value = __CFUniCharGetMappedValue((const __CFUniCharDecomposeMappings *)__CFUniCharDecompositionTable, __CFUniCharDecompositionTableLength, character); + uint32_t length = CFUniCharConvertFlagToCount(value); + UTF32Char firstChar = value & 0xFFFFFF; +#if KERNEL + const UTF32Char *mappings = (kCFUniCharNonBmpFlag & value ? (length == 1 ? &firstChar : __CFUniCharNonBMPMultipleDecompositionTable + firstChar) : NULL); + UTF16Char theChar = (UTF16Char)firstChar; + const UTF16Char *bmpMappings = (mappings ? NULL : (length == 1 ? &theChar : __CFUniCharMultipleDecompositionTable + firstChar)); +#else KERNEL + UTF32Char *mappings = (length > 1 ? __CFUniCharMultipleDecompositionTable + firstChar : &firstChar); +#endif KERNEL + uint32_t usedLength = 0; + + if (maxBufferLength < length) return 0; + +#if KERNEL + if (bmpMappings) { + if (value & kCFUniCharRecursiveDecompositionFlag) { + usedLength = __CFUniCharRecursivelyDecomposeCharacter((UTF32Char)*bmpMappings, convertedChars, maxBufferLength - length); + + --length; // Decrement for the first char + if (!usedLength || usedLength + length > maxBufferLength) return 0; + ++bmpMappings; + convertedChars += usedLength; + } + + usedLength += length; + + while (length--) *(convertedChars++) = *(bmpMappings++); + + return usedLength; + } +#endif KERNEL + if (value & kCFUniCharRecursiveDecompositionFlag) { + usedLength = __CFUniCharRecursivelyDecomposeCharacter(*mappings, convertedChars, maxBufferLength - length); + + --length; // Decrement for the first char + if (!usedLength || usedLength + length > maxBufferLength) return 0; + ++mappings; + convertedChars += usedLength; + } + + usedLength += length; + + while (length--) *(convertedChars++) = *(mappings++); + + return usedLength; +} + +#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) + +uint32_t CFUniCharDecomposeCharacter(UTF32Char character, UTF32Char *convertedChars, uint32_t maxBufferLength) { +#if !KERNEL + if (NULL == __CFUniCharDecompositionTable) __CFUniCharLoadDecompositionTable(); +#endif !KERNEL + if (character >= HANGUL_SBASE && character <= (HANGUL_SBASE + HANGUL_SCOUNT)) { + uint32_t length; + + character -= HANGUL_SBASE; + + length = (character % HANGUL_TCOUNT ? 3 : 2); + + if (maxBufferLength < length) return 0; + + *(convertedChars++) = character / HANGUL_NCOUNT + HANGUL_LBASE; + *(convertedChars++) = (character % HANGUL_NCOUNT) / HANGUL_TCOUNT + HANGUL_VBASE; + if (length > 2) *convertedChars = (character % HANGUL_TCOUNT) + HANGUL_TBASE; + return length; + } else { + return __CFUniCharRecursivelyDecomposeCharacter(character, convertedChars, maxBufferLength); + } +} + +#if KERNEL +#define CFAllocatorAllocate(a,size,flag) malloc((size)) +#define CFAllocatorDeallocate(a,ptr) free((ptr)) +#endif KERNEL + +#define MAX_BUFFER_LENGTH (32) +bool CFUniCharDecompose(const UTF16Char *src, uint32_t length, uint32_t *consumedLength, void *dst, uint32_t maxLength, uint32_t *filledLength, bool needToReorder, uint32_t dstFormat, bool isHFSPlus) { + uint32_t usedLength = 0; + uint32_t originalLength = length; + UTF32Char buffer[MAX_BUFFER_LENGTH]; + UTF32Char *decompBuffer = buffer; + uint32_t decompBufferLen = MAX_BUFFER_LENGTH; + UTF32Char currentChar; + uint32_t idx; + bool isDecomp = false; + bool isNonBase = false; + +#if !KERNEL + if (NULL == __CFUniCharDecompositionTable) __CFUniCharLoadDecompositionTable(); +#endif !KERNEL + + while (length > 0) { + currentChar = *(src++); + --length; + + if (currentChar < 0x80) { + if (maxLength) { + if (usedLength < maxLength) { + switch (dstFormat) { + case kCFUniCharUTF8Format: *(((uint8_t *)dst)++) = currentChar; break; + case kCFUniCharUTF16Format: *(((UTF16Char *)dst)++) = currentChar; break; + case kCFUniCharUTF32Format: *(((UTF32Char *)dst)++) = currentChar; break; + } + } else { + if (decompBuffer != buffer) CFAllocatorDeallocate(NULL, decompBuffer); + if (consumedLength) *consumedLength = originalLength - length - 1; + if (filledLength) *filledLength = usedLength; + return false; + } + } + ++usedLength; + continue; + } + + if (CFUniCharIsSurrogateHighCharacter(currentChar) && (length > 0) && CFUniCharIsSurrogateLowCharacter(*src)) { + currentChar = CFUniCharGetLongCharacterForSurrogatePair(currentChar, *(src++)); + --length; + } + + isDecomp = __CFUniCharIsDecomposableCharacterWithFlag(currentChar, isHFSPlus); + isNonBase = (needToReorder && __CFUniCharIsNonBaseCharacter(currentChar)); + + if (!isDecomp || isNonBase) { + if (isNonBase) { + if (isDecomp) { + idx = CFUniCharDecomposeCharacter(currentChar, decompBuffer, MAX_BUFFER_LENGTH); + } else { + idx = 1; + *decompBuffer = currentChar; + } + + while (length > 0) { + if (CFUniCharIsSurrogateHighCharacter(*src) && ((length + 1) > 0) && CFUniCharIsSurrogateLowCharacter(*(src + 1))) { + currentChar = CFUniCharGetLongCharacterForSurrogatePair(*src, *(src + 1)); + } else { + currentChar = *src; + } + if (__CFUniCharIsNonBaseCharacter(currentChar)) { + if (currentChar > 0xFFFF) { // Non-BMP + length -= 2; + src += 2; + } else { + --length; + ++src; + } + if ((idx + 1) >= decompBufferLen) { + UTF32Char *newBuffer; + + decompBufferLen += MAX_BUFFER_LENGTH; + newBuffer = (UTF32Char *)CFAllocatorAllocate(NULL, sizeof(UTF32Char) * decompBufferLen, 0); + memmove(newBuffer, decompBuffer, (decompBufferLen - MAX_BUFFER_LENGTH) * sizeof(UTF32Char)); + if (decompBuffer != buffer) CFAllocatorDeallocate(NULL, decompBuffer); + decompBuffer = newBuffer; + } + + if (__CFUniCharIsDecomposableCharacterWithFlag(currentChar, isHFSPlus)) { // Vietnamese accent, etc. + idx += CFUniCharDecomposeCharacter(currentChar, decompBuffer + idx, MAX_BUFFER_LENGTH - idx); + } else { + decompBuffer[idx++] = currentChar; + } + } else { + break; + } + } + + if (idx > 1) { // Need to reorder + __CFUniCharPrioritySort(decompBuffer, idx); + } + if (!CFUniCharFillDestinationBuffer(decompBuffer, idx, &dst, maxLength, &usedLength, dstFormat)) { + if (decompBuffer != buffer) CFAllocatorDeallocate(NULL, decompBuffer); + if (consumedLength) *consumedLength = originalLength - length; + if (filledLength) *filledLength = usedLength; + return false; + } + } else { + if (dstFormat == kCFUniCharUTF32Format) { + ++usedLength; + if (maxLength) { + if (usedLength > maxLength) { + if (decompBuffer != buffer) CFAllocatorDeallocate(NULL, decompBuffer); + if (consumedLength) *consumedLength = originalLength - length; + if (filledLength) *filledLength = usedLength; + return false; + } + *(((UTF32Char *)dst)++) = currentChar; + } + } else { + if (!CFUniCharFillDestinationBuffer(¤tChar, 1, &dst, maxLength, &usedLength, dstFormat)) { + if (decompBuffer != buffer) CFAllocatorDeallocate(NULL, decompBuffer); + if (consumedLength) *consumedLength = originalLength - length; + if (filledLength) *filledLength = usedLength; + return false; + } + } + } + } else { + if (dstFormat == kCFUniCharUTF32Format && maxLength) { + idx = CFUniCharDecomposeCharacter(currentChar, dst, maxLength - usedLength); + + if (idx == 0) { + if (decompBuffer != buffer) CFAllocatorDeallocate(NULL, decompBuffer); + if (consumedLength) *consumedLength = originalLength - length; + if (filledLength) *filledLength = usedLength; + return false; + } else if (needToReorder && (idx > 1)) { // Need to reorder + bool moreCombiningMarks = false; + ++((UTF32Char *)dst); --idx; ++usedLength; // Skip the base + + while (length > 0) { + if (CFUniCharIsSurrogateHighCharacter(*src) && ((length + 1) > 0) && CFUniCharIsSurrogateLowCharacter(*(src + 1))) { + currentChar = CFUniCharGetLongCharacterForSurrogatePair(*src, *(src + 1)); + } else { + currentChar = *src; + } + if (__CFUniCharIsNonBaseCharacter(currentChar)) { + if (currentChar > 0xFFFF) { // Non-BMP + length -= 2; + src += 2; + } else { + --length; + ++src; + } + if ((idx + usedLength + 1) >= maxLength) { + if (decompBuffer != buffer) CFAllocatorDeallocate(NULL, decompBuffer); + if (consumedLength) *consumedLength = originalLength - length; + if (filledLength) *filledLength = usedLength; + return false; + } + ((UTF32Char *)dst)[idx++] = currentChar; + moreCombiningMarks = true; + } else { + break; + } + } + if (moreCombiningMarks) __CFUniCharPrioritySort(((UTF32Char *)dst), idx); + + } + usedLength += idx; + ((UTF32Char *)dst) += idx; + } else { + idx = CFUniCharDecomposeCharacter(currentChar, decompBuffer, decompBufferLen); + + if (maxLength && idx + usedLength > maxLength) { + if (decompBuffer != buffer) CFAllocatorDeallocate(NULL, decompBuffer); + if (consumedLength) *consumedLength = originalLength - length; + if (filledLength) *filledLength = usedLength; + return false; + } else if (needToReorder && (idx > 1)) { // Need to reorder + bool moreCombiningMarks = false; + + while (length > 0) { + if (CFUniCharIsSurrogateHighCharacter(*src) && ((length + 1) > 0) && CFUniCharIsSurrogateLowCharacter(*(src + 1))) { + currentChar = CFUniCharGetLongCharacterForSurrogatePair(*src, *(src + 1)); + } else { + currentChar = *src; + } + if (__CFUniCharIsNonBaseCharacter(currentChar)) { + if (currentChar > 0xFFFF) { // Non-BMP + length -= 2; + src += 2; + } else { + --length; + ++src; + } + if ((idx + 1) >= decompBufferLen) { + UTF32Char *newBuffer; + + decompBufferLen += MAX_BUFFER_LENGTH; + newBuffer = (UTF32Char *)CFAllocatorAllocate(NULL, sizeof(UTF32Char) * decompBufferLen, 0); + memmove(newBuffer, decompBuffer, (decompBufferLen - MAX_BUFFER_LENGTH) * sizeof(UTF32Char)); + if (decompBuffer != buffer) CFAllocatorDeallocate(NULL, decompBuffer); + decompBuffer = newBuffer; + } + decompBuffer[idx++] = currentChar; + moreCombiningMarks = true; + } else { + break; + } + } + if (moreCombiningMarks) __CFUniCharPrioritySort(decompBuffer + 1, idx - 1); + } + if (!CFUniCharFillDestinationBuffer(decompBuffer, idx, &dst, maxLength, &usedLength, dstFormat)) { + if (decompBuffer != buffer) CFAllocatorDeallocate(NULL, decompBuffer); + if (consumedLength) *consumedLength = originalLength - length; + if (filledLength) *filledLength = usedLength; + return false; + } + } + } + } + if (decompBuffer != buffer) CFAllocatorDeallocate(NULL, decompBuffer); + + if (consumedLength) *consumedLength = originalLength - length; + if (filledLength) *filledLength = usedLength; + + return true; +} + +#if !KERNEL + +#define MAX_COMP_DECOMP_LEN (32) + +static uint32_t __CFUniCharRecursivelyCompatibilityDecomposeCharacter(UTF32Char character, UTF32Char *convertedChars) { + uint32_t value = __CFUniCharGetMappedValue((const __CFUniCharDecomposeMappings *)__CFUniCharCompatibilityDecompositionTable, __CFUniCharCompatibilityDecompositionTableLength, character); + uint32_t length = CFUniCharConvertFlagToCount(value); + UTF32Char firstChar = value & 0xFFFFFF; + const UTF32Char *mappings = (length > 1 ? __CFUniCharCompatibilityMultipleDecompositionTable + firstChar : &firstChar); + uint32_t usedLength = length; + UTF32Char currentChar; + uint32_t currentLength; + + while (length-- > 0) { + currentChar = *(mappings++); + if (__CFUniCharIsDecomposableCharacterWithFlag(currentChar, false)) { + currentLength = __CFUniCharRecursivelyDecomposeCharacter(currentChar, convertedChars, MAX_COMP_DECOMP_LEN - length); + convertedChars += currentLength; + usedLength += (currentLength - 1); + } else if (CFUniCharIsMemberOf(currentChar, kCFUniCharCompatibilityDecomposableCharacterSet)) { + currentLength = __CFUniCharRecursivelyCompatibilityDecomposeCharacter(currentChar, convertedChars); + convertedChars += currentLength; + usedLength += (currentLength - 1); + } else { + *(convertedChars++) = currentChar; + } + } + + return usedLength; +} + +CF_INLINE void __CFUniCharMoveBufferFromEnd(UTF32Char *convertedChars, uint32_t length, uint32_t delta) { + const UTF32Char *limit = convertedChars; + UTF32Char *dstP; + + convertedChars += length; + dstP = convertedChars + delta; + + while (convertedChars > limit) *(--dstP) = *(--convertedChars); +} + +__private_extern__ uint32_t CFUniCharCompatibilityDecompose(UTF32Char *convertedChars, uint32_t length, uint32_t maxBufferLength) { + UTF32Char currentChar; + UTF32Char buffer[MAX_COMP_DECOMP_LEN]; + const UTF32Char *bufferP; + const UTF32Char *limit = convertedChars + length; + uint32_t filledLength; + + if (NULL == __CFUniCharCompatibilityDecompositionTable) __CFUniCharLoadCompatibilityDecompositionTable(); + + while (convertedChars < limit) { + currentChar = *convertedChars; + + if (CFUniCharIsMemberOf(currentChar, kCFUniCharCompatibilityDecomposableCharacterSet)) { + filledLength = __CFUniCharRecursivelyCompatibilityDecomposeCharacter(currentChar, buffer); + + if (filledLength + length - 1 > maxBufferLength) return 0; + + if (filledLength > 1) __CFUniCharMoveBufferFromEnd(convertedChars + 1, limit - convertedChars - 1, filledLength - 1); + + bufferP = buffer; + length += (filledLength - 1); + while (filledLength-- > 0) *(convertedChars++) = *(bufferP++); + } else { + ++convertedChars; + } + } + + return length; +} + +CF_EXPORT void CFUniCharPrioritySort(UTF32Char *characters, uint32_t length) { + __CFUniCharPrioritySort(characters, length); +} +#endif !KERNEL diff --git a/StringEncodings.subproj/CFUnicodeDecomposition.h b/StringEncodings.subproj/CFUnicodeDecomposition.h new file mode 100644 index 0000000..4f9659e --- /dev/null +++ b/StringEncodings.subproj/CFUnicodeDecomposition.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* + * CFUnicodeDecomposition.h + * CoreFoundation + * + * Created by aki on Wed Oct 03 2001. + * Copyright (c) 2001-2003, Apple Inc. All rights reserved. + * + */ + +#if !defined(__COREFOUNDATION_CFUNICODEDECOMPOSITION__) +#define __COREFOUNDATION_CFUNICODEDECOMPOSITION__ 1 + +#if !defined(KERNEL) +#define KERNEL 0 +#endif + +#if KERNEL +#include "CFKernelTypes.h" +#else // KERNEL +#include "CFUniChar.h" +#endif /* KERNEL */ + +#if defined(__cplusplus) +extern "C" { +#endif + +#if !KERNEL +CF_INLINE bool CFUniCharIsDecomposableCharacter(UTF32Char character, bool isHFSPlusCanonical) { + if (isHFSPlusCanonical && !isHFSPlusCanonical) return false; // hack to get rid of "unused" warning + if (character < 0x80) return false; + return CFUniCharIsMemberOf(character, kCFUniCharHFSPlusDecomposableCharacterSet); +} +#endif /* !KERNEL */ + +CF_EXPORT uint32_t CFUniCharDecomposeCharacter(UTF32Char character, UTF32Char *convertedChars, uint32_t maxBufferLength); +#if !KERNEL +CF_EXPORT uint32_t CFUniCharCompatibilityDecompose(UTF32Char *convertedChars, uint32_t length, uint32_t maxBufferLength); +#endif /* !KERNEL */ + +CF_EXPORT bool CFUniCharDecompose(const UTF16Char *src, uint32_t length, uint32_t *consumedLength, void *dst, uint32_t maxLength, uint32_t *filledLength, bool needToReorder, uint32_t dstFormat, bool isHFSPlus); + +CF_EXPORT void CFUniCharPrioritySort(UTF32Char *characters, uint32_t length); + +#if defined(__cplusplus) +} +#endif + +#endif /* ! __COREFOUNDATION_CFUNICODEDECOMPOSITION__ */ + diff --git a/StringEncodings.subproj/CFUnicodePrecomposition.c b/StringEncodings.subproj/CFUnicodePrecomposition.c new file mode 100644 index 0000000..cea2844 --- /dev/null +++ b/StringEncodings.subproj/CFUnicodePrecomposition.c @@ -0,0 +1,366 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFUnicodePrecomposition.c + Copyright 1999-2002, Apple, Inc. All rights reserved. + Responsibility: Aki Inoue +*/ + +#if !defined(KERNEL) +#define KERNEL 0 +#endif + +#include +#if KERNEL +#include "CFUnicodePrecomposition.h" +#include "CFUniCharPrecompData.h" +#else KERNEL +#include +#include +#include "CFUniChar.h" +#include "CFUnicodePrecomposition.h" +#include "CFInternal.h" +#include "CFUniCharPriv.h" +#endif KERNEL + +// Canonical Precomposition +#if KERNEL +static const uint32_t __CFUniCharPrecompositionTableLength = (sizeof(__CFUniCharPrecompSourceTable) / (sizeof(uint32_t) * 2)); +CF_EXPORT uint8_t **CFUniCharCombiningPriorityTable; +CF_EXPORT uint8_t **CFUniCharCombiningPriorityExtraTable; +CF_EXPORT uint8_t CFUniCharNumberOfPlanesForCombiningPriority; + +CF_EXPORT uint8_t __CFUniCharGetCombiningPriority(UTF32Char character) { + if (character < (CFUniCharNumberOfPlanesForCombiningPriority << 16)) { + uint32_t plane = character >> 16; + const uint8_t *bitmap = CFUniCharCombiningPriorityTable[plane]; + + if (bitmap) { + uint8_t value = bitmap[(character >> 8) & 0xFF]; + + if (value) { + bitmap = CFUniCharCombiningPriorityExtraTable[plane] + ((value - 1) * 256); + return bitmap[character % 256]; + } + } + } + return 0; +} + +CF_EXPORT uint8_t **CFUniCharNonBaseBitmap; +CF_EXPORT uint8_t CFUniCharNumberOfPlanesForNonBaseBitmap; + +CF_INLINE bool __CFUniCharIsNonBaseCharacter(UTF32Char character) { + if (character < (CFUniCharNumberOfPlanesForNonBaseBitmap << 16)) { + const uint8_t *bitmap = CFUniCharNonBaseBitmap[character >> 16]; + uint8_t value = bitmap[(character >> 8) & 0xFF]; + + if (value == 0xFF) { + return true; + } else if (value) { + bitmap = bitmap + ((value - 1) * 32) + 256; + return (bitmap[(character & 0xFF) / 8] & (1 << (character % 8)) ? true : false); + } + } + return false; +} + +#else KERNEL +static UTF32Char *__CFUniCharPrecompSourceTable = NULL; +static uint32_t __CFUniCharPrecompositionTableLength = 0; +static uint16_t *__CFUniCharBMPPrecompDestinationTable = NULL; +static uint32_t *__CFUniCharNonBMPPrecompDestinationTable = NULL; + +static const uint8_t *__CFUniCharNonBaseBitmapForBMP_P = NULL; // Adding _P so the symbol name is different from the one in CFUnicodeDecomposition.c +static const uint8_t *__CFUniCharCombiningClassForBMP = NULL; + +static CFSpinLock_t __CFUniCharPrecompositionTableLock = 0; + +static void __CFUniCharLoadPrecompositionTable(void) { + + __CFSpinLock(&__CFUniCharPrecompositionTableLock); + + if (NULL == __CFUniCharPrecompSourceTable) { + const void *bytes = CFUniCharGetMappingData(kCFUniCharCanonicalPrecompMapping); + uint32_t bmpMappingLength; + + if (NULL == bytes) { + __CFSpinUnlock(&__CFUniCharPrecompositionTableLock); + return; + } + + __CFUniCharPrecompositionTableLength = *(((uint32_t *)bytes)++); + bmpMappingLength = *(((uint32_t *)bytes)++); + __CFUniCharPrecompSourceTable = (UTF32Char *)bytes; + __CFUniCharBMPPrecompDestinationTable = (uint16_t *)((intptr_t)bytes + (__CFUniCharPrecompositionTableLength * sizeof(UTF32Char) * 2)); + __CFUniCharNonBMPPrecompDestinationTable = (uint32_t *)(((intptr_t)__CFUniCharBMPPrecompDestinationTable) + bmpMappingLength); + + __CFUniCharNonBaseBitmapForBMP_P = CFUniCharGetBitmapPtrForPlane(kCFUniCharNonBaseCharacterSet, 0); + __CFUniCharCombiningClassForBMP = CFUniCharGetUnicodePropertyDataForPlane(kCFUniCharCombiningProperty, 0); + } + + __CFSpinUnlock(&__CFUniCharPrecompositionTableLock); +} + + // Adding _P so the symbol name is different from the one in CFUnicodeDecomposition.c +#define __CFUniCharIsNonBaseCharacter __CFUniCharIsNonBaseCharacter_P +CF_INLINE bool __CFUniCharIsNonBaseCharacter(UTF32Char character) { + return CFUniCharIsMemberOfBitmap(character, (character < 0x10000 ? __CFUniCharNonBaseBitmapForBMP_P : CFUniCharGetBitmapPtrForPlane(kCFUniCharNonBaseCharacterSet, ((character >> 16) & 0xFF)))); +} +#endif KERNEL + +typedef struct { + UTF16Char _key; + UTF16Char _value; +} __CFUniCharPrecomposeBMPMappings; + +static UTF16Char __CFUniCharGetMappedBMPValue(const __CFUniCharPrecomposeBMPMappings *theTable, uint32_t numElem, UTF16Char character) { + const __CFUniCharPrecomposeBMPMappings *p, *q, *divider; + + if ((character < theTable[0]._key) || (character > theTable[numElem-1]._key)) { + return 0; + } + p = theTable; + q = p + (numElem-1); + while (p <= q) { + divider = p + ((q - p) >> 1); /* divide by 2 */ + if (character < divider->_key) { q = divider - 1; } + else if (character > divider->_key) { p = divider + 1; } + else { return divider->_value; } + } + return 0; +} + +typedef struct { + UTF32Char _key; + uint32_t _value; +} __CFUniCharPrecomposeMappings; + +static uint32_t __CFUniCharGetMappedValue_P(const __CFUniCharPrecomposeMappings *theTable, uint32_t numElem, UTF32Char character) { + const __CFUniCharPrecomposeMappings *p, *q, *divider; + + if ((character < theTable[0]._key) || (character > theTable[numElem-1]._key)) { + return 0; + } + p = theTable; + q = p + (numElem-1); + while (p <= q) { + divider = p + ((q - p) >> 1); /* divide by 2 */ + if (character < divider->_key) { q = divider - 1; } + else if (character > divider->_key) { p = divider + 1; } + else { return divider->_value; } + } + return 0; +} + +#if !KERNEL +__private_extern__ +#endif !KERNEL +UTF32Char CFUniCharPrecomposeCharacter(UTF32Char base, UTF32Char combining) { + uint32_t value; + +#if !KERNEL + if (NULL == __CFUniCharPrecompSourceTable) __CFUniCharLoadPrecompositionTable(); +#endif !KERNEL + + if (!(value = __CFUniCharGetMappedValue_P((const __CFUniCharPrecomposeMappings *)__CFUniCharPrecompSourceTable, __CFUniCharPrecompositionTableLength, combining))) return 0xFFFD; + +#if !KERNEL + // We don't have precomposition in non-BMP + if (value & kCFUniCharNonBmpFlag) { + value = __CFUniCharGetMappedValue_P((const __CFUniCharPrecomposeMappings *)((uint32_t *)__CFUniCharNonBMPPrecompDestinationTable + (value & 0xFFFF)), (value >> 16) & 0x7FFF, base); + } else { +#endif !KERNEL + value = __CFUniCharGetMappedBMPValue((const __CFUniCharPrecomposeBMPMappings *)((uint32_t *)__CFUniCharBMPPrecompDestinationTable + (value & 0xFFFF)), (value >> 16), base); +#if !KERNEL + } +#endif !KERNEL + return (value ? value : 0xFFFD); +} + +#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 void __CFUniCharMoveBufferFromEnd(UTF16Char *convertedChars, uint32_t length, uint32_t delta) { + const UTF16Char *limit = convertedChars; + UTF16Char *dstP; + + convertedChars += length; + dstP = convertedChars + delta; + + while (convertedChars > limit) *(--dstP) = *(--convertedChars); +} + +bool CFUniCharPrecompose(const UTF16Char *characters, uint32_t length, uint32_t *consumedLength, UTF16Char *precomposed, uint32_t maxLength, uint32_t *filledLength) { + UTF32Char currentChar = 0, lastChar = 0, precomposedChar = 0xFFFD; + uint32_t originalLength = length, usedLength = 0; + UTF16Char *currentBase = precomposed; + uint8_t currentClass, lastClass = 0; + bool currentBaseIsBMP = true; + bool isPrecomposed; + +#if !KERNEL + if (NULL == __CFUniCharPrecompSourceTable) __CFUniCharLoadPrecompositionTable(); +#endif !KERNEL + + while (length > 0) { + currentChar = *(characters++); + --length; + + if (CFUniCharIsSurrogateHighCharacter(currentChar) && (length > 0) && CFUniCharIsSurrogateLowCharacter(*characters)) { + currentChar = CFUniCharGetLongCharacterForSurrogatePair(currentChar, *(characters++)); + --length; + } + + if (lastChar && __CFUniCharIsNonBaseCharacter(currentChar)) { + isPrecomposed = (precomposedChar == 0xFFFD ? false : true); + if (isPrecomposed) lastChar = precomposedChar; + +#if KERNEL + currentClass = __CFUniCharGetCombiningPriority(currentChar); +#else KERNEL + currentClass = (currentChar > 0xFFFF ? CFUniCharGetUnicodeProperty(currentChar, kCFUniCharCombiningProperty) : CFUniCharGetCombiningPropertyForCharacter(currentChar, __CFUniCharCombiningClassForBMP)); +#endif KERNEL + + if ((lastClass == 0) || (currentClass != lastClass)) { + if ((precomposedChar = CFUniCharPrecomposeCharacter(lastChar, currentChar)) == 0xFFFD) { + if (isPrecomposed) precomposedChar = lastChar; + lastClass = currentClass; + } else { + lastClass = 0; + continue; + } + } + if (currentChar > 0xFFFF) { // Non-BMP + usedLength += 2; + if (usedLength > maxLength) break; + currentChar -= 0x10000; + *(precomposed++) = (UTF16Char)((currentChar >> 10) + 0xD800UL); + *(precomposed++) = (UTF16Char)((currentChar & 0x3FF) + 0xDC00UL); + } else { + ++usedLength; + if (usedLength > maxLength) break; + *(precomposed++) = (UTF16Char)currentChar; + } + } else { + if ((currentChar >= HANGUL_LBASE) && (currentChar < (HANGUL_LBASE + 0xFF))) { // Hangul Jamo + int8_t lIndex = currentChar - HANGUL_LBASE; + + if ((length > 0) && (0 <= lIndex) && (lIndex <= HANGUL_LCOUNT)) { + int16_t vIndex = *characters - HANGUL_VBASE; + + if ((vIndex >= 0) && (vIndex <= HANGUL_VCOUNT)) { + int16_t tIndex = 0; + + ++characters; --length; + + if (length > 0) { + tIndex = *characters - HANGUL_TBASE; + if ((tIndex < 0) || (tIndex > HANGUL_TCOUNT)) { + tIndex = 0; + } else { + ++characters; --length; + } + } + currentChar = (lIndex * HANGUL_VCOUNT + vIndex) * HANGUL_TCOUNT + tIndex + HANGUL_SBASE; + } + } + } + + if (precomposedChar != 0xFFFD) { + if (currentBaseIsBMP) { // Non-BMP + if (lastChar > 0xFFFF) { // Last char was Non-BMP + --usedLength; + memmove(currentBase + 1, currentBase + 2, (precomposed - (currentBase + 2)) * sizeof(UTF16Char)); + } + *(currentBase) = (UTF16Char)precomposedChar; + } else { + if (lastChar < 0x10000) { // Last char was BMP + ++usedLength; + if (usedLength > maxLength) break; + __CFUniCharMoveBufferFromEnd(currentBase + 1, precomposed - (currentBase + 1), 1); + } + precomposedChar -= 0x10000; + *currentBase = (UTF16Char)((precomposedChar >> 10) + 0xD800UL); + *(currentBase + 1) = (UTF16Char)((precomposedChar & 0x3FF) + 0xDC00UL); + } + precomposedChar = 0xFFFD; + } + currentBase = precomposed; + + lastChar = currentChar; + lastClass = 0; + + if (currentChar > 0xFFFF) { // Non-BMP + usedLength += 2; + if (usedLength > maxLength) break; + currentChar -= 0x10000; + *(precomposed++) = (UTF16Char)((currentChar >> 10) + 0xD800UL); + *(precomposed++) = (UTF16Char)((currentChar & 0x3FF) + 0xDC00UL); + currentBaseIsBMP = false; + } else { + ++usedLength; + if (usedLength > maxLength) break; + *(precomposed++) = (UTF16Char)currentChar; + currentBaseIsBMP = true; + } + } + } + + if (precomposedChar != 0xFFFD) { + if (currentChar > 0xFFFF) { // Non-BMP + if (lastChar < 0x10000) { // Last char was BMP + ++usedLength; + if (usedLength > maxLength) { + if (consumedLength) *consumedLength = originalLength - length; + if (filledLength) *filledLength = usedLength; + return false; + } + __CFUniCharMoveBufferFromEnd(currentBase + 1, precomposed - (currentBase + 1), 1); + } + precomposedChar -= 0x10000; + *currentBase = (UTF16Char)((precomposedChar >> 10) + 0xD800UL); + *(currentBase + 1) = (UTF16Char)((precomposedChar & 0x3FF) + 0xDC00UL); + } else { + if (lastChar > 0xFFFF) { // Last char was Non-BMP + --usedLength; + memmove(currentBase + 1, currentBase + 2, (precomposed - (currentBase + 2)) * sizeof(UTF16Char)); + } + *(currentBase) = (UTF16Char)precomposedChar; + } + } + + if (consumedLength) *consumedLength = originalLength - length; + if (filledLength) *filledLength = usedLength; + + return true; +} + diff --git a/StringEncodings.subproj/CFUnicodePrecomposition.h b/StringEncodings.subproj/CFUnicodePrecomposition.h new file mode 100644 index 0000000..2f469ca --- /dev/null +++ b/StringEncodings.subproj/CFUnicodePrecomposition.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* + * CFUnicodePrecomposition.h + * CoreFoundation + * + * Created by aki on Wed Oct 03 2001. + * Copyright (c) 2001-2003, Apple Inc. All rights reserved. + * + */ + +#if !defined(__COREFOUNDATION_CFUNICODEPRECOMPOSITION__) +#define __COREFOUNDATION_CFUNICODEPRECOMPOSITION__ 1 + +#if KERNEL +#include "CFKernelTypes.h" +#else // KERNEL +#include "CFUniChar.h" +#endif /* KERNEL */ + +#if defined(__cplusplus) +extern "C" { +#endif + +// As you can see, this function cannot precompose Hangul Jamo +CF_EXPORT UTF32Char CFUniCharPrecomposeCharacter(UTF32Char base, UTF32Char combining); + +CF_EXPORT bool CFUniCharPrecompose(const UTF16Char *characters, uint32_t length, uint32_t *consumedLength, UTF16Char *precomposed, uint32_t maxLength, uint32_t *filledLength); + +#if defined(__cplusplus) +} +#endif + +#endif /* ! __COREFOUNDATION_CFUNICODEDECOMPOSITION__ */ + diff --git a/URL.subproj/CFURL.c b/URL.subproj/CFURL.c new file mode 100644 index 0000000..226023c --- /dev/null +++ b/URL.subproj/CFURL.c @@ -0,0 +1,4442 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFURL.c + Copyright 1998-2002, Apple, Inc. All rights reserved. + Responsibility: Becky Willrich +*/ + +#include +#include "CFPriv.h" +#include "CFCharacterSetPriv.h" +#include +#include "CFInternal.h" +#include "CFStringEncodingConverter.h" +#include "CFUtilities.h" +#include +#include +#include +#include +#if defined(__MACH__) || defined(__LINUX__) || defined(__FREEBSD__) +#include +#endif + + +static CFArrayRef HFSPathToURLComponents(CFStringRef path, CFAllocatorRef alloc, Boolean isDir); +static CFArrayRef WindowsPathToURLComponents(CFStringRef path, CFAllocatorRef alloc, Boolean isDir); +static CFStringRef WindowsPathToURLPath(CFStringRef path, CFAllocatorRef alloc, Boolean isDir); +static CFStringRef POSIXPathToURLPath(CFStringRef path, CFAllocatorRef alloc, Boolean isDirectory); +static CFStringRef HFSPathToURLPath(CFStringRef path, CFAllocatorRef alloc, Boolean isDir); +static CFStringRef URLPathToHFSPath(CFStringRef path, CFAllocatorRef allocator, CFStringEncoding encoding); +CFStringRef CFURLCreateStringWithFileSystemPath(CFAllocatorRef allocator, CFURLRef anURL, CFURLPathStyle fsType, Boolean resolveAgainstBase); +static Boolean _CFGetFSRefFromURL(CFAllocatorRef alloc, CFURLRef url, void *voidRef); +static CFURLRef _CFCreateURLFromFSRef(CFAllocatorRef alloc, const void *voidRef, Boolean isDirectory); +CFURLRef _CFURLCreateCurrentDirectoryURL(CFAllocatorRef allocator); + +#if defined(__MACH__) || defined(__LINUX__) || defined(__FREEBSD__) || defined(__WIN32__) +#if !defined(HAVE_CARBONCORE) +typedef void *FSRef; +#define noErr 0 +#define FSGetVolumeInfo(A, B, C, D, E, F, G) (-3296) +#define FSGetCatalogInfo(A, B, C, D, E, F) (-3296) +#define FSMakeFSRefUnicode(A, B, C, D, E) (-3296) +#define FSPathMakeRef(A, B, C) (-3296) +#define FSRefMakePath(A, B, C) (-3296) +#define FSpMakeFSRef(A, B) (-3296) +#define FSNewAlias(A, B, C) (-3296) +#define DisposeHandle(A) (-3296) +#else + +#include + +static void __CF_DisposeHandle_internal(Handle h) { + static void (*dyfunc)(Handle) = NULL; + if (NULL == dyfunc) { + void *image = __CFLoadCarbonCore(); + dyfunc = NSAddressOfSymbol(NSLookupSymbolInImage(image, "_DisposeHandle", NSLOOKUPSYMBOLINIMAGE_OPTION_BIND)); + } + dyfunc(h); +} + +static OSErr __CF_FSNewAlias_internal(const FSRef *A, const FSRef *B, AliasHandle *C) { + static OSErr (*dyfunc)(const FSRef *, const FSRef *, AliasHandle *) = NULL; + if (NULL == dyfunc) { + void *image = __CFLoadCarbonCore(); + dyfunc = NSAddressOfSymbol(NSLookupSymbolInImage(image, "_FSNewAlias", NSLOOKUPSYMBOLINIMAGE_OPTION_BIND)); + } + return dyfunc(A, B, C); +} + +static OSErr __CF_FSGetVolumeInfo_internal(FSVolumeRefNum A, ItemCount B, FSVolumeRefNum *C, FSVolumeInfoBitmap D, FSVolumeInfo *E, HFSUniStr255*F, FSRef *G) { + static OSErr (*dyfunc)(FSVolumeRefNum, ItemCount, FSVolumeRefNum *, FSVolumeInfoBitmap, FSVolumeInfo *, HFSUniStr255 *, FSRef *) = NULL; + if (NULL == dyfunc) { + void *image = __CFLoadCarbonCore(); + dyfunc = NSAddressOfSymbol(NSLookupSymbolInImage(image, "_FSGetVolumeInfo", NSLOOKUPSYMBOLINIMAGE_OPTION_BIND)); + } + return dyfunc(A, B, C, D, E, F, G); +} + +static OSErr __CF_FSGetCatalogInfo_internal(const FSRef *A, FSCatalogInfoBitmap B, FSCatalogInfo *C, HFSUniStr255 *D, FSSpec *E, FSRef *F) { + static OSErr (*dyfunc)(const FSRef *, FSCatalogInfoBitmap, FSCatalogInfo *, HFSUniStr255 *, FSSpec *, FSRef *) = NULL; + if (NULL == dyfunc) { + void *image = __CFLoadCarbonCore(); + dyfunc = NSAddressOfSymbol(NSLookupSymbolInImage(image, "_FSGetCatalogInfo", NSLOOKUPSYMBOLINIMAGE_OPTION_BIND)); + } + return dyfunc(A, B, C, D, E, F); +} + +static OSErr __CF_FSMakeFSRefUnicode_internal(const FSRef *A, UniCharCount B, const UniChar *C, TextEncoding D, FSRef *E) { + static OSErr (*dyfunc)(const FSRef *, UniChar, const UniChar *, TextEncoding, FSRef *) = NULL; + if (NULL == dyfunc) { + void *image = __CFLoadCarbonCore(); + dyfunc = NSAddressOfSymbol(NSLookupSymbolInImage(image, "_FSMakeFSRefUnicode", NSLOOKUPSYMBOLINIMAGE_OPTION_BIND)); + } + return dyfunc(A, B, C, D, E); +} + +static OSStatus __CF_FSPathMakeRef_internal(const uint8_t *A, FSRef *B, Boolean *C) { + static OSStatus (*dyfunc)(const uint8_t *, FSRef *, Boolean *) = NULL; + if (NULL == dyfunc) { + void *image = __CFLoadCarbonCore(); + dyfunc = NSAddressOfSymbol(NSLookupSymbolInImage(image, "_FSPathMakeRef", NSLOOKUPSYMBOLINIMAGE_OPTION_BIND)); + } + return dyfunc(A, B, C); +} + +static OSStatus __CF_FSRefMakePath_internal(const FSRef *A, uint8_t *B, UInt32 C) { + static OSStatus (*dyfunc)(const FSRef *, uint8_t *, UInt32) = NULL; + if (NULL == dyfunc) { + void *image = __CFLoadCarbonCore(); + dyfunc = NSAddressOfSymbol(NSLookupSymbolInImage(image, "_FSRefMakePath", NSLOOKUPSYMBOLINIMAGE_OPTION_BIND)); + } + return dyfunc(A, B, C); +} + +static OSErr __CF_FSpMakeFSRef_internal(const FSSpec *A, FSRef *B) { + static OSErr (*dyfunc)(const FSSpec *, FSRef *) = NULL; + if (NULL == dyfunc) { + void *image = __CFLoadCarbonCore(); + dyfunc = NSAddressOfSymbol(NSLookupSymbolInImage(image, "_FSpMakeFSRef", NSLOOKUPSYMBOLINIMAGE_OPTION_BIND)); + } + return dyfunc(A, B); +} + +#define FSGetVolumeInfo(A, B, C, D, E, F, G) __CF_FSGetVolumeInfo_internal(A, B, C, D, E, F, G) +#define FSGetCatalogInfo(A, B, C, D, E, F) __CF_FSGetCatalogInfo_internal(A, B, C, D, E, F) +#define FSMakeFSRefUnicode(A, B, C, D, E) __CF_FSMakeFSRefUnicode_internal(A, B, C, D, E) +#define FSPathMakeRef(A, B, C) __CF_FSPathMakeRef_internal(A, B, C) +#define FSRefMakePath(A, B, C) __CF_FSRefMakePath_internal(A, B, C) +#define FSpMakeFSRef(A, B) __CF_FSpMakeFSRef_internal(A, B) +#define FSNewAlias(A, B, C) __CF_FSNewAlias_internal(A, B, C) +#define DisposeHandle(A) __CF_DisposeHandle_internal(A) +#endif +#endif + +#if defined(__MACOS8__) + +#include +#include +#include + +static Boolean __CFMacOS8HasFSRefs() { + static Boolean sHasFSRefs = (Boolean) -1; + if ( sHasFSRefs == (Boolean) -1 ) { + long result; + sHasFSRefs = Gestalt( gestaltFSAttr, &result ) == noErr && + ( result & (1 << gestaltHasHFSPlusAPIs) ) != 0; + } + return sHasFSRefs; +} + +static Ptr __CFGropeAroundForMacOS8Symbol(ConstStr255Param name) { + static const char *libraries[] = {"\pCarbonLib", "\pInterfaceLib", "\pPrivateInterfaceLib"}; + int idx; + for (idx = 0; idx < sizeof(libraries) / sizeof(libraries[0]); idx++) { + CFragConnectionID connID; /* We get the connections ONLY if already available. */ + OSErr err = GetSharedLibrary(libraries[idx], kPowerPCCFragArch, kFindCFrag, &connID, NULL, NULL); + if (err == noErr) { + Ptr cfmfunc = NULL; + CFragSymbolClass symbolClass; + err = FindSymbol(connID, name, &cfmfunc, &symbolClass); + if (err == noErr && symbolClass == kTVectorCFragSymbol) { + return cfmfunc; + } + } + } + return NULL; +} + +static OSErr __CF_FSMakeFSRefUnicode_internal(const FSRef *A, UniCharCount B, const UniChar *C, TextEncoding D, FSRef *E) { + static OSErr (*cfmfunc)(const FSRef *, UniChar, const UniChar *, TextEncoding, FSRef *) = NULL; + static Boolean looked = false; + if (!looked && __CFMacOS8HasFSRefs()) { + cfmfunc = __CFGropeAroundForMacOS8Symbol("\pFSMakeFSRefUnicode"); + looked = true; + } + return (cfmfunc != NULL) ? cfmfunc(A, B, C, D, E) : cfragNoSymbolErr; +} + +static OSErr __CF_FSGetVolumeInfo_internal(FSVolumeRefNum A, ItemCount B, FSVolumeRefNum *C, FSVolumeInfoBitmap D, FSVolumeInfo *E, HFSUniStr255*F, FSRef *G) { + static OSErr (*cfmfunc)(FSVolumeRefNum, ItemCount, FSVolumeRefNum *, FSVolumeInfoBitmap, FSVolumeInfo *, HFSUniStr255 *, FSRef *) = NULL; + static Boolean looked = false; + if (!looked && __CFMacOS8HasFSRefs()) { + cfmfunc = __CFGropeAroundForMacOS8Symbol("\pFSGetVolumeInfo"); + looked = true; + } + return (cfmfunc != NULL) ? cfmfunc(A, B, C, D, E, F, G) : cfragNoSymbolErr; +} + +static OSErr __CF_FSGetCatalogInfo_internal(const FSRef *A, FSCatalogInfoBitmap B, FSCatalogInfo *C, HFSUniStr255 *D, FSSpec *E, FSRef *F) { + static OSErr (*cfmfunc)(const FSRef *, FSCatalogInfoBitmap, FSCatalogInfo *, HFSUniStr255 *, FSSpec *, FSRef *) = NULL; + static Boolean looked = false; + if (!looked && __CFMacOS8HasFSRefs()) { + cfmfunc = __CFGropeAroundForMacOS8Symbol("\pFSGetCatalogInfo"); + looked = true; + } + return (cfmfunc != NULL) ? cfmfunc(A, B, C, D, E, F) : cfragNoSymbolErr; +} + +static OSStatus __CF_FSRefMakePath_internal(const FSRef *A, uint8_t *B, UInt32 C) { + static OSStatus (*cfmfunc)(const FSRef *, uint8_t *buf, UInt32) = NULL; + static Boolean looked = false; + if (!looked && __CFMacOS8HasFSRefs()) { + cfmfunc = __CFGropeAroundForMacOS8Symbol("\pFSRefMakePath"); + looked = true; + } + return (cfmfunc != NULL) ? cfmfunc(A, B, C) : cfragNoSymbolErr; +} + +#define FSMakeFSRefUnicode(A, B, C, D, E) __CF_FSMakeFSRefUnicode_internal(A, B, C, D, E) +#define FSGetVolumeInfo(A, B, C, D, E, F, G) __CF_FSGetVolumeInfo_internal(A, B, C, D, E, F, G) +#define FSGetCatalogInfo(A, B, C, D, E, F) __CF_FSGetCatalogInfo_internal(A, B, C, D, E, F) +#define FSRefMakePath(A, B, C) __CF_FSRefMakePath_internal(A, B, C) +#endif + +#if defined(__MACH__) +#include +#include +#endif + +#if defined(__MACOS8__) +#include +#include +#endif + +#define DEBUG_URL_MEMORY_USAGE 0 + +/* The bit flags in myURL->_flags */ +#define HAS_SCHEME (0x0001) +#define HAS_USER (0x0002) +#define HAS_PASSWORD (0x0004) +#define HAS_HOST (0x0008) +#define HAS_PORT (0x0010) +#define HAS_PATH (0x0020) +#define HAS_PARAMETERS (0x0040) +#define HAS_QUERY (0x0080) +#define HAS_FRAGMENT (0x0100) +// Last free bit (0x200) in lower word goes here! +#define IS_IPV6_ENCODED (0x0400) +#define IS_OLD_UTF8_STYLE (0x0800) +#define IS_DIRECTORY (0x1000) +#define IS_PARSED (0x2000) +#define IS_ABSOLUTE (0x4000) +#define IS_DECOMPOSABLE (0x8000) + +#define PATH_TYPE_MASK (0x000F0000) +/* POSIX_AND_URL_PATHS_MATCH will only be true if the URL and POSIX paths are identical, character for character, except for the presence/absence of a trailing slash on directories */ +#define POSIX_AND_URL_PATHS_MATCH (0x00100000) +#define ORIGINAL_AND_URL_STRINGS_MATCH (0x00200000) + +/* If ORIGINAL_AND_URL_STRINGS_MATCH is false, these bits determine where they differ */ +// Scheme can actually never differ because if there were escaped characters prior to the colon, we'd interpret the string as a relative path +#define SCHEME_DIFFERS (0x00400000) +#define USER_DIFFERS (0x00800000) +#define PASSWORD_DIFFERS (0x01000000) +#define HOST_DIFFERS (0x02000000) +// Port can actually never differ because if there were a non-digit following a colon in the net location, we'd interpret the whole net location as the host +#define PORT_DIFFERS (0x04000000) +#define PATH_DIFFERS (0x08000000) +#define PARAMETERS_DIFFER (0x10000000) +#define QUERY_DIFFERS (0x20000000) +#define FRAGMENT_DIFfERS (0x40000000) + +// Number of bits to shift to get from HAS_FOO to FOO_DIFFERS flag +#define BIT_SHIFT_FROM_COMPONENT_TO_DIFFERS_FLAG (22) + +// Other useful defines +#define NET_LOCATION_MASK (HAS_HOST | HAS_USER | HAS_PASSWORD | HAS_PORT) +#define RESOURCE_SPECIFIER_MASK (HAS_PARAMETERS | HAS_QUERY | HAS_FRAGMENT) +#define FULL_URL_REPRESENTATION (0xF) + +/* URL_PATH_TYPE(anURL) will be one of the CFURLPathStyle constants, in which case string is a file system path, or will be FULL_URL_REPRESENTATION, in which case the string is the full URL string. One caveat - string always has a trailing path delimiter if the url is a directory URL. This must be stripped before returning file system representations! */ +#define URL_PATH_TYPE(url) (((url->_flags) & PATH_TYPE_MASK) >> 16) +#define PATH_DELIM_FOR_TYPE(fsType) ((fsType) == kCFURLHFSPathStyle ? ':' : (((fsType) == kCFURLWindowsPathStyle) ? '\\' : '/')) +#define PATH_DELIM_AS_STRING_FOR_TYPE(fsType) ((fsType) == kCFURLHFSPathStyle ? CFSTR(":") : (((fsType) == kCFURLWindowsPathStyle) ? CFSTR("\\") : CFSTR("/"))) + + +struct __CFURL { + CFRuntimeBase _cfBase; + UInt32 _flags; + CFStringRef _string; // Never NULL; the meaning of _string depends on URL_PATH_TYPE(myURL) (see above) + CFURLRef _base; // NULL for absolute URLs; if present, _base is guaranteed to itself be absolute. + CFRange *ranges; + void *_reserved; // Reserved for URLHandle's use. + CFStringEncoding _encoding; // The encoding to use when asked to remove percent escapes; this is never consulted if IS_OLD_UTF8_STYLE is set. + CFMutableStringRef _sanatizedString; // The fully compliant RFC string. This is only non-NULL if ORIGINAL_AND_URL_STRINGS_MATCH is false. This should never be mutated except when the sanatized string is first computed +}; + +static void _convertToURLRepresentation(struct __CFURL *url); +static CFURLRef _CFURLCopyAbsoluteFileURL(CFURLRef relativeURL); +static CFStringRef _resolveFileSystemPaths(CFStringRef relativePath, CFStringRef basePath, Boolean baseIsDir, CFURLPathStyle fsType, CFAllocatorRef alloc); +static void _parseComponents(CFAllocatorRef alloc, CFStringRef string, CFURLRef base, UInt32 *flags, CFRange **range); +static CFRange _rangeForComponent(UInt32 flags, CFRange *ranges, UInt32 compFlag); +static CFRange _netLocationRange(UInt32 flags, CFRange *ranges); +static UInt32 _firstResourceSpecifierFlag(UInt32 flags); +static void computeSanitizedString(CFURLRef url); +static CFStringRef correctedComponent(CFStringRef component, UInt32 compFlag, CFStringEncoding enc); +static CFMutableStringRef resolveAbsoluteURLString(CFAllocatorRef alloc, CFStringRef relString, UInt32 relFlags, CFRange *relRanges, CFStringRef baseString, UInt32 baseFlags, CFRange *baseRanges); +static CFStringRef _resolvedPath(UniChar *pathStr, UniChar *end, UniChar pathDelimiter, Boolean stripLeadingDotDots, Boolean stripTrailingDelimiter, CFAllocatorRef alloc); + + +CF_INLINE void _parseComponentsOfURL(CFURLRef url) { + _parseComponents(CFGetAllocator(url), url->_string, url->_base, &(((struct __CFURL *)url)->_flags), &(((struct __CFURL *)url)->ranges)); +} + +static Boolean _createOldUTF8StyleURLs = false; + +CF_INLINE Boolean createOldUTF8StyleURLs(void) { + if (_createOldUTF8StyleURLs) { + return true; + } + return !_CFExecutableLinkedOnOrAfter(CFSystemVersionJaguar); +} + +// Our backdoor in case removing the UTF8 constraint for URLs creates unexpected problems. See radar 2902530 -- REW +CF_EXPORT +void _CFURLCreateOnlyUTF8CompatibleURLs(Boolean createUTF8URLs) { + _createOldUTF8StyleURLs = createUTF8URLs; +} + +CF_INLINE Boolean scheme_valid(UniChar c) { + if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z')) return true; + if ('0' <= c && c <= '9') return true; // Added 12/1/2000; RFC 2396 extends schemes to include digits + if ((c == '.') || (c == '-') || (c == '+')) return true; + return false; +} + +// "Unreserved" as defined by RFC 2396 +CF_INLINE Boolean isUnreservedCharacter(UniChar ch) { + // unreserved characters are ASCII-7 alphanumerics, plus certain punctuation marks, all in the 0-127 range + if (ch > 0x7f) return false; + if (('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z')) return true; + if ('0' <= ch && ch <= '9') return true; + if (ch == '-' || ch == '_' || ch == '.' || ch == '!' || ch == '~' || ch == '*' || ch == '\'' || ch == '(' || ch == ')') return true; + return false; +} + +CF_INLINE Boolean isPathLegalCharacter(UniChar ch) { + // the unreserved chars plus a couple others + if (ch > 0x7f) return false; + if (('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z')) return true; + if ('0' <= ch && ch <= '9') return true; + if (ch == '-' || ch == '_' || ch == '.' || ch == '!' || ch == '~' || ch == '*' || ch == '\'' || ch == '(' || ch == ')') return true; + if (ch == '/' || ch == ':' || ch == '@' || ch == '&' || ch == '=' || ch == '+' || ch == '$' || ch == ',') return true; + return false; +} + +CF_INLINE Boolean isURLLegalCharacter(UniChar ch) { + if (ch > 0x7f) return false; + if (('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z')) return true; + if ('0' <= ch && ch <= '9') return true; + if (ch == '-' || ch == '_' || ch == '.' || ch == '!' || ch == '~' || ch == '*' || ch == '\'' || ch == '(' || ch == ')') return true; + if (ch == ';' || ch == '/' || ch == '?' || ch == ':' || ch == '@' || ch == '&' || ch == '=' || ch == '+' || ch == '$' || ch == ',') return true; + return false; +} + +CF_INLINE Boolean isHexDigit(UniChar ch) { + return ((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F')); +} +// Returns false if ch1 or ch2 isn't properly formatted +CF_INLINE Boolean _translateBytes(UniChar ch1, UniChar ch2, uint8_t *result) { + *result = 0; + if (ch1 >= '0' && ch1 <= '9') *result += (ch1 - '0'); + else if (ch1 >= 'a' && ch1 <= 'f') *result += 10 + ch1 - 'a'; + else if (ch1 >= 'A' && ch1 <= 'F') *result += 10 + ch1 - 'A'; + else return false; + + *result = (*result) << 4; + if (ch2 >= '0' && ch2 <= '9') *result += (ch2 - '0'); + else if (ch2 >= 'a' && ch2 <= 'f') *result += 10 + ch2 - 'a'; + else if (ch2 >= 'A' && ch2 <= 'F') *result += 10 + ch2 - 'A'; + else return false; + + return true; +} + +CF_INLINE Boolean _haveTestedOriginalString(CFURLRef url) { + return ((url->_flags & ORIGINAL_AND_URL_STRINGS_MATCH) != 0) || (url->_sanatizedString != NULL); +} + +typedef CFStringRef (*StringTransformation)(CFAllocatorRef, CFStringRef, CFIndex); +static CFArrayRef copyStringArrayWithTransformation(CFArrayRef array, StringTransformation transformation) { + CFAllocatorRef alloc = CFGetAllocator(array); + CFMutableArrayRef mArray = NULL; + CFIndex i, c = CFArrayGetCount(array); + for (i = 0; i < c; i ++) { + CFStringRef origComp = CFArrayGetValueAtIndex(array, i); + CFStringRef unescapedComp = transformation(alloc, origComp, i); + if (!unescapedComp) { + break; + } + if (unescapedComp != origComp) { + if (!mArray) { + mArray = CFArrayCreateMutableCopy(alloc, c, array); + } + CFArraySetValueAtIndex(mArray, i, unescapedComp); + } + CFRelease(unescapedComp); + } + if (i != c) { + if (mArray) CFRelease(mArray); + return NULL; + } else if (mArray) { + return mArray; + } else { + CFRetain(array); + return array; + } +} + +// Returns NULL if str cannot be converted for whatever reason, str if str contains no characters in need of escaping, or a newly-created string with the appropriate % escape codes in place. Caller must always release the returned string. +CF_INLINE CFStringRef _replacePathIllegalCharacters(CFStringRef str, CFAllocatorRef alloc, Boolean preserveSlashes) { + if (preserveSlashes) { + return CFURLCreateStringByAddingPercentEscapes(alloc, str, NULL, CFSTR(";?"), kCFStringEncodingUTF8); + } else { + return CFURLCreateStringByAddingPercentEscapes(alloc, str, NULL, CFSTR(";?/"), kCFStringEncodingUTF8); + } +} + +static CFStringRef escapePathComponent(CFAllocatorRef alloc, CFStringRef origComponent, CFIndex componentIndex) { + return CFURLCreateStringByAddingPercentEscapes(alloc, origComponent, NULL, CFSTR(";?/"), kCFStringEncodingUTF8); +} + +static CFStringRef escapeWindowsPathComponent(CFAllocatorRef alloc, CFStringRef origComponent, CFIndex componentIndex) { + if (CFStringGetLength(origComponent) == 2 && CFStringGetCharacterAtIndex(origComponent, 1) == '|') { + // Don't corrupt a drive letter component + CFRetain(origComponent); + return origComponent; + } else { + return CFURLCreateStringByAddingPercentEscapes(alloc, origComponent, NULL, CFSTR(";?/"), kCFStringEncodingUTF8); + } +} + +// We have 2 UniChars of a surrogate; we must convert to the correct percent-encoded UTF8 string and append to str. Added so that file system URLs can always be converted from POSIX to full URL representation. -- REW, 8/20/2001 +static Boolean _hackToConvertSurrogates(UniChar highChar, UniChar lowChar, CFMutableStringRef str) { + UniChar surrogate[2]; + uint8_t bytes[6]; // Aki sez it should never take more than 6 bytes + UInt32 len; + uint8_t *currByte; + surrogate[0] = highChar; + surrogate[1] = lowChar; + if (CFStringEncodingUnicodeToBytes(kCFStringEncodingUTF8, 0, surrogate, 2, NULL, bytes, 6, &len) != kCFStringEncodingConversionSuccess) { + return false; + } + for (currByte = bytes; currByte < bytes + len; currByte ++) { + UniChar escapeSequence[3] = {'%', '\0', '\0'}; + unsigned char high, low; + high = ((*currByte) & 0xf0) >> 4; + low = (*currByte) & 0x0f; + escapeSequence[1] = (high < 10) ? '0' + high : 'A' + high - 10; + escapeSequence[2] = (low < 10) ? '0' + low : 'A' + low - 10; + CFStringAppendCharacters(str, escapeSequence, 3); + } + return true; +} + +static Boolean _appendPercentEscapesForCharacter(UniChar ch, CFStringEncoding encoding, CFMutableStringRef str) { + uint8_t bytes[6]; // 6 bytes is the maximum a single character could require in UTF8 (most common case); other encodings could require more + uint8_t *bytePtr = bytes, *currByte; + UInt32 byteLength; + CFAllocatorRef alloc = NULL; + if (CFStringEncodingUnicodeToBytes(encoding, 0, &ch, 1, NULL, bytePtr, 6, &byteLength) != kCFStringEncodingConversionSuccess) { + byteLength = CFStringEncodingByteLengthForCharacters(encoding, 0, &ch, 1); + if (byteLength <= 6) { + // The encoding cannot accomodate the character + return false; + } + alloc = CFGetAllocator(str); + bytePtr = CFAllocatorAllocate(alloc, byteLength, 0); + if (!bytePtr || CFStringEncodingUnicodeToBytes(encoding, 0, &ch, 1, NULL, bytePtr, byteLength, &byteLength) != kCFStringEncodingConversionSuccess) { + if (bytePtr) CFAllocatorDeallocate(alloc, bytePtr); + return false; + } + } + for (currByte = bytePtr; currByte < bytePtr + byteLength; currByte ++) { + UniChar escapeSequence[3] = {'%', '\0', '\0'}; + unsigned char high, low; + high = ((*currByte) & 0xf0) >> 4; + low = (*currByte) & 0x0f; + escapeSequence[1] = (high < 10) ? '0' + high : 'A' + high - 10; + escapeSequence[2] = (low < 10) ? '0' + low : 'A' + low - 10; + CFStringAppendCharacters(str, escapeSequence, 3); + } + if (bytePtr != bytes) { + CFAllocatorDeallocate(alloc, bytePtr); + } + return true; +} + +// Uses UTF-8 to translate all percent escape sequences; returns NULL if it encounters a format failure. May return the original string. +CFStringRef CFURLCreateStringByReplacingPercentEscapes(CFAllocatorRef alloc, CFStringRef originalString, CFStringRef charactersToLeaveEscaped) { + CFMutableStringRef newStr = NULL; + CFIndex length; + CFIndex mark = 0; + CFRange percentRange, searchRange; + CFStringRef escapedStr = NULL; + CFMutableStringRef strForEscapedChar = NULL; + UniChar escapedChar; + Boolean escapeAll = (charactersToLeaveEscaped && CFStringGetLength(charactersToLeaveEscaped) == 0); + Boolean failed = false; + + if (!originalString) return NULL; + + if (charactersToLeaveEscaped == NULL) { + return CFStringCreateCopy(alloc, originalString); + } + + length = CFStringGetLength(originalString); + searchRange = CFRangeMake(0, length); + + while (!failed && CFStringFindWithOptions(originalString, CFSTR("%"), searchRange, 0, &percentRange)) { + uint8_t bytes[4]; // Single UTF-8 character could require up to 4 bytes. + uint8_t numBytesExpected; + UniChar ch1, ch2; + + escapedStr = NULL; + // Make sure we have at least 2 more characters + if (length - percentRange.location < 3) { failed = true; break; } + + // if we don't have at least 2 more characters, we can't interpret the percent escape code, + // so we assume the percent character is legit, and let it pass into the string + ch1 = CFStringGetCharacterAtIndex(originalString, percentRange.location+1); + ch2 = CFStringGetCharacterAtIndex(originalString, percentRange.location+2); + if (!_translateBytes(ch1, ch2, bytes)) { failed = true; break; } + if (!(bytes[0] & 0x80)) { + numBytesExpected = 1; + } else if (!(bytes[0] & 0x20)) { + numBytesExpected = 2; + } else if (!(bytes[0] & 0x10)) { + numBytesExpected = 3; + } else { + numBytesExpected = 4; + } + if (numBytesExpected == 1) { + // one byte sequence (most common case); handle this specially + escapedChar = bytes[0]; + if (!strForEscapedChar) { + strForEscapedChar = CFStringCreateMutableWithExternalCharactersNoCopy(alloc, &escapedChar, 1, 1, kCFAllocatorNull); + } + escapedStr = strForEscapedChar; + } else { + CFIndex j; + // Make sure up front that we have enough characters + if (length < percentRange.location + numBytesExpected * 3) { failed = true; break; } + for (j = 1; j < numBytesExpected; j ++) { + if (CFStringGetCharacterAtIndex(originalString, percentRange.location + 3*j) != '%') { failed = true; break; } + ch1 = CFStringGetCharacterAtIndex(originalString, percentRange.location + 3*j + 1); + ch2 = CFStringGetCharacterAtIndex(originalString, percentRange.location + 3*j + 2); + if (!_translateBytes(ch1, ch2, bytes+j)) { failed = true; break; } + } + + // !!! We should do the low-level bit-twiddling ourselves; this is expensive! REW, 6/10/99 + escapedStr = CFStringCreateWithBytes(alloc, bytes, numBytesExpected, kCFStringEncodingUTF8, false); + if (!escapedStr) { + failed = true; + } else if (CFStringGetLength(escapedStr) == 0 && numBytesExpected == 3 && bytes[0] == 0xef && bytes[1] == 0xbb && bytes[2] == 0xbf) { + // Somehow, the UCS-2 BOM got translated in to a UTF8 string + escapedChar = 0xfeff; + if (!strForEscapedChar) { + strForEscapedChar = CFStringCreateMutableWithExternalCharactersNoCopy(alloc, &escapedChar, 1, 1, kCFAllocatorNull); + } + CFRelease(escapedStr); + escapedStr = strForEscapedChar; + } + if (failed) break; + } + + // The new character is in escapedChar; the number of percent escapes it took is in numBytesExpected. + searchRange.location = percentRange.location + 3 * numBytesExpected; + searchRange.length = length - searchRange.location; + + if (!escapeAll) { + if (CFStringFind(charactersToLeaveEscaped, escapedStr, 0).location != kCFNotFound) { + if (escapedStr != strForEscapedChar) { + CFRelease(escapedStr); + escapedStr = NULL; + } + continue; + } + } + + if (!newStr) { + newStr = CFStringCreateMutable(alloc, length); + } + if (percentRange.location - mark > 0) { + // The creation of this temporary string is unfortunate. + CFStringRef substring = CFStringCreateWithSubstring(alloc, originalString, CFRangeMake(mark, percentRange.location - mark)); + CFStringAppend(newStr, substring); + CFRelease(substring); + } + CFStringAppend(newStr, escapedStr); + if (escapedStr != strForEscapedChar) { + CFRelease(escapedStr); + escapedStr = NULL; + } + mark = searchRange.location;// We need mark to be the index of the first character beyond the escape sequence + } + + if (escapedStr && escapedStr != strForEscapedChar) CFRelease(escapedStr); + if (strForEscapedChar) CFRelease(strForEscapedChar); + if (failed) { + if (newStr) CFRelease(newStr); + return NULL; + } else if (newStr) { + if (mark < length) { + // Need to cat on the remainder of the string + CFStringRef substring = CFStringCreateWithSubstring(alloc, originalString, CFRangeMake(mark, length - mark)); + CFStringAppend(newStr, substring); + CFRelease(substring); + } + return newStr; + } else { + return CFStringCreateCopy(alloc, originalString); + } +} + +CF_EXPORT +CFStringRef CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFAllocatorRef alloc, CFStringRef originalString, CFStringRef charactersToLeaveEscaped, CFStringEncoding enc) { + if (enc == kCFStringEncodingUTF8) { + return CFURLCreateStringByReplacingPercentEscapes(alloc, originalString, charactersToLeaveEscaped); + } else { + CFMutableStringRef newStr = NULL; + CFMutableStringRef escapedStr = NULL; + CFIndex length; + CFIndex mark = 0; + CFRange percentRange, searchRange; + Boolean escapeAll = (charactersToLeaveEscaped && CFStringGetLength(charactersToLeaveEscaped) == 0); + Boolean failed = false; + uint8_t byteBuffer[8]; + uint8_t *bytes = byteBuffer; + int capacityOfBytes = 8; + + if (!originalString) return NULL; + + if (charactersToLeaveEscaped == NULL) { + return CFStringCreateCopy(alloc, originalString); + } + + length = CFStringGetLength(originalString); + searchRange = CFRangeMake(0, length); + + while (!failed && CFStringFindWithOptions(originalString, CFSTR("%"), searchRange, 0, &percentRange)) { + UniChar ch1, ch2; + CFIndex percentLoc = percentRange.location; + CFStringRef convertedString; + int numBytesUsed = 0; + do { + // Make sure we have at least 2 more characters + if (length - percentLoc < 3) { failed = true; break; } + + if (numBytesUsed == capacityOfBytes) { + if (bytes == byteBuffer) { + bytes = CFAllocatorAllocate(alloc, 16 * sizeof(uint8_t), 0); + memmove(bytes, byteBuffer, capacityOfBytes); + capacityOfBytes = 16; + } else { + capacityOfBytes = 2*capacityOfBytes; + bytes = CFAllocatorReallocate(alloc, bytes, capacityOfBytes * sizeof(uint8_t), 0); + } + } + percentLoc ++; + ch1 = CFStringGetCharacterAtIndex(originalString, percentLoc); + percentLoc ++; + ch2 = CFStringGetCharacterAtIndex(originalString, percentLoc); + percentLoc ++; + if (!_translateBytes(ch1, ch2, bytes + numBytesUsed)) { failed = true; break; } + numBytesUsed ++; + } while (CFStringGetCharacterAtIndex(originalString, percentLoc) == '%'); + searchRange.location = percentLoc; + searchRange.length = length - searchRange.location; + + if (failed) break; + convertedString = CFStringCreateWithBytes(alloc, bytes, numBytesUsed, enc, false); + if (!convertedString) { + failed = true; + break; + } + + if (!newStr) { + newStr = CFStringCreateMutable(alloc, length); + } + if (percentRange.location - mark > 0) { + // The creation of this temporary string is unfortunate. + CFStringRef substring = CFStringCreateWithSubstring(alloc, originalString, CFRangeMake(mark, percentRange.location - mark)); + CFStringAppend(newStr, substring); + CFRelease(substring); + } + + if (escapeAll) { + CFStringAppend(newStr, convertedString); + CFRelease(convertedString); + } else { + CFIndex i, c = CFStringGetLength(convertedString); + if (!escapedStr) { + escapedStr = CFStringCreateMutableWithExternalCharactersNoCopy(alloc, &ch1, 1, 1, kCFAllocatorNull); + } + for (i = 0; i < c; i ++) { + ch1 = CFStringGetCharacterAtIndex(convertedString, i); + if (CFStringFind(charactersToLeaveEscaped, escapedStr, 0).location == kCFNotFound) { + CFStringAppendCharacters(newStr, &ch1, 1); + } else { + // Must regenerate the escape sequence for this character; because we started with percent escapes, we know this call cannot fail + _appendPercentEscapesForCharacter(ch1, enc, newStr); + } + } + } + mark = searchRange.location;// We need mark to be the index of the first character beyond the escape sequence + } + + if (escapedStr) CFRelease(escapedStr); + if (bytes != byteBuffer) CFAllocatorDeallocate(alloc, bytes); + if (failed) { + if (newStr) CFRelease(newStr); + return NULL; + } else if (newStr) { + if (mark < length) { + // Need to cat on the remainder of the string + CFStringRef substring = CFStringCreateWithSubstring(alloc, originalString, CFRangeMake(mark, length - mark)); + CFStringAppend(newStr, substring); + CFRelease(substring); + } + return newStr; + } else { + return CFStringCreateCopy(alloc, originalString); + } + } +} + + +static CFStringRef _addPercentEscapesToString(CFAllocatorRef allocator, CFStringRef originalString, Boolean (*shouldReplaceChar)(UniChar, void*), CFIndex (*handlePercentChar)(CFIndex, CFStringRef, CFStringRef *, void *), CFStringEncoding encoding, void *context) { + CFMutableStringRef newString = NULL; + CFIndex idx, length; + CFStringInlineBuffer buf; + + if (!originalString) return NULL; + length = CFStringGetLength(originalString); + if (length == 0) return CFStringCreateCopy(allocator, originalString); + CFStringInitInlineBuffer(originalString, &buf, CFRangeMake(0, length)); + + for (idx = 0; idx < length; idx ++) { + UniChar ch = CFStringGetCharacterFromInlineBuffer(&buf, idx); + Boolean shouldReplace = shouldReplaceChar(ch, context); + if (shouldReplace) { + // Perform the replacement + if (!newString) { + newString = CFStringCreateMutableCopy(CFGetAllocator(originalString), 0, originalString); + CFStringDelete(newString, CFRangeMake(idx, length-idx)); + } + if (!_appendPercentEscapesForCharacter(ch, encoding, newString)) { +//#warning FIXME - once CFString supports finding glyph boundaries walk by glyph boundaries instead of by unichars + if (encoding == kCFStringEncodingUTF8 && CFCharacterSetIsSurrogateHighCharacter(ch) && idx + 1 < length && CFCharacterSetIsSurrogateLowCharacter(CFStringGetCharacterFromInlineBuffer(&buf, idx+1))) { + // Hack to guarantee we always safely convert file URLs between POSIX & full URL representation + if (_hackToConvertSurrogates(ch, CFStringGetCharacterFromInlineBuffer(&buf, idx+1), newString)) { + idx ++; // We consumed 2 characters, not 1 + } else { + break; + } + } else { + break; + } + } + } else if (ch == '%' && handlePercentChar) { + CFStringRef replacementString = NULL; + CFIndex newIndex = handlePercentChar(idx, originalString, &replacementString, context); + if (newIndex < 0) { + break; + } else if (replacementString) { + if (!newString) { + newString = CFStringCreateMutableCopy(CFGetAllocator(originalString), 0, originalString); + CFStringDelete(newString, CFRangeMake(idx, length-idx)); + } + CFStringAppend(newString, replacementString); + CFRelease(replacementString); + } + if (newIndex == idx) { + if (newString) { + CFStringAppendCharacters(newString, &ch, 1); + } + } else { + if (!replacementString && newString) { + CFIndex tmpIndex; + for (tmpIndex = idx; tmpIndex < newIndex; tmpIndex ++) { + ch = CFStringGetCharacterAtIndex(originalString, idx); + CFStringAppendCharacters(newString, &ch, 1); + } + } + idx = newIndex - 1; + } + } else if (newString) { + CFStringAppendCharacters(newString, &ch, 1); + } + } + if (idx < length) { + // Ran in to an encoding failure + if (newString) CFRelease(newString); + return NULL; + } else if (newString) { + return newString; + } else { + return CFStringCreateCopy(CFGetAllocator(originalString), originalString); + } +} + + +static Boolean _stringContainsCharacter(CFStringRef string, UniChar ch) { + CFIndex i, c = CFStringGetLength(string); + CFStringInlineBuffer buf; + CFStringInitInlineBuffer(string, &buf, CFRangeMake(0, c)); + for (i = 0; i < c; i ++) if (__CFStringGetCharacterFromInlineBufferQuick(&buf, i) == ch) return true; + return false; +} + +static Boolean _shouldPercentReplaceChar(UniChar ch, void *context) { + CFStringRef unescape = ((CFStringRef *)context)[0]; + CFStringRef escape = ((CFStringRef *)context)[1]; + Boolean shouldReplace = (isURLLegalCharacter(ch) == false); + if (shouldReplace) { + if (unescape && _stringContainsCharacter(unescape, ch)) { + shouldReplace = false; + } + } else if (escape && _stringContainsCharacter(escape, ch)) { + shouldReplace = true; + } + return shouldReplace; +} + +CF_EXPORT CFStringRef CFURLCreateStringByAddingPercentEscapes(CFAllocatorRef allocator, CFStringRef originalString, CFStringRef charactersToLeaveUnescaped, CFStringRef legalURLCharactersToBeEscaped, CFStringEncoding encoding) { + CFStringRef strings[2]; + strings[0] = charactersToLeaveUnescaped; + strings[1] = legalURLCharactersToBeEscaped; + return _addPercentEscapesToString(allocator, originalString, _shouldPercentReplaceChar, NULL, encoding, strings); +} + +static Boolean __CFURLEqual(CFTypeRef cf1, CFTypeRef cf2) { + CFURLRef url1 = cf1; + CFURLRef url2 = cf2; + UInt32 pathType1, pathType2; + + __CFGenericValidateType(cf1, CFURLGetTypeID()); + __CFGenericValidateType(cf2, CFURLGetTypeID()); + + if (url1 == url2) return true; + if ((url1->_flags & IS_PARSED) && (url2->_flags & IS_PARSED) && (url1->_flags & IS_DIRECTORY) != (url2->_flags & IS_DIRECTORY)) return false; + if (url1->_base) { + if (!url2->_base) return false; + if (!CFEqual(url1->_base, url2->_base)) return false; + } else if (url2->_base) { + return false; + } + + pathType1 = URL_PATH_TYPE(url1); + pathType2 = URL_PATH_TYPE(url2); + if (pathType1 == pathType2) { + if (pathType1 != FULL_URL_REPRESENTATION) { + return CFEqual(url1->_string, url2->_string); + } else { + // Do not compare the original strings; compare the sanatized strings. + return CFEqual(CFURLGetString(url1), CFURLGetString(url2)); + } + } else { + // Try hard to avoid the expensive conversion from a file system representation to the canonical form + CFStringRef scheme1 = CFURLCopyScheme(url1); + CFStringRef scheme2 = CFURLCopyScheme(url2); + Boolean eq; + if (scheme1 && scheme2) { + eq = CFEqual(scheme1, scheme2); + CFRelease(scheme1); + CFRelease(scheme2); + } else if (!scheme1 && !scheme2) { + eq = TRUE; + } else { + eq = FALSE; + if (scheme1) CFRelease(scheme1); + else CFRelease(scheme2); + } + if (!eq) return false; + + if (pathType1 == FULL_URL_REPRESENTATION) { + if (!(url1->_flags & IS_PARSED)) { + _parseComponentsOfURL(url1); + } + if (url1->_flags & (HAS_USER | HAS_PORT | HAS_PASSWORD | HAS_QUERY | HAS_PARAMETERS | HAS_FRAGMENT )) { + return false; + } + } + + if (pathType2 == FULL_URL_REPRESENTATION) { + if (!(url2->_flags & IS_PARSED)) { + _parseComponentsOfURL(url2); + } + if (url2->_flags & (HAS_USER | HAS_PORT | HAS_PASSWORD | HAS_QUERY | HAS_PARAMETERS | HAS_FRAGMENT )) { + return false; + } + } + + // No help for it; we now must convert to the canonical representation and compare. + return CFEqual(CFURLGetString(url1), CFURLGetString(url2)); + } +} + +static UInt32 __CFURLHash(CFTypeRef cf) { + /* This is tricky, because we do not want the hash value to change as a file system URL is changed to its canonical representation, nor do we wish to force the conversion to the canonical representation. We choose instead to take the last path component (or "/" in the unlikely case that the path is empty), then hash on that. */ + CFURLRef url = cf; + UInt32 result; + if (CFURLCanBeDecomposed(url)) { + CFStringRef lastComp = CFURLCopyLastPathComponent(url); + if (lastComp) { + result = CFHash(lastComp); + CFRelease(lastComp); + } else { + result = 0; + } + } else { + result = CFHash(CFURLGetString(url)); + } + return result; +} + +static CFStringRef __CFURLCopyFormattingDescription(CFTypeRef cf, CFDictionaryRef formatOptions) { + CFURLRef url = cf; + __CFGenericValidateType(cf, CFURLGetTypeID()); + if (!url->_base) { + CFRetain(url->_string); + return url->_string; + } else { + // Do not dereference url->_base; it may be an ObjC object + return CFStringCreateWithFormat(CFGetAllocator(url), NULL, CFSTR("%@ -- %@"), url->_string, url->_base); + } +} + + +static CFStringRef __CFURLCopyDescription(CFTypeRef cf) { + CFURLRef url = (CFURLRef)cf; + CFStringRef result; + CFAllocatorRef alloc = CFGetAllocator(url); + if (url->_base) { + CFStringRef baseString = CFCopyDescription(url->_base); + result = CFStringCreateWithFormat(alloc, NULL, CFSTR("{type = %d, string = %@,\n\tbase = %@}"), cf, alloc, URL_PATH_TYPE(url), url->_string, baseString); + CFRelease(baseString); + } else { + result = CFStringCreateWithFormat(alloc, NULL, CFSTR("{type = %d, string = %@, base = (null)}"), cf, alloc, URL_PATH_TYPE(url), url->_string); + } + return result; +} + +#if DEBUG_URL_MEMORY_USAGE +static CFAllocatorRef URLAllocator = NULL; +static UInt32 numFileURLsCreated = 0; +static UInt32 numFileURLsConverted = 0; +static UInt32 numFileURLsDealloced = 0; +static UInt32 numURLs = 0; +static UInt32 numDealloced = 0; +void __CFURLDumpMemRecord(void) { + CFStringRef str = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d URLs created; %d destroyed\n%d file URLs created; %d converted; %d destroyed\n"), numURLs, numDealloced, numFileURLsCreated, numFileURLsConverted, numFileURLsDealloced); + CFShow(str); + CFRelease(str); + if (URLAllocator) CFCountingAllocatorPrintPointers(URLAllocator); +} +#endif + +static void __CFURLDeallocate(CFTypeRef cf) { + CFURLRef url = cf; + CFAllocatorRef alloc; + __CFGenericValidateType(cf, CFURLGetTypeID()); + alloc = CFGetAllocator(url); +#if DEBUG_URL_MEMORY_USAGE + numDealloced ++; + if (URL_PATH_TYPE(url) != FULL_URL_REPRESENTATION) { + numFileURLsDealloced ++; + } +#endif + CFRelease(url->_string); + if (url->_base) CFRelease(url->_base); + if (url->ranges) CFAllocatorDeallocate(alloc, url->ranges); + if (url->_sanatizedString) CFRelease(url->_sanatizedString); +} + +static CFTypeID __kCFURLTypeID = _kCFRuntimeNotATypeID; + +static const CFRuntimeClass __CFURLClass = { + 0, + "CFURL", + NULL, // init + NULL, // copy + __CFURLDeallocate, + __CFURLEqual, + __CFURLHash, + __CFURLCopyFormattingDescription, + __CFURLCopyDescription +}; + +CONST_STRING_DECL(kCFURLFileScheme, "file") +CONST_STRING_DECL(kCFURLLocalhost, "localhost") + +__private_extern__ void __CFURLInitialize(void) { + __kCFURLTypeID = _CFRuntimeRegisterClass(&__CFURLClass); +} + +/* Toll-free bridging support; get the true CFURL from an NSURL */ +CF_INLINE CFURLRef _CFURLFromNSURL(CFURLRef url) { + CF_OBJC_FUNCDISPATCH0(__kCFURLTypeID, CFURLRef, url, "_cfurl"); + return url; +} + +CFTypeID CFURLGetTypeID(void) { + return __kCFURLTypeID; +} + +__private_extern__ void CFShowURL(CFURLRef url) { + if (!url) { + printf("(null)\n"); + return; + } + printf("{", (unsigned)url); + if (CF_IS_OBJC(__kCFURLTypeID, url)) { + printf("ObjC bridged object}\n"); + return; + } + printf("\n\tPath type: "); + switch (URL_PATH_TYPE(url)) { + case kCFURLPOSIXPathStyle: + printf("POSIX"); + break; + case kCFURLHFSPathStyle: + printf("HFS"); + break; + case kCFURLWindowsPathStyle: + printf("NTFS"); + break; + case FULL_URL_REPRESENTATION: + printf("Native URL"); + break; + default: + printf("UNRECOGNIZED PATH TYPE %d", (char)URL_PATH_TYPE(url)); + } + printf("\n\tRelative string: "); + CFShow(url->_string); + printf("\tBase URL: "); + if (url->_base) { + printf("<0x%x> ", (unsigned)url->_base); + CFShow(url->_base); + } else { + printf("(null)\n"); + } + printf("\tFlags: 0x%x\n}\n", (unsigned)url->_flags); +} + + +/***************************************************/ +/* URL creation and String/Data creation from URLS */ +/***************************************************/ +static void constructBuffers(CFAllocatorRef alloc, CFStringRef string, const unsigned char **cstring, const UniChar **ustring, Boolean *useCString, Boolean *freeCharacters) { + CFIndex neededLength; + CFIndex length; + CFRange rg; + + *cstring = CFStringGetCStringPtr(string, kCFStringEncodingISOLatin1); + if (*cstring) { + *ustring = NULL; + *useCString = true; + *freeCharacters = false; + return; + } + + *ustring = CFStringGetCharactersPtr(string); + if (*ustring) { + *useCString = false; + *freeCharacters = false; + return; + } + + *freeCharacters = true; + length = CFStringGetLength(string); + rg = CFRangeMake(0, length); + CFStringGetBytes(string, rg, kCFStringEncodingISOLatin1, 0, false, NULL, INT_MAX, &neededLength); + if (neededLength == length) { + char *buf = CFAllocatorAllocate(alloc, length, 0); + CFStringGetBytes(string, rg, kCFStringEncodingISOLatin1, 0, false, buf, length, NULL); + *cstring = buf; + *useCString = true; + } else { + UniChar *buf = CFAllocatorAllocate(alloc, length * sizeof(UniChar), 0); + CFStringGetCharacters(string, rg, buf); + *useCString = false; + *ustring = buf; + } +} + +#define STRING_CHAR(x) (useCString ? cstring[(x)] : ustring[(x)]) +static void _parseComponents(CFAllocatorRef alloc, CFStringRef string, CFURLRef baseURL, UInt32 *theFlags, CFRange **range) { + CFRange ranges[9]; + /* index gives the URL part involved; to calculate the correct range index, use the number of the bit of the equivalent flag (i.e. the host flag is HAS_HOST, which is 0x8. so the range index for the host is 3.) Note that this is true in this function ONLY, since the ranges stored in (*range) are actually packed, skipping those URL components that don't exist. This is why the indices are hard-coded in this function. */ + + CFIndex idx, base_idx = 0; + CFIndex string_length; + UInt32 flags = (IS_PARSED | *theFlags); + Boolean useCString, freeCharacters, isCompliant; + uint8_t numRanges = 0; + const unsigned char *cstring = NULL; + const UniChar *ustring = NULL; + + string_length = CFStringGetLength(string); + constructBuffers(alloc, string, &cstring, &ustring, &useCString, &freeCharacters); + + // Algorithm is as described in RFC 1808 + // 1: parse the fragment; remainder after left-most "#" is fragment + for (idx = base_idx; idx < string_length; idx++) { + if ('#' == STRING_CHAR(idx)) { + flags |= HAS_FRAGMENT; + ranges[8].location = idx + 1; + ranges[8].length = string_length - (idx + 1); + numRanges ++; + string_length = idx; // remove fragment from parse string + break; + } + } + // 2: parse the scheme + for (idx = base_idx; idx < string_length; idx++) { + UniChar ch = STRING_CHAR(idx); + if (':' == ch) { + flags |= HAS_SCHEME; + flags |= IS_ABSOLUTE; + ranges[0].location = base_idx; + ranges[0].length = idx; + numRanges ++; + base_idx = idx + 1; + break; + } else if (!scheme_valid(ch)) { + break; // invalid scheme character -- no scheme + } + } + + // Make sure we have an RFC-1808 compliant URL - that's either something without a scheme, or scheme:/(stuff) or scheme://(stuff) + if (!(flags & HAS_SCHEME)) { + isCompliant = true; + } else if (!(base_idx < string_length)) { + isCompliant = false; + } else if (STRING_CHAR(base_idx) != '/') { + isCompliant = false; + } else { + isCompliant = true; + } + + if (!isCompliant) { + // Clear the fragment flag if it's been set + if (flags & HAS_FRAGMENT) { + flags &= (~HAS_FRAGMENT); + string_length = CFStringGetLength(string); + } + (*theFlags) = flags; + (*range) = CFAllocatorAllocate(alloc, sizeof(CFRange), 0); + (*range)->location = ranges[0].location; + (*range)->length = ranges[0].length; + if (freeCharacters) { + CFAllocatorDeallocate(alloc, useCString ? (void *)cstring : (void *)ustring); + } + return; + } + + // URL is 1808-compliant + flags |= IS_DECOMPOSABLE; + + // 3: parse the network location and login + if (2 <= (string_length - base_idx) && '/' == STRING_CHAR(base_idx) && '/' == STRING_CHAR(base_idx+1)) { + CFIndex base = 2 + base_idx, extent; + Boolean insideIPV6Host = false; + for (idx = base; idx < string_length; idx++) { + if ('/' == STRING_CHAR(idx) || '?' == STRING_CHAR(idx)) break; + } + extent = idx; + + // net_loc parts extend from base to extent (but not including), which might be to end of string + // net location is ":@:" + if (extent != base) { + for (idx = base; idx < extent; idx++) { + if ('@' == STRING_CHAR(idx)) { // there is a user + CFIndex idx2; + flags |= HAS_USER; + numRanges ++; + ranges[1].location = base; // base of the user + for (idx2 = base; idx2 < idx; idx2++) { + if (':' == STRING_CHAR(idx2)) { // found a password separator + flags |= HAS_PASSWORD; + numRanges ++; + ranges[2].location = idx2+1; // base of the password + ranges[2].length = idx-(idx2+1); // password extent + ranges[1].length = idx2 - base; // user extent + break; + } + } + if (!(flags & HAS_PASSWORD)) { + // user extends to the '@' + ranges[1].length = idx - base; // user extent + } + base = idx + 1; + break; + } + } + flags |= HAS_HOST; + numRanges ++; + ranges[3].location = base; // base of host + + // base has been advanced past the user and password if they existed + for (idx = base; idx < extent; idx++) { + // IPV6 support (RFC 2732) DCJ June/10/2002 + if ('[' == STRING_CHAR(idx)) { // starting IPV6 explicit address + insideIPV6Host = true; + flags |= IS_IPV6_ENCODED; + } + if (']' == STRING_CHAR(idx)) { // ending IPV6 explicit address + insideIPV6Host = false; + } + // there is a port if we see a colon outside ipv6 address + if (!insideIPV6Host && ':' == STRING_CHAR(idx)) { + flags |= HAS_PORT; + numRanges ++; + ranges[4].location = idx+1; // base of port + ranges[4].length = extent - (idx+1); // port extent + ranges[3].length = idx - base; // host extent + break; + } + } + if (!(flags & HAS_PORT)) { + ranges[3].length = extent - base; // host extent + } + } + base_idx = extent; + } + + // 4: parse the query; remainder after left-most "?" is query + for (idx = base_idx; idx < string_length; idx++) { + if ('?' == STRING_CHAR(idx)) { + flags |= HAS_QUERY; + numRanges ++; + ranges[7].location = idx + 1; + ranges[7].length = string_length - (idx+1); + string_length = idx; // remove query from parse string + break; + } + } + + // 5: parse the parameters; remainder after left-most ";" is parameters + for (idx = base_idx; idx < string_length; idx++) { + if (';' == STRING_CHAR(idx)) { + flags |= HAS_PARAMETERS; + numRanges ++; + ranges[6].location = idx + 1; + ranges[6].length = string_length - (idx+1); + string_length = idx; // remove parameters from parse string + break; + } + } + + // 6: parse the path; it's whatever's left between string_length & base_idx + if (string_length - base_idx != 0 || (flags & NET_LOCATION_MASK)) + { + // If we have a net location, we are 1808-compliant, and an empty path substring implies a path of "/" + UniChar ch; + Boolean isDir; + CFRange pathRg; + flags |= HAS_PATH; + numRanges ++; + pathRg.location = base_idx; + pathRg.length = string_length - base_idx; + ranges[5] = pathRg; + + if (pathRg.length > 0) { + ch = STRING_CHAR(pathRg.location + pathRg.length - 1); + if (ch == '/') { + isDir = true; + } else if (ch == '.') { + if (pathRg.length == 1) { + isDir = true; + } else { + ch = STRING_CHAR(pathRg.location + pathRg.length - 2); + if (ch == '/') { + isDir = true; + } else if (ch != '.') { + isDir = false; + } else if (pathRg.length == 2) { + isDir = true; + } else { + isDir = (STRING_CHAR(pathRg.location + pathRg.length - 3) == '/'); + } + } + } else { + isDir = false; + } + } else { + isDir = (baseURL != NULL) ? CFURLHasDirectoryPath(baseURL) : false; + } + if (isDir) { + flags |= IS_DIRECTORY; + } + } + + if (freeCharacters) { + CFAllocatorDeallocate(alloc, useCString ? (void *)cstring : (void *)ustring); + } + (*theFlags) = flags; + (*range) = CFAllocatorAllocate(alloc, sizeof(CFRange)*numRanges, 0); + numRanges = 0; + for (idx = 0, flags = 1; flags != (1<<9); flags = (flags<<1), idx ++) { + if ((*theFlags) & flags) { + (*range)[numRanges] = ranges[idx]; + numRanges ++; + } + } + if (((*theFlags) & HAS_PATH) && !CFStringFindWithOptions(string, CFSTR("%"), ranges[5], 0, NULL)) { + (*theFlags) |= POSIX_AND_URL_PATHS_MATCH; + } +} + +static Boolean scanCharacters(CFAllocatorRef alloc, CFMutableStringRef *escapedString, UInt32 *flags, const unsigned char *cstring, const UniChar *ustring, Boolean useCString, CFIndex base, CFIndex end, CFIndex *mark, UInt32 componentFlag, CFStringEncoding encoding) { + CFIndex idx; + Boolean sawIllegalChar = false; + for (idx = base; idx < end; idx ++) { + Boolean shouldEscape; + UniChar ch = STRING_CHAR(idx); + if (isURLLegalCharacter(ch)) { + if ((componentFlag == HAS_USER || componentFlag == HAS_PASSWORD) && (ch == '/' || ch == '?' || ch == '@')) { + shouldEscape = true; + } else { + shouldEscape = false; + } + } else if (ch == '%' && idx + 2 < end && isHexDigit(STRING_CHAR(idx + 1)) && isHexDigit(STRING_CHAR(idx+2))) { + shouldEscape = false; + } else if (componentFlag == HAS_HOST && ((idx == base && ch == '[') || (idx == end-1 && ch == ']'))) { + shouldEscape = false; + } else { + shouldEscape = true; + } + if (!shouldEscape) continue; + + sawIllegalChar = true; + if (componentFlag && flags) { + *flags |= (componentFlag << BIT_SHIFT_FROM_COMPONENT_TO_DIFFERS_FLAG); + } + if (!*escapedString) { + *escapedString = CFStringCreateMutable(alloc, 0); + } + if (useCString) { + CFStringRef tempString = CFStringCreateWithBytes(alloc, &(cstring[*mark]), idx - *mark, kCFStringEncodingISOLatin1, false); + CFStringAppend(*escapedString, tempString); + CFRelease(tempString); + } else { + CFStringAppendCharacters(*escapedString, &(ustring[*mark]), idx - *mark); + } + *mark = idx + 1; + _appendPercentEscapesForCharacter(ch, encoding, *escapedString); // This can never fail because anURL->_string was constructed from the encoding passed in + } + return sawIllegalChar; +} + +static void computeSanitizedString(CFURLRef url) { + CFAllocatorRef alloc = CFGetAllocator(url); + CFIndex string_length = CFStringGetLength(url->_string); + Boolean useCString, freeCharacters; + const unsigned char *cstring = NULL; + const UniChar *ustring = NULL; + CFIndex base; // where to scan from + CFIndex mark; // first character not-yet copied to sanitized string + if (!(url->_flags & IS_PARSED)) { + _parseComponentsOfURL(url); + } + constructBuffers(alloc, url->_string, &cstring, &ustring, &useCString, &freeCharacters); + if (!(url->_flags & IS_DECOMPOSABLE)) { + // Impossible to have a problem character in the scheme + base = _rangeForComponent(url->_flags, url->ranges, HAS_SCHEME).length + 1; + mark = 0; + if (!scanCharacters(alloc, &(((struct __CFURL *)url)->_sanatizedString), &(((struct __CFURL *)url)->_flags), cstring, ustring, useCString, base, string_length, &mark, 0, url->_encoding)) { + ((struct __CFURL *)url)->_flags |= ORIGINAL_AND_URL_STRINGS_MATCH; + } + } else { + // Go component by component + CFIndex currentComponent = HAS_USER; + mark = 0; + while (currentComponent < (HAS_FRAGMENT << 1)) { + CFRange componentRange = _rangeForComponent(url->_flags, url->ranges, currentComponent); + if (componentRange.location != kCFNotFound) { + scanCharacters(alloc, &(((struct __CFURL *)url)->_sanatizedString), &(((struct __CFURL *)url)->_flags), cstring, ustring, useCString, componentRange.location, componentRange.location + componentRange.length, &mark, currentComponent, url->_encoding); + } + currentComponent = currentComponent << 1; + } + if (!url->_sanatizedString) { + ((struct __CFURL *)url)->_flags |= ORIGINAL_AND_URL_STRINGS_MATCH; + } + } + if (url->_sanatizedString && mark != string_length) { + if (useCString) { + CFStringRef tempString = CFStringCreateWithBytes(alloc, &(cstring[mark]), string_length - mark, kCFStringEncodingISOLatin1, false); + CFStringAppend(url->_sanatizedString, tempString); + CFRelease(tempString); + } else { + CFStringAppendCharacters(url->_sanatizedString, &(ustring[mark]), string_length - mark); + } + } + if (freeCharacters) { + CFAllocatorDeallocate(alloc, useCString ? (void *)cstring : (void *)ustring); + } +} + + +static CFStringRef correctedComponent(CFStringRef comp, UInt32 compFlag, CFStringEncoding enc) { + CFAllocatorRef alloc = CFGetAllocator(comp); + CFIndex string_length = CFStringGetLength(comp); + Boolean useCString, freeCharacters; + const unsigned char *cstring = NULL; + const UniChar *ustring = NULL; + CFIndex mark = 0; // first character not-yet copied to sanitized string + CFMutableStringRef result = NULL; + + constructBuffers(alloc, comp, &cstring, &ustring, &useCString, &freeCharacters); + scanCharacters(alloc, &result, NULL, cstring, ustring, useCString, 0, string_length, &mark, compFlag, enc); + if (result) { + if (mark < string_length) { + if (useCString) { + CFStringRef tempString = CFStringCreateWithBytes(alloc, &(cstring[mark]), string_length - mark, kCFStringEncodingISOLatin1, false); + CFStringAppend(result, tempString); + CFRelease(tempString); + } else { + CFStringAppendCharacters(result, &(ustring[mark]), string_length - mark); + } + } + } else { + // This should nevr happen + CFRetain(comp); + result = (CFMutableStringRef)comp; + } + if (freeCharacters) { + CFAllocatorDeallocate(alloc, useCString ? (void *)cstring : (void *)ustring); + } + return result; +} + +#undef STRING_CHAR + +CF_EXPORT CFURLRef _CFURLAlloc(CFAllocatorRef allocator) { + struct __CFURL *url; +#if DEBUG_URL_MEMORY_USAGE + numURLs ++; + if (!URLAllocator) { + URLAllocator = CFCountingAllocatorCreate(NULL); + } + allocator = URLAllocator; +#endif + url = (struct __CFURL *)_CFRuntimeCreateInstance(allocator, __kCFURLTypeID, sizeof(struct __CFURL) - sizeof(CFRuntimeBase), NULL); + if (url) { + url->_flags = 0; + if (createOldUTF8StyleURLs()) { + url->_flags |= IS_OLD_UTF8_STYLE; + } + url->_string = NULL; + url->_base = NULL; + url->ranges = NULL; + url->_reserved = NULL; + url->_encoding = kCFStringEncodingUTF8; + url->_sanatizedString = NULL; + } + return url; +} + +// It is the caller's responsibility to guarantee that if URLString is absolute, base is NULL. This is necessary to avoid duplicate processing for file system URLs, which had to decide whether to compute the cwd for the base; we don't want to duplicate that work. This ALSO means it's the caller's responsibility to set the IS_ABSOLUTE bit, since we may have a degenerate URL whose string is relative, but lacks a base. +static void _CFURLInit(struct __CFURL *url, CFStringRef URLString, UInt32 fsType, CFURLRef base) { + CFAssert1(URLString != NULL && CFGetTypeID(URLString) == CFStringGetTypeID() && CFStringGetLength(URLString) != 0, __kCFLogAssertion, "%s(): internal CF error; empty string encountered", __PRETTY_FUNCTION__); + CFAssert2((fsType == FULL_URL_REPRESENTATION) || (fsType == kCFURLPOSIXPathStyle) || (fsType == kCFURLWindowsPathStyle) || (fsType == kCFURLHFSPathStyle), __kCFLogAssertion, "%s(): Received bad fsType %d", __PRETTY_FUNCTION__, fsType); + + // Coming in, the url has its allocator flag properly set, and its base initialized, and nothing else. + url->_string = CFStringCreateCopy(CFGetAllocator(url), URLString); + url->_flags |= (fsType << 16); + url->_base = base ? CFURLCopyAbsoluteURL(base) : NULL; +#if DEBUG_URL_MEMORY_USAGE + if (fsType != FULL_URL_REPRESENTATION) { + numFileURLsCreated ++; + } +#endif +} + +CF_EXPORT void _CFURLInitFSPath(CFURLRef url, CFStringRef path) { + CFIndex len = CFStringGetLength(path); + if (len && CFStringGetCharacterAtIndex(path, 0) == '/') { + _CFURLInit((struct __CFURL *)url, path, kCFURLPOSIXPathStyle, NULL); + ((struct __CFURL *)url)->_flags |= IS_ABSOLUTE; + } else { + CFURLRef cwdURL = _CFURLCreateCurrentDirectoryURL(CFGetAllocator(url)); + _CFURLInit((struct __CFURL *)url, path, kCFURLPOSIXPathStyle, cwdURL); + CFRelease(cwdURL); + } + if (!len || '/' == CFStringGetCharacterAtIndex(path, len - 1)) + ((struct __CFURL *)url)->_flags |= IS_DIRECTORY; +} + +// Exported for Foundation's use +CF_EXPORT Boolean _CFStringIsLegalURLString(CFStringRef string) { + // Check each character to make sure it is a legal URL char. The valid characters are 'A'-'Z', 'a' - 'z', '0' - '9', plus the characters in "-_.!~*'()", and the set of reserved characters (these characters have special meanings in the URL syntax), which are ";/?:@&=+$,". In addition, percent escape sequences '%' hex-digit hex-digit are permitted. + // Plus the hash character '#' which denotes the beginning of a fragment, and can appear exactly once in the entire URL string. -- REW, 12/13/2000 + CFStringInlineBuffer stringBuffer; + CFIndex idx = 0, length; + Boolean sawHash = false; + if (!string) { + CFAssert(false, __kCFLogAssertion, "Cannot create an CFURL from a NULL string"); + return false; + } + length = CFStringGetLength(string); + CFStringInitInlineBuffer(string, &stringBuffer, CFRangeMake(0, length)); + while (idx < length) { + UniChar ch = CFStringGetCharacterFromInlineBuffer(&stringBuffer, idx); + idx ++; + if (ch >= 'a' && ch <= 'z') continue; + if (ch >= '0' && ch <= '9') continue; + if (ch >= 'A' && ch <= 'Z') continue; + if (ch == '-' || ch == '_' || ch == '.' || ch == '!' || ch == '~' || ch == '*' || ch == '\'' || ch == '(' || ch == ')') continue; + if (ch == ';' || ch == '/' || ch == '?' || ch == ':' || ch == '@' || ch == '&' || ch == '=' || ch == '+' || ch == '$' || ch == ',') continue; + if (ch == '[' || ch == ']') continue; // IPV6 support (RFC 2732) DCJ June/10/2002 + if (ch == '#') { + if (sawHash) break; + sawHash = true; + continue; + } + // Commenting out all the CFAsserts below because they cause the program to abort if running against the debug library. If we have a non-fatal assert, we should use that instead. -- REW 5/20/2002 + if (ch != '%') { + //CFAssert1(false, __kCFLogAssertion, "Detected illegal URL character 0x%x when trying to create a CFURL", ch); + break; + } + if (idx + 2 > length) { + //CFAssert1(false, __kCFLogAssertion, "Detected illegal percent escape sequence at character %d when trying to create a CFURL", idx-1); + idx = -1; // To guarantee index < length, and our failure case is triggered + break; + } + ch = CFStringGetCharacterFromInlineBuffer(&stringBuffer, idx); + idx ++; + if (!((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F'))) { + //CFAssert1(false, __kCFLogAssertion, "Detected illegal percent escape sequence at character %d when trying to create a CFURL", idx-2); + idx = -1; + break; + } + ch = CFStringGetCharacterFromInlineBuffer(&stringBuffer, idx); + idx ++; + if (!((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F'))) { + //CFAssert1(false, __kCFLogAssertion, "Detected illegal percent escape sequence at character %d when trying to create a CFURL", idx-3); + idx = -1; + break; + } + } + if (idx < length) { + return false; + } + return true; +} + +CF_EXPORT void _CFURLInitWithString(CFURLRef myURL, CFStringRef string, CFURLRef baseURL) { + struct __CFURL *url = (struct __CFURL *)myURL; // Supress annoying compile warnings + Boolean isAbsolute = false; + CFRange colon = CFStringFind(string, CFSTR(":"), 0); + if (colon.location != kCFNotFound) { + isAbsolute = true; + CFIndex i; + for (i = 0; i < colon.location; i++) { + char ch = CFStringGetCharacterAtIndex(string, i); + if (!scheme_valid(ch)) { + isAbsolute = false; + break; + } + } + } + _CFURLInit(url, string, FULL_URL_REPRESENTATION, isAbsolute ? NULL : baseURL); + if (isAbsolute) { + url->_flags |= IS_ABSOLUTE; + } +} + +struct __CFURLEncodingTranslationParameters { + CFStringEncoding fromEnc; + CFStringEncoding toEnc; + const UniChar *addlChars; + int count; + Boolean escapeHighBit; + Boolean escapePercents; + Boolean agreesOverASCII; + Boolean encodingsMatch; +} ; + +static Boolean _shouldEscapeForEncodingConversion(UniChar ch, void *context) { + struct __CFURLEncodingTranslationParameters *info = (struct __CFURLEncodingTranslationParameters *)context; + if (info->escapeHighBit && ch > 0x7F) { + return true; + } else if (ch == '%' && info->escapePercents) { + return true; + } else if (info->addlChars) { + const UniChar *escChar = info->addlChars; + int i; + for (i = 0; i < info->count; escChar ++, i ++) { + if (*escChar == ch) { + return true; + } + } + } + return false; +} + +static CFIndex _convertEscapeSequence(CFIndex percentIndex, CFStringRef urlString, CFStringRef *newString, void *context) { + struct __CFURLEncodingTranslationParameters *info = (struct __CFURLEncodingTranslationParameters *)context; + CFMutableDataRef newData; + Boolean sawNonASCIICharacter = false; + CFIndex i = percentIndex; + CFIndex length; + *newString = NULL; + if (info->encodingsMatch) return percentIndex + 3; // +3 because we want the two characters of the percent encoding to not be copied verbatim, as well + newData = CFDataCreateMutable(CFGetAllocator(urlString), 0); + length = CFStringGetLength(urlString); + + while (i < length && CFStringGetCharacterAtIndex(urlString, i) == '%') { + uint8_t byte; + if (i+2 >= length || !_translateBytes(CFStringGetCharacterAtIndex(urlString, i+1), CFStringGetCharacterAtIndex(urlString, i+2), &byte)) { + CFRelease(newData); + return -1; + } + if (byte > 0x7f) sawNonASCIICharacter = true; + CFDataAppendBytes(newData, &byte, 1); + i += 3; + } + if (!sawNonASCIICharacter && info->agreesOverASCII) { + return i; + } else { + CFStringRef tmp = CFStringCreateWithBytes(CFGetAllocator(urlString), CFDataGetBytePtr(newData), CFDataGetLength(newData), info->fromEnc, false); + CFIndex tmpIndex, tmpLen; + if (!tmp) { + CFRelease(newData); + return -1; + } + tmpLen = CFStringGetLength(tmp); + *newString = CFStringCreateMutable(CFGetAllocator(urlString), 0); + for (tmpIndex = 0; tmpIndex < tmpLen; tmpIndex ++) { + if (!_appendPercentEscapesForCharacter(CFStringGetCharacterAtIndex(tmp, tmpIndex), info->toEnc, (CFMutableStringRef)(*newString))) { + break; + } + } + CFRelease(tmp); + CFRelease(newData); + if (tmpIndex < tmpLen) { + CFRelease(*newString); + *newString = NULL; + return -1; + } else { + return i; + } + } +} + +/* Returned string is retained for the caller; if escapePercents is true, then we do not look for any %-escape encodings in urlString */ +static CFStringRef _convertPercentEscapes(CFStringRef urlString, CFStringEncoding fromEncoding, CFStringEncoding toEncoding, Boolean escapeAllHighBitCharacters, Boolean escapePercents, const UniChar *addlCharsToEscape, int numAddlChars) { + struct __CFURLEncodingTranslationParameters context; + context.fromEnc = fromEncoding; + context.toEnc = toEncoding; + context.addlChars = addlCharsToEscape; + context.count = numAddlChars; + context.escapeHighBit = escapeAllHighBitCharacters; + context.escapePercents = escapePercents; + context.agreesOverASCII = (__CFStringEncodingIsSupersetOfASCII(toEncoding) && __CFStringEncodingIsSupersetOfASCII(fromEncoding)) ? true : false; + context.encodingsMatch = (fromEncoding == toEncoding) ? true : false; + return _addPercentEscapesToString(CFGetAllocator(urlString), urlString, _shouldEscapeForEncodingConversion, _convertEscapeSequence, toEncoding, &context); +} + +// encoding will be used both to interpret the bytes of URLBytes, and to interpret any percent-escapes within the bytes. +CFURLRef CFURLCreateWithBytes(CFAllocatorRef allocator, const uint8_t *URLBytes, CFIndex length, CFStringEncoding encoding, CFURLRef baseURL) { + CFStringRef urlString = CFStringCreateWithBytes(allocator, URLBytes, length, encoding, false); + CFURLRef result; + if (!urlString || CFStringGetLength(urlString) == 0) { + if (urlString) CFRelease(urlString); + return NULL; + } + if (createOldUTF8StyleURLs()) { + if (encoding != kCFStringEncodingUTF8) { + CFStringRef tmp = _convertPercentEscapes(urlString, encoding, kCFStringEncodingUTF8, false, false, NULL, 0); + CFRelease(urlString); + urlString = tmp; + if (!urlString) return NULL; + } + } + + result = _CFURLAlloc(allocator); + if (result) { + _CFURLInitWithString(result, urlString, baseURL); + if (encoding != kCFStringEncodingUTF8 && !createOldUTF8StyleURLs()) { + ((struct __CFURL *)result)->_encoding = encoding; + } + } + CFRelease(urlString); // it's retained by result, now. + return result; +} + +CFDataRef CFURLCreateData(CFAllocatorRef allocator, CFURLRef url, CFStringEncoding encoding, Boolean escapeWhitespace) { + static const UniChar whitespaceChars[4] = {' ', '\n', '\r', '\t'}; + CFStringRef myStr = CFURLGetString(url); + CFStringRef newStr; + CFDataRef result; + if (url->_flags & IS_OLD_UTF8_STYLE) { + newStr = (encoding == kCFStringEncodingUTF8) ? CFRetain(myStr) : _convertPercentEscapes(myStr, kCFStringEncodingUTF8, encoding, true, false, escapeWhitespace ? whitespaceChars : NULL, escapeWhitespace ? 4 : 0); + } else { + newStr=myStr; + CFRetain(newStr); + } + result = CFStringCreateExternalRepresentation(allocator, newStr, encoding, 0); + CFRelease(newStr); + return result; +} + +// Any escape sequences in URLString will be interpreted via UTF-8. +CFURLRef CFURLCreateWithString(CFAllocatorRef allocator, CFStringRef URLString, CFURLRef baseURL) { + CFURLRef url; + if (!URLString || CFStringGetLength(URLString) == 0) return NULL; + if (!_CFStringIsLegalURLString(URLString)) return NULL; + url = _CFURLAlloc(allocator); + if (url) { + _CFURLInitWithString(url, URLString, baseURL); + } + return url; +} + +static CFURLRef _CFURLCreateWithArbitraryString(CFAllocatorRef allocator, CFStringRef URLString, CFURLRef baseURL) { + CFURLRef url; + if (!URLString || CFStringGetLength(URLString) == 0) return NULL; + url = _CFURLAlloc(allocator); + if (url) { + _CFURLInitWithString(url, URLString, baseURL); + } + return url; +} + +CFURLRef CFURLCreateAbsoluteURLWithBytes(CFAllocatorRef alloc, const UInt8 *relativeURLBytes, CFIndex length, CFStringEncoding encoding, CFURLRef baseURL, Boolean useCompatibilityMode) { + CFStringRef relativeString = CFStringCreateWithBytes(alloc, relativeURLBytes, length, encoding, false); + if (!relativeString) { + return NULL; + } + if (!useCompatibilityMode) { + CFURLRef url = _CFURLCreateWithArbitraryString(alloc, relativeString, baseURL); + CFRelease(relativeString); + ((struct __CFURL *)url)->_encoding = encoding; + if (url) { + CFURLRef absURL = CFURLCopyAbsoluteURL(url); + CFRelease(url); + return absURL; + } else { + return NULL; + } + } else { + UInt32 absFlags = 0; + CFRange *absRanges; + CFStringRef absString = NULL; + Boolean absStringIsMutable = false; + CFURLRef absURL; + if (!baseURL) { + absString = relativeString; + } else { + UniChar ch = CFStringGetCharacterAtIndex(relativeString, 0); + if (ch == '?' || ch == ';' || ch == '#') { + // Nothing but parameter + query + fragment; append to the baseURL string + CFStringRef baseString; + if (CF_IS_OBJC(__kCFURLTypeID, baseURL)) { + baseString = CFURLGetString(baseURL); + } else { + baseString = baseURL->_string; + } + absString = CFStringCreateMutable(alloc, CFStringGetLength(baseString) + CFStringGetLength(relativeString)); + CFStringAppend((CFMutableStringRef)absString, baseString); + CFStringAppend((CFMutableStringRef)absString, relativeString); + absStringIsMutable = true; + } else { + UInt32 relFlags = 0; + CFRange *relRanges; + CFStringRef relString = NULL; + _parseComponents(alloc, relativeString, baseURL, &relFlags, &relRanges); + if (relFlags & HAS_SCHEME) { + CFStringRef baseScheme = CFURLCopyScheme(baseURL); + CFRange relSchemeRange = _rangeForComponent(relFlags, relRanges, HAS_SCHEME); + if (baseScheme && CFStringGetLength(baseScheme) == relSchemeRange.length && CFStringHasPrefix(relativeString, baseScheme)) { + relString = CFStringCreateWithSubstring(alloc, relativeString, CFRangeMake(relSchemeRange.length+1, CFStringGetLength(relativeString) - relSchemeRange.length - 1)); + CFAllocatorDeallocate(alloc, relRanges); + relFlags = 0; + _parseComponents(alloc, relString, baseURL, &relFlags, &relRanges); + } else { + // Discard the base string; the relative string is absolute and we're not in the funky edge case where the schemes match + CFRetain(relativeString); + absString = relativeString; + } + if (baseScheme) CFRelease(baseScheme); + } else { + CFRetain(relativeString); + relString = relativeString; + } + if (!absString) { + if (!CF_IS_OBJC(__kCFURLTypeID, baseURL)) { + if (!(baseURL->_flags & IS_PARSED)) { + _parseComponentsOfURL(baseURL); + } + absString = resolveAbsoluteURLString(alloc, relString, relFlags, relRanges, baseURL->_string, baseURL->_flags, baseURL->ranges); + } else { + CFStringRef baseString; + UInt32 baseFlags = 0; + CFRange *baseRanges; + if (CF_IS_OBJC(__kCFURLTypeID, baseURL)) { + baseString = CFURLGetString(baseURL); + } else { + baseString = baseURL->_string; + } + _parseComponents(alloc, baseString, NULL, &baseFlags, &baseRanges); + absString = resolveAbsoluteURLString(alloc, relString, relFlags, relRanges, baseString, baseFlags, baseRanges); + CFAllocatorDeallocate(alloc, baseRanges); + } + absStringIsMutable = true; + } + if (relString) CFRelease(relString); + CFAllocatorDeallocate(alloc, relRanges); + } + CFRelease(relativeString); + } + _parseComponents(alloc, absString, NULL, &absFlags, &absRanges); + if (absFlags & HAS_PATH) { + CFRange pathRg = _rangeForComponent(absFlags, absRanges, HAS_PATH); + // This is expensive, but it allows us to reuse _resolvedPath. It should be cleaned up to get this allocation removed at some point. - REW + UniChar *buf = CFAllocatorAllocate(alloc, sizeof(UniChar) * (pathRg.length + 1), NULL); + CFStringRef newPath; + CFStringGetCharacters(absString, pathRg, buf); + buf[pathRg.length] = '\0'; + newPath = _resolvedPath(buf, buf + pathRg.length, '/', true, false, alloc); + if (CFStringGetLength(newPath) != pathRg.length) { + if (!absStringIsMutable) { + CFStringRef tmp = CFStringCreateMutableCopy(alloc, CFStringGetLength(absString), absString); + CFRelease(absString); + absString = tmp; + } + CFStringReplace((CFMutableStringRef)absString, pathRg, newPath); + } + CFRelease(newPath); + // Do not deallocate buf; newPath took ownership of it. + } + CFAllocatorDeallocate(alloc, absRanges); + absURL = _CFURLCreateWithArbitraryString(alloc, absString, NULL); + CFRelease(absString); + if (absURL) { + ((struct __CFURL *)absURL)->_encoding = encoding; + } + return absURL; + } +} + +/* This function is this way because I pulled it out of _resolvedURLPath (so that _resolvedFileSystemPath could use it), and I didn't want to spend a bunch of energy reworking the code. So instead of being a bit more intelligent about inputs, it just demands a slightly perverse set of parameters, to match the old _resolvedURLPath code. -- REW, 6/14/99 */ +static CFStringRef _resolvedPath(UniChar *pathStr, UniChar *end, UniChar pathDelimiter, Boolean stripLeadingDotDots, Boolean stripTrailingDelimiter, CFAllocatorRef alloc) { + UniChar *idx = pathStr; + while (idx < end) { + if (*idx == '.') { + if (idx+1 == end) { + if (idx != pathStr) { + *idx = '\0'; + end = idx; + } + break; + } else if (*(idx+1) == pathDelimiter) { + if (idx + 2 != end || idx != pathStr) { + memmove(idx, idx+2, (end-(idx+2)+1) * sizeof(UniChar)); + end -= 2; + continue; + } else { + // Do not delete the sole path component + break; + } + } else if (*(idx+1) == '.' && (idx+2 == end || *(idx+2) == pathDelimiter)) { + if (idx - pathStr >= 2) { + // Need at least 2 characters between index and pathStr, because we know if index != newPath, then *(index-1) == pathDelimiter, and we need something before that to compact out. + UniChar *lastDelim = idx-2; + while (lastDelim >= pathStr && *lastDelim != pathDelimiter) lastDelim --; + lastDelim ++; + if (lastDelim != idx && (idx-lastDelim != 3 || *lastDelim != '.' || *(lastDelim +1) != '.')) { + // We have a genuine component to compact out + if (idx+2 != end) { + unsigned numCharsToMove = end - (idx+3) + 1; // +1 to move the '\0' as well + memmove(lastDelim, idx+3, numCharsToMove * sizeof(UniChar)); + end -= (idx + 3 - lastDelim); + idx = lastDelim; + continue; + } else if (lastDelim != pathStr) { + *lastDelim = '\0'; + end = lastDelim; + break; + } else { + // Don't allow the path string to devolve to the empty string. Fall back to "." instead. - REW + pathStr[0] = '.'; + pathStr[1] = '/'; + pathStr[2] = '\0'; + break; + } + } + } else if (stripLeadingDotDots) { + if (idx + 3 != end) { + unsigned numCharsToMove = end - (idx + 3) + 1; + memmove(idx, idx+3, numCharsToMove * sizeof(UniChar)); + end -= 3; + continue; + } else { + // Do not devolve the last path component + break; + } + } + } + } + while (idx < end && *idx != pathDelimiter) idx ++; + idx ++; + } + if (stripTrailingDelimiter && end != pathStr && end-1 != pathStr && *(end-1) == pathDelimiter) { + end --; + } + return CFStringCreateWithCharactersNoCopy(alloc, pathStr, end - pathStr, alloc); +} + +static CFMutableStringRef resolveAbsoluteURLString(CFAllocatorRef alloc, CFStringRef relString, UInt32 relFlags, CFRange *relRanges, CFStringRef baseString, UInt32 baseFlags, CFRange *baseRanges) { + CFMutableStringRef newString = CFStringCreateMutable(alloc, 0); + CFIndex bufLen = CFStringGetLength(baseString) + CFStringGetLength(relString); // Overkill, but guarantees we never allocate again + UniChar *buf = CFAllocatorAllocate(alloc, bufLen * sizeof(UniChar), 0); + CFRange rg; + + rg = _rangeForComponent(baseFlags, baseRanges, HAS_SCHEME); + if (rg.location != kCFNotFound) { + CFStringGetCharacters(baseString, rg, buf); + CFStringAppendCharacters(newString, buf, rg.length); + CFStringAppendCString(newString, ":", kCFStringEncodingASCII); + } + + if (relFlags & NET_LOCATION_MASK) { + CFStringAppend(newString, relString); + } else { + CFStringAppendCString(newString, "//", kCFStringEncodingASCII); + rg = _netLocationRange(baseFlags, baseRanges); + if (rg.location != kCFNotFound) { + CFStringGetCharacters(baseString, rg, buf); + CFStringAppendCharacters(newString, buf, rg.length); + } + + if (relFlags & HAS_PATH) { + CFRange relPathRg = _rangeForComponent(relFlags, relRanges, HAS_PATH); + CFRange basePathRg = _rangeForComponent(baseFlags, baseRanges, HAS_PATH); + CFStringRef newPath; + Boolean useRelPath = false; + Boolean useBasePath = false; + if (basePathRg.location == kCFNotFound) { + useRelPath = true; + } else if (relPathRg.length == 0) { + useBasePath = true; + } else if (CFStringGetCharacterAtIndex(relString, relPathRg.location) == '/') { + useRelPath = true; + } else if (basePathRg.location == kCFNotFound || basePathRg.length == 0) { + useRelPath = true; + } + if (useRelPath) { + newPath = CFStringCreateWithSubstring(alloc, relString, relPathRg); + } else if (useBasePath) { + newPath = CFStringCreateWithSubstring(alloc, baseString, basePathRg); + } else { + // #warning FIXME - Get rid of this allocation + UniChar *newPathBuf = CFAllocatorAllocate(alloc, sizeof(UniChar) * (relPathRg.length + basePathRg.length + 1), 0); + UniChar *idx, *end; + CFStringGetCharacters(baseString, basePathRg, newPathBuf); + idx = newPathBuf + basePathRg.length - 1; + while (idx != newPathBuf && *idx != '/') idx --; + if (*idx == '/') idx ++; + CFStringGetCharacters(relString, relPathRg, idx); + end = idx + relPathRg.length; + *end = 0; + newPath = _resolvedPath(newPathBuf, end, '/', false, false, alloc); + } + /* Under Win32 absolute path can begin with letter + * so we have to add one '/' to the newString + * (Sergey Zubarev) + */ +#if defined(__WIN32__) + if (CFStringGetCharacterAtIndex(newPath, 0) != '/') { + CFStringAppend(newString, CFSTR("/")); + } +#endif + // if the relative URL does not begin with a slash and + // the base does not end with a slash, add a slash + if ((basePathRg.location == kCFNotFound || basePathRg.length == 0) && CFStringGetCharacterAtIndex(newPath, 0) != '/') { + CFStringAppendCString(newString, "/", kCFStringEncodingASCII); + } + + CFStringAppend(newString, newPath); + CFRelease(newPath); + rg.location = relPathRg.location + relPathRg.length; + rg.length = CFStringGetLength(relString); + if (rg.length > rg.location) { + rg.length -= rg.location; + CFStringGetCharacters(relString, rg, buf); + CFStringAppendCharacters(newString, buf, rg.length); + } + } else { + rg = _rangeForComponent(baseFlags, baseRanges, HAS_PATH); + if (rg.location != kCFNotFound) { + CFStringGetCharacters(baseString, rg, buf); + CFStringAppendCharacters(newString, buf, rg.length); + } + + if (!(relFlags & RESOURCE_SPECIFIER_MASK)) { + // ??? Can this ever happen? + UInt32 rsrcFlag = _firstResourceSpecifierFlag(baseFlags); + if (rsrcFlag) { + rg.location = _rangeForComponent(baseFlags, baseRanges, rsrcFlag).location; + rg.length = CFStringGetLength(baseString) - rg.location; + rg.location --; // To pick up the separator + rg.length ++; + CFStringGetCharacters(baseString, rg, buf); + CFStringAppendCharacters(newString, buf, rg.length); + } + } else if (relFlags & HAS_PARAMETERS) { + rg = _rangeForComponent(relFlags, relRanges, HAS_PARAMETERS); + rg.location --; // To get the semicolon that starts the parameters + rg.length = CFStringGetLength(relString) - rg.location; + CFStringGetCharacters(relString, rg, buf); + CFStringAppendCharacters(newString, buf, rg.length); + } else { + // Sigh; we have to resolve these against one another + rg = _rangeForComponent(baseFlags, baseRanges, HAS_PARAMETERS); + if (rg.location != kCFNotFound) { + CFStringAppendCString(newString, ";", kCFStringEncodingASCII); + CFStringGetCharacters(baseString, rg, buf); + CFStringAppendCharacters(newString, buf, rg.length); + } + rg = _rangeForComponent(relFlags, relRanges, HAS_QUERY); + if (rg.location != kCFNotFound) { + CFStringAppendCString(newString, "?", kCFStringEncodingASCII); + CFStringGetCharacters(relString, rg, buf); + CFStringAppendCharacters(newString, buf, rg.length); + } else { + rg = _rangeForComponent(baseFlags, baseRanges, HAS_QUERY); + if (rg.location != kCFNotFound) { + CFStringAppendCString(newString, "?", kCFStringEncodingASCII); + CFStringGetCharacters(baseString, rg, buf); + CFStringAppendCharacters(newString, buf, rg.length); + } + } + // Only the relative portion of the URL can supply the fragment; otherwise, what would be in the relativeURL? + rg = _rangeForComponent(relFlags, relRanges, HAS_FRAGMENT); + if (rg.location != kCFNotFound) { + CFStringAppendCString(newString, "#", kCFStringEncodingASCII); + CFStringGetCharacters(relString, rg, buf); + CFStringAppendCharacters(newString, buf, rg.length); + } + } + } + } + CFAllocatorDeallocate(alloc, buf); + return newString; +} + +CFURLRef CFURLCopyAbsoluteURL(CFURLRef relativeURL) { + CFURLRef anURL, base; + CFURLPathStyle fsType; + CFAllocatorRef alloc = CFGetAllocator(relativeURL); + CFStringRef baseString, newString; + UInt32 baseFlags; + CFRange *baseRanges; + Boolean baseIsObjC; + + CFAssert1(relativeURL != NULL, __kCFLogAssertion, "%s(): Cannot create an absolute URL from a NULL relative URL", __PRETTY_FUNCTION__); + if (CF_IS_OBJC(__kCFURLTypeID, relativeURL)) { + CFURLRef (*absoluteURLMsg)(const void *, SEL) = (void *)__CFSendObjCMsg; + static SEL s = NULL; if (!s) s = __CFGetObjCSelector("absoluteURL"); + anURL = absoluteURLMsg((const void *)relativeURL, s); + if (anURL) CFRetain(anURL); + return anURL; + } + + __CFGenericValidateType(relativeURL, __kCFURLTypeID); + + base = relativeURL->_base; + if (!base) { + return CFRetain(relativeURL); + } + baseIsObjC = CF_IS_OBJC(__kCFURLTypeID, base); + fsType = URL_PATH_TYPE(relativeURL); + + if (!baseIsObjC && fsType != FULL_URL_REPRESENTATION && fsType == URL_PATH_TYPE(base)) { + return _CFURLCopyAbsoluteFileURL(relativeURL); + } + if (fsType != FULL_URL_REPRESENTATION) { + _convertToURLRepresentation((struct __CFURL *)relativeURL); + fsType = FULL_URL_REPRESENTATION; + } + if (!(relativeURL->_flags & IS_PARSED)) { + _parseComponentsOfURL(relativeURL); + } + if ((relativeURL->_flags & POSIX_AND_URL_PATHS_MATCH) && !(relativeURL->_flags & (RESOURCE_SPECIFIER_MASK | NET_LOCATION_MASK)) && !baseIsObjC && (URL_PATH_TYPE(base) == kCFURLPOSIXPathStyle)) { + // There's nothing to relativeURL's string except the path + CFStringRef newPath = _resolveFileSystemPaths(relativeURL->_string, base->_string, CFURLHasDirectoryPath(base), kCFURLPOSIXPathStyle, alloc); + CFURLRef result = CFURLCreateWithFileSystemPath(alloc, newPath, kCFURLPOSIXPathStyle, CFURLHasDirectoryPath(relativeURL)); + CFRelease(newPath); + return result; + } + + if (!baseIsObjC) { + CFURLPathStyle baseType = URL_PATH_TYPE(base); + if (baseType != FULL_URL_REPRESENTATION) { + _convertToURLRepresentation((struct __CFURL *)base); + } else if (!(base->_flags & IS_PARSED)) { + _parseComponentsOfURL(base); + } + baseString = base->_string; + baseFlags = base->_flags; + baseRanges = base->ranges; + } else { + baseString = CFURLGetString(base); + baseFlags = 0; + baseRanges = NULL; + _parseComponents(alloc, baseString, NULL, &baseFlags, &baseRanges); + } + + newString = resolveAbsoluteURLString(alloc, relativeURL->_string, relativeURL->_flags, relativeURL->ranges, baseString, baseFlags, baseRanges); + if (baseIsObjC) { + CFAllocatorDeallocate(alloc, baseRanges); + } + anURL = _CFURLCreateWithArbitraryString(alloc, newString, NULL); + CFRelease(newString); + ((struct __CFURL *)anURL)->_encoding = relativeURL->_encoding; + return anURL; +} + + +/*******************/ +/* Basic accessors */ +/*******************/ + +Boolean CFURLCanBeDecomposed(CFURLRef anURL) { + anURL = _CFURLFromNSURL(anURL); + if (URL_PATH_TYPE(anURL) != FULL_URL_REPRESENTATION) return true; + if (!(anURL->_flags & IS_PARSED)) { + _parseComponentsOfURL(anURL); + } + return ((anURL->_flags & IS_DECOMPOSABLE) != 0); +} + +CFStringRef CFURLGetString(CFURLRef url) { + CF_OBJC_FUNCDISPATCH0(__kCFURLTypeID, CFStringRef , url, "relativeString"); + if (URL_PATH_TYPE(url) != FULL_URL_REPRESENTATION) { + if (url->_base && (url->_flags & POSIX_AND_URL_PATHS_MATCH)) { + return url->_string; + } + _convertToURLRepresentation((struct __CFURL *)url); + } + if (!_haveTestedOriginalString(url)) { + computeSanitizedString(url); + } + if (url->_flags & ORIGINAL_AND_URL_STRINGS_MATCH) { + return url->_string; + } else { + return url->_sanatizedString; + } +} + +CFIndex CFURLGetBytes(CFURLRef url, UInt8 *buffer, CFIndex bufferLength) { + CFIndex length, charsConverted, usedLength; + CFStringRef string; + CFStringEncoding enc; + if (CF_IS_OBJC(__kCFURLTypeID, url)) { + string = CFURLGetString(url); + enc = kCFStringEncodingUTF8; + } else { + string = url->_string; + enc = url->_encoding; + } + length = CFStringGetLength(string); + charsConverted = CFStringGetBytes(string, CFRangeMake(0, length), enc, 0, false, buffer, bufferLength, &usedLength); + if (charsConverted != length) { + return -1; + } else { + return usedLength; + } +} + +CFURLRef CFURLGetBaseURL(CFURLRef anURL) { + CF_OBJC_FUNCDISPATCH0(__kCFURLTypeID, CFURLRef, anURL, "baseURL"); + return anURL->_base; +} + +// Assumes the URL is already parsed +static CFRange _rangeForComponent(UInt32 flags, CFRange *ranges, UInt32 compFlag) { + UInt32 idx = 0; + if (!(flags & compFlag)) return CFRangeMake(kCFNotFound, 0); + while (!(compFlag & 1)) { + compFlag = compFlag >> 1; + if (flags & 1) { + idx ++; + } + flags = flags >> 1; + } + return ranges[idx]; +} + +static CFStringRef _retainedComponentString(CFURLRef url, UInt32 compFlag, Boolean fromOriginalString, Boolean removePercentEscapes) { + CFRange rg; + CFStringRef comp; + CFAllocatorRef alloc = CFGetAllocator(url); + CFAssert1(URL_PATH_TYPE(url) == FULL_URL_REPRESENTATION, __kCFLogAssertion, "%s(): passed a file system URL", __PRETTY_FUNCTION__); + if (removePercentEscapes) fromOriginalString = true; + if (!(url->_flags & IS_PARSED)) { + _parseComponentsOfURL(url); + } + rg = _rangeForComponent(url->_flags, url->ranges, compFlag); + if (rg.location == kCFNotFound) return NULL; + comp = CFStringCreateWithSubstring(alloc, url->_string, rg); + if (!fromOriginalString) { + if (!_haveTestedOriginalString(url)) { + computeSanitizedString(url); + } + if (!(url->_flags & ORIGINAL_AND_URL_STRINGS_MATCH) && (url->_flags & (compFlag << BIT_SHIFT_FROM_COMPONENT_TO_DIFFERS_FLAG))) { + CFStringRef newComp = correctedComponent(comp, compFlag, url->_encoding); + CFRelease(comp); + comp = newComp; + } + } + if (removePercentEscapes) { + CFStringRef tmp; + if (url->_flags & IS_OLD_UTF8_STYLE || url->_encoding == kCFStringEncodingUTF8) { + tmp = CFURLCreateStringByReplacingPercentEscapes(alloc, comp, CFSTR("")); + } else { + tmp = CFURLCreateStringByReplacingPercentEscapesUsingEncoding(alloc, comp, CFSTR(""), url->_encoding); + } + CFRelease(comp); + comp = tmp; + } + return comp; +} + +CFStringRef CFURLCopyScheme(CFURLRef anURL) { + CFStringRef scheme; + if (CF_IS_OBJC(__kCFURLTypeID, anURL)) { + CFStringRef (*schemeMsg)(const void *, SEL) = (void *)__CFSendObjCMsg; + static SEL s = NULL; if (!s) s = __CFGetObjCSelector("scheme"); + scheme = schemeMsg((const void *)anURL, s); + if (scheme) CFRetain(scheme); + return scheme; + } + if (URL_PATH_TYPE(anURL) != FULL_URL_REPRESENTATION) { + if (anURL->_base) { + return CFURLCopyScheme(anURL->_base); + } else { + CFRetain(kCFURLFileScheme); // because caller will release it + return kCFURLFileScheme; + } + } + scheme = _retainedComponentString(anURL, HAS_SCHEME, true, false); + if (scheme) { + return scheme; + } else if (anURL->_base) { + return CFURLCopyScheme(anURL->_base); + } else { + return NULL; + } +} + +static CFRange _netLocationRange(UInt32 flags, CFRange *ranges) { + CFRange netRgs[4]; + CFRange netRg = {kCFNotFound, 0}; + CFIndex i, c = 4; + + if ((flags & NET_LOCATION_MASK) == 0) return CFRangeMake(kCFNotFound, 0); + + netRgs[0] = _rangeForComponent(flags, ranges, HAS_USER); + netRgs[1] = _rangeForComponent(flags, ranges, HAS_PASSWORD); + netRgs[2] = _rangeForComponent(flags, ranges, HAS_HOST); + netRgs[3] = _rangeForComponent(flags, ranges, HAS_PORT); + for (i = 0; i < c; i ++) { + if (netRgs[i].location == kCFNotFound) continue; + if (netRg.location == kCFNotFound) { + netRg = netRgs[i]; + } else { + netRg.length = netRgs[i].location + netRgs[i].length - netRg.location; + } + } + return netRg; +} + +CFStringRef CFURLCopyNetLocation(CFURLRef anURL) { + anURL = _CFURLFromNSURL(anURL); + if (URL_PATH_TYPE(anURL) != FULL_URL_REPRESENTATION) { + // !!! This won't work if we go to putting the vol ref num in the net location for HFS + if (anURL->_base) { + return CFURLCopyNetLocation(anURL->_base); + } else { + CFRetain(kCFURLLocalhost); + return kCFURLLocalhost; + } + } + if (!(anURL->_flags & IS_PARSED)) { + _parseComponentsOfURL(anURL); + } + if (anURL->_flags & NET_LOCATION_MASK) { + // We provide the net location + CFRange netRg = _netLocationRange(anURL->_flags, anURL->ranges); + CFStringRef netLoc; + if (!_haveTestedOriginalString(anURL)) { + computeSanitizedString(anURL); + } + if (!(anURL->_flags & ORIGINAL_AND_URL_STRINGS_MATCH) && (anURL->_flags & (USER_DIFFERS | PASSWORD_DIFFERS | HOST_DIFFERS | PORT_DIFFERS))) { + // Only thing that can come before the net location is the scheme. It's impossible for the scheme to contain percent escapes. Therefore, we can use the location of netRg in _sanatizedString, just not the length. + CFRange netLocEnd; + netRg.length = CFStringGetLength(anURL->_sanatizedString) - netRg.location; + if (CFStringFindWithOptions(anURL->_sanatizedString, CFSTR("/"), netRg, 0, &netLocEnd)) { + netRg.length = netLocEnd.location - netRg.location; + } + netLoc = CFStringCreateWithSubstring(CFGetAllocator(anURL), anURL->_sanatizedString, netRg); + } else { + netLoc = CFStringCreateWithSubstring(CFGetAllocator(anURL), anURL->_string, netRg); + } + return netLoc; + } else if (anURL->_base) { + return CFURLCopyNetLocation(anURL->_base); + } else { + return NULL; + } +} + +// NOTE - if you want an absolute path, you must first get the absolute URL. If you want a file system path, use the file system methods above. +CFStringRef CFURLCopyPath(CFURLRef anURL) { + anURL = _CFURLFromNSURL(anURL); + if (URL_PATH_TYPE(anURL) == kCFURLPOSIXPathStyle && (anURL->_flags & POSIX_AND_URL_PATHS_MATCH)) { + CFRetain(anURL->_string); + return anURL->_string; + } + if (URL_PATH_TYPE(anURL) != FULL_URL_REPRESENTATION) { + _convertToURLRepresentation((struct __CFURL *)anURL); + } + return _retainedComponentString(anURL, HAS_PATH, false, false); +} + +/* NULL if CFURLCanBeDecomposed(anURL) is false; also does not resolve the URL against its base. See also CFCreateAbsoluteURL(). Note that, strictly speaking, any leading '/' is not considered part of the URL's path, although its presence or absence determines whether the path is absolute. CFURLCopyPath()'s return value includes any leading slash (giving the path the normal POSIX appearance); CFURLCopyStrictPath()'s return value omits any leading slash, and uses isAbsolute to report whether the URL's path is absolute. + + CFURLCopyFileSystemPath() returns the URL's path as a file system path for the given path style. All percent escape sequences are replaced. The URL is not resolved against its base before computing the path. +*/ +CFStringRef CFURLCopyStrictPath(CFURLRef anURL, Boolean *isAbsolute) { + CFStringRef path = CFURLCopyPath(anURL); + if (!path || CFStringGetLength(path) == 0) { + if (path) CFRelease(path); + if (isAbsolute) *isAbsolute = false; + return NULL; + } + if (CFStringGetCharacterAtIndex(path, 0) == '/') { + CFStringRef tmp; + if (isAbsolute) *isAbsolute = true; + tmp = CFStringCreateWithSubstring(CFGetAllocator(path), path, CFRangeMake(1, CFStringGetLength(path)-1)); + CFRelease(path); + path = tmp; + } else { + if (isAbsolute) *isAbsolute = false; + } + return path; +} + +Boolean CFURLHasDirectoryPath(CFURLRef anURL) { + __CFGenericValidateType(anURL, __kCFURLTypeID); + if (URL_PATH_TYPE(anURL) == FULL_URL_REPRESENTATION) { + if (!(anURL->_flags & IS_PARSED)) { + _parseComponentsOfURL(anURL); + } + if (!anURL->_base || (anURL->_flags & (HAS_PATH | NET_LOCATION_MASK))) { + return ((anURL->_flags & IS_DIRECTORY) != 0); + } + return CFURLHasDirectoryPath(anURL->_base); + } + return ((anURL->_flags & IS_DIRECTORY) != 0); +} + +static UInt32 _firstResourceSpecifierFlag(UInt32 flags) { + UInt32 firstRsrcSpecFlag = 0; + UInt32 flag = HAS_FRAGMENT; + while (flag != HAS_PATH) { + if (flags & flag) { + firstRsrcSpecFlag = flag; + } + flag = flag >> 1; + } + return firstRsrcSpecFlag; +} + +CFStringRef CFURLCopyResourceSpecifier(CFURLRef anURL) { + anURL = _CFURLFromNSURL(anURL); + __CFGenericValidateType(anURL, __kCFURLTypeID); + if (URL_PATH_TYPE(anURL) != FULL_URL_REPRESENTATION) { + return NULL; + } + if (!(anURL->_flags & IS_PARSED)) { + _parseComponentsOfURL(anURL); + } + if (!(anURL->_flags & IS_DECOMPOSABLE)) { + CFRange schemeRg = _rangeForComponent(anURL->_flags, anURL->ranges, HAS_SCHEME); + CFIndex base = schemeRg.location + schemeRg.length + 1; + if (!_haveTestedOriginalString(anURL)) { + computeSanitizedString(anURL); + } + if (anURL->_sanatizedString) { + // It is impossible to have a percent escape in the scheme (if there were one, we would have considered the URL a relativeURL with a colon in the path instead), so this range computation is always safe. + return CFStringCreateWithSubstring(CFGetAllocator(anURL), anURL->_sanatizedString, CFRangeMake(base, CFStringGetLength(anURL->_sanatizedString)-base)); + } else { + return CFStringCreateWithSubstring(CFGetAllocator(anURL), anURL->_string, CFRangeMake(base, CFStringGetLength(anURL->_string)-base)); + } + } else { + UInt32 firstRsrcSpecFlag = _firstResourceSpecifierFlag(anURL->_flags); + UInt32 flag; + if (firstRsrcSpecFlag) { + Boolean canUseOriginalString = true; + Boolean canUseSanitizedString = true; + CFAllocatorRef alloc = CFGetAllocator(anURL); + if (!_haveTestedOriginalString(anURL)) { + computeSanitizedString(anURL); + } + if (!(anURL->_flags & ORIGINAL_AND_URL_STRINGS_MATCH)) { + // See if any pieces in the resource specifier differ between sanitized string and original string + for (flag = firstRsrcSpecFlag; flag != (HAS_FRAGMENT << 1); flag = flag << 1) { + if (anURL->_flags & (flag << BIT_SHIFT_FROM_COMPONENT_TO_DIFFERS_FLAG)) { + canUseOriginalString = false; + break; + } + } + } + if (!canUseOriginalString) { + // If none of the pieces prior to the first resource specifier flag differ, then we can use the offset from the original string as the offset in the sanitized string. + for (flag = firstRsrcSpecFlag >> 1; flag != 0; flag = flag >> 1) { + if (anURL->_flags & (flag << BIT_SHIFT_FROM_COMPONENT_TO_DIFFERS_FLAG)) { + canUseSanitizedString = false; + break; + } + } + } + if (canUseOriginalString) { + CFRange rg = _rangeForComponent(anURL->_flags, anURL->ranges, firstRsrcSpecFlag); + rg.location --; // Include the character that demarcates the component + rg.length = CFStringGetLength(anURL->_string) - rg.location; + return CFStringCreateWithSubstring(alloc, anURL->_string, rg); + } else if (canUseSanitizedString) { + CFRange rg = _rangeForComponent(anURL->_flags, anURL->ranges, firstRsrcSpecFlag); + rg.location --; // Include the character that demarcates the component + rg.length = CFStringGetLength(anURL->_sanatizedString) - rg.location; + return CFStringCreateWithSubstring(alloc, anURL->_sanatizedString, rg); + } else { + // Must compute the correct string to return; just reparse.... + UInt32 sanFlags = 0; + CFRange *sanRanges = NULL; + CFRange rg; + _parseComponents(alloc, anURL->_sanatizedString, anURL->_base, &sanFlags, &sanRanges); + rg = _rangeForComponent(sanFlags, sanRanges, firstRsrcSpecFlag); + CFAllocatorDeallocate(alloc, sanRanges); + rg.location --; // Include the character that demarcates the component + rg.length = CFStringGetLength(anURL->_sanatizedString) - rg.location; + return CFStringCreateWithSubstring(CFGetAllocator(anURL), anURL->_sanatizedString, rg); + } + } else { + // The resource specifier cannot possibly come from the base. + return NULL; + } + } +} + +/*************************************/ +/* Accessors that create new objects */ +/*************************************/ + +// For the next four methods, it is important to realize that, if a URL supplies any part of the net location (host, user, port, or password), it must supply all of the net location (i.e. none of it comes from its base URL). Also, it is impossible for a URL to be relative, supply none of the net location, and still have its (empty) net location take precedence over its base URL (because there's nothing that precedes the net location except the scheme, and if the URL supplied the scheme, it would be absolute, and there would be no base). +CFStringRef CFURLCopyHostName(CFURLRef anURL) { + CFStringRef tmp; + if (CF_IS_OBJC(__kCFURLTypeID, anURL)) { + CFStringRef (*hostMsg)(const void *, SEL) = (void *)__CFSendObjCMsg; + static SEL s = NULL; if (!s) s = __CFGetObjCSelector("host"); + tmp = hostMsg((const void *)anURL, s); + if (tmp) CFRetain(tmp); + return tmp; + } + __CFGenericValidateType(anURL, __kCFURLTypeID); + if (URL_PATH_TYPE(anURL) != FULL_URL_REPRESENTATION) { + if (anURL->_base) { + return CFURLCopyHostName(anURL->_base); + } else { + CFRetain(kCFURLLocalhost); + return kCFURLLocalhost; + } + } + tmp = _retainedComponentString(anURL, HAS_HOST, true, true); + if (tmp) { + if (anURL->_flags & IS_IPV6_ENCODED) { + // Have to strip off the brackets to get the true hostname. + // Assume that to be legal the first and last characters are brackets! + CFStringRef strippedHost = CFStringCreateWithSubstring(CFGetAllocator(anURL), tmp, CFRangeMake(1, CFStringGetLength(tmp) - 2)); + CFRelease(tmp); + tmp = strippedHost; + } + return tmp; + } else if (anURL->_base && !(anURL->_flags & NET_LOCATION_MASK) && !(anURL->_flags & HAS_SCHEME)) { + return CFURLCopyHostName(anURL->_base); + } else { + return NULL; + } +} + +// Return -1 to indicate no port is specified +SInt32 CFURLGetPortNumber(CFURLRef anURL) { + CFStringRef port; + if (CF_IS_OBJC(__kCFURLTypeID, anURL)) { + CFNumberRef (*portMsg)(const void *, SEL) = (void *)__CFSendObjCMsg; + static SEL s = NULL; if (!s) s = __CFGetObjCSelector("port"); + CFNumberRef cfPort = portMsg((const void *)anURL, s); + SInt32 num; + if (cfPort && CFNumberGetValue(cfPort, kCFNumberSInt32Type, &num)) return num; + return -1; + } + __CFGenericValidateType(anURL, __kCFURLTypeID); + if (URL_PATH_TYPE(anURL) != FULL_URL_REPRESENTATION) { + if (anURL->_base) { + return CFURLGetPortNumber(anURL->_base); + } + return -1; + } + port = _retainedComponentString(anURL, HAS_PORT, true, false); + if (port) { + SInt32 portNum, idx, length = CFStringGetLength(port); + CFStringInlineBuffer buf; + CFStringInitInlineBuffer(port, &buf, CFRangeMake(0, length)); + idx = 0; + if (!__CFStringScanInteger(&buf, NULL, &idx, false, &portNum) || (idx != length)) { + portNum = -1; + } + CFRelease(port); + return portNum; + } else if (anURL->_base && !(anURL->_flags & NET_LOCATION_MASK) && !(anURL->_flags & HAS_SCHEME)) { + return CFURLGetPortNumber(anURL->_base); + } else { + return -1; + } +} + +CFStringRef CFURLCopyUserName(CFURLRef anURL) { + CFStringRef user; + if (CF_IS_OBJC(__kCFURLTypeID, anURL)) { + CFStringRef (*userMsg)(const void *, SEL) = (void *)__CFSendObjCMsg; + static SEL s = NULL; if (!s) s = __CFGetObjCSelector("user"); + user = userMsg((const void *)anURL, s); + if (user) CFRetain(user); + return user; + } + __CFGenericValidateType(anURL, __kCFURLTypeID); + if (URL_PATH_TYPE(anURL) != FULL_URL_REPRESENTATION) { + if (anURL->_base) { + return CFURLCopyUserName(anURL->_base); + } + return NULL; + } + user = _retainedComponentString(anURL, HAS_USER, true, true); + if (user) { + return user; + } else if (anURL->_base && !(anURL->_flags & NET_LOCATION_MASK) && !(anURL->_flags & HAS_SCHEME)) { + return CFURLCopyUserName(anURL->_base); + } else { + return NULL; + } +} + +CFStringRef CFURLCopyPassword(CFURLRef anURL) { + CFStringRef passwd; + if (CF_IS_OBJC(__kCFURLTypeID, anURL)) { + CFStringRef (*passwordMsg)(const void *, SEL) = (void *)__CFSendObjCMsg; + static SEL s = NULL; if (!s) s = __CFGetObjCSelector("password"); + passwd = passwordMsg((const void *)anURL, s); + if (passwd) CFRetain(passwd); + return passwd; + } + __CFGenericValidateType(anURL, __kCFURLTypeID); + if (URL_PATH_TYPE(anURL) != FULL_URL_REPRESENTATION) { + if (anURL->_base) { + return CFURLCopyPassword(anURL->_base); + } + return NULL; + } + passwd = _retainedComponentString(anURL, HAS_PASSWORD, true, true); + if (passwd) { + return passwd; + } else if (anURL->_base && !(anURL->_flags & NET_LOCATION_MASK) && !(anURL->_flags & HAS_SCHEME)) { + return CFURLCopyPassword(anURL->_base); + } else { + return NULL; + } +} + +// The NSURL methods do not deal with escaping escape characters at all; therefore, in order to properly bridge NSURL methods, and still provide the escaping behavior that we want, we need to create functions that match the ObjC behavior exactly, and have the public CFURL... functions call these. -- REW, 10/29/98 + +static CFStringRef _unescapedParameterString(CFURLRef anURL) { + CFStringRef str; + if (CF_IS_OBJC(__kCFURLTypeID, anURL)) { + CFStringRef (*paramMsg)(const void *, SEL) = (void *)__CFSendObjCMsg; + static SEL s = NULL; if (!s) s = __CFGetObjCSelector("parameterString"); + str = paramMsg((const void *)anURL, s); + if (str) CFRetain(str); + return str; + } + __CFGenericValidateType(anURL, __kCFURLTypeID); + if (URL_PATH_TYPE(anURL) != FULL_URL_REPRESENTATION) { + return NULL; + } + str = _retainedComponentString(anURL, HAS_PARAMETERS, false, false); + if (str) return str; + if (!(anURL->_flags & IS_DECOMPOSABLE)) return NULL; + if (!anURL->_base || (anURL->_flags & (NET_LOCATION_MASK | HAS_PATH | HAS_SCHEME))) { + return NULL; + // Parameter string definitely coming from the relative portion of the URL + } + return _unescapedParameterString(anURL->_base); +} + +CFStringRef CFURLCopyParameterString(CFURLRef anURL, CFStringRef charactersToLeaveEscaped) { + CFStringRef param = _unescapedParameterString(anURL); + if (param) { + CFStringRef result; + if (anURL->_flags & IS_OLD_UTF8_STYLE || anURL->_encoding == kCFStringEncodingUTF8) { + result = CFURLCreateStringByReplacingPercentEscapes(CFGetAllocator(anURL), param, charactersToLeaveEscaped); + } else { + result = CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFGetAllocator(anURL), param, charactersToLeaveEscaped, anURL->_encoding); + } + CFRelease(param); + return result; + } + return NULL; +} + +static CFStringRef _unescapedQueryString(CFURLRef anURL) { + CFStringRef str; + if (CF_IS_OBJC(__kCFURLTypeID, anURL)) { + CFStringRef (*queryMsg)(const void *, SEL) = (void *)__CFSendObjCMsg; + static SEL s = NULL; if (!s) s = __CFGetObjCSelector("query"); + str = queryMsg((const void *)anURL, s); + if (str) CFRetain(str); + return str; + } + __CFGenericValidateType(anURL, __kCFURLTypeID); + if (URL_PATH_TYPE(anURL) != FULL_URL_REPRESENTATION) { + return NULL; + } + str = _retainedComponentString(anURL, HAS_QUERY, false, false); + if (str) return str; + if (!(anURL->_flags & IS_DECOMPOSABLE)) return NULL; + if (!anURL->_base || (anURL->_flags & (HAS_SCHEME | NET_LOCATION_MASK | HAS_PATH | HAS_PARAMETERS))) { + return NULL; + } + return _unescapedQueryString(anURL->_base); +} + +CFStringRef CFURLCopyQueryString(CFURLRef anURL, CFStringRef charactersToLeaveEscaped) { + CFStringRef query = _unescapedQueryString(anURL); + if (query) { + CFStringRef tmp; + if (anURL->_flags & IS_OLD_UTF8_STYLE || anURL->_encoding == kCFStringEncodingUTF8) { + tmp = CFURLCreateStringByReplacingPercentEscapes(CFGetAllocator(anURL), query, charactersToLeaveEscaped); + } else { + tmp = CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFGetAllocator(anURL), query, charactersToLeaveEscaped, anURL->_encoding); + } + CFRelease(query); + return tmp; + } + return NULL; +} + +// Fragments are NEVER taken from a base URL +static CFStringRef _unescapedFragment(CFURLRef anURL) { + CFStringRef str; + if (CF_IS_OBJC(__kCFURLTypeID, anURL)) { + CFStringRef (*fragmentMsg)(const void *, SEL) = (void *)__CFSendObjCMsg; + static SEL s = NULL; if (!s) s = __CFGetObjCSelector("fragment"); + str = fragmentMsg((const void *)anURL, s); + if (str) CFRetain(str); + return str; + } + __CFGenericValidateType(anURL, __kCFURLTypeID); + if (URL_PATH_TYPE(anURL) != FULL_URL_REPRESENTATION) { + return NULL; + } + str = _retainedComponentString(anURL, HAS_FRAGMENT, false, false); + return str; +} + +CFStringRef CFURLCopyFragment(CFURLRef anURL, CFStringRef charactersToLeaveEscaped) { + CFStringRef fragment = _unescapedFragment(anURL); + if (fragment) { + CFStringRef tmp; + if (anURL->_flags & IS_OLD_UTF8_STYLE || anURL->_encoding == kCFStringEncodingUTF8) { + tmp = CFURLCreateStringByReplacingPercentEscapes(CFGetAllocator(anURL), fragment, charactersToLeaveEscaped); + } else { + tmp = CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFGetAllocator(anURL), fragment, charactersToLeaveEscaped, anURL->_encoding); + } + CFRelease(fragment); + return tmp; + } + return NULL; +} + +static CFIndex insertionLocationForMask(CFURLRef url, CFOptionFlags mask) { + CFIndex firstMaskFlag = 1; + CFIndex lastComponentBeforeMask = 0; + while (firstMaskFlag <= HAS_FRAGMENT) { + if (firstMaskFlag & mask) break; + if (url->_flags & firstMaskFlag) lastComponentBeforeMask = firstMaskFlag; + firstMaskFlag = firstMaskFlag << 1; + } + if (lastComponentBeforeMask == 0) { + // mask includes HAS_SCHEME + return 0; + } else if (lastComponentBeforeMask == HAS_SCHEME) { + // Do not have to worry about the non-decomposable case here. + return _rangeForComponent(url->_flags, url->ranges, HAS_SCHEME).length + 3; + } else { + // For all other components, the separator precedes the component, so there's no need + // to add extra chars to get to the next insertion point + CFRange rg = _rangeForComponent(url->_flags, url->ranges, lastComponentBeforeMask); + return rg.location + rg.length; + } +} + +static CFRange _CFURLGetCharRangeForMask(CFURLRef url, CFOptionFlags mask, CFRange *charRangeWithSeparators) { + CFOptionFlags currentOption; + CFOptionFlags firstMaskFlag = HAS_SCHEME; + Boolean haveReachedMask = false; + CFIndex beforeMask = 0; + CFIndex afterMask = kCFNotFound; + CFRange *currRange = url->ranges; + CFRange maskRange = {kCFNotFound, 0}; + for (currentOption = 1; currentOption <= HAS_FRAGMENT; currentOption = currentOption << 1) { + if (!haveReachedMask && (currentOption & mask) != 0) { + firstMaskFlag = currentOption; + haveReachedMask = true; + } + if (!(url->_flags & currentOption)) continue; + if (!haveReachedMask) { + beforeMask = currRange->location + currRange->length; + } else if (currentOption <= mask) { + if (maskRange.location == kCFNotFound) { + maskRange = *currRange; + } else { + maskRange.length = currRange->location + currRange->length - maskRange.location; + } + } else { + afterMask = currRange->location; + break; + } + currRange ++; + } + if (afterMask == kCFNotFound) { + afterMask = maskRange.location + maskRange.length; + } + charRangeWithSeparators->location = beforeMask; + charRangeWithSeparators->length = afterMask - beforeMask; + return maskRange; +} + +static CFRange _getCharRangeInDecomposableURL(CFURLRef url, CFURLComponentType component, CFRange *rangeIncludingSeparators) { + CFOptionFlags mask; + switch (component) { + case kCFURLComponentScheme: + mask = HAS_SCHEME; + break; + case kCFURLComponentNetLocation: + mask = NET_LOCATION_MASK; + break; + case kCFURLComponentPath: + mask = HAS_PATH; + break; + case kCFURLComponentResourceSpecifier: + mask = RESOURCE_SPECIFIER_MASK; + break; + case kCFURLComponentUser: + mask = HAS_USER; + break; + case kCFURLComponentPassword: + mask = HAS_PASSWORD; + break; + case kCFURLComponentUserInfo: + mask = HAS_USER | HAS_PASSWORD; + break; + case kCFURLComponentHost: + mask = HAS_HOST; + break; + case kCFURLComponentPort: + mask = HAS_PORT; + break; + case kCFURLComponentParameterString: + mask = HAS_PARAMETERS; + break; + case kCFURLComponentQuery: + mask = HAS_QUERY; + break; + case kCFURLComponentFragment: + mask = HAS_FRAGMENT; + break; + default: + rangeIncludingSeparators->location = kCFNotFound; + rangeIncludingSeparators->length = 0; + return CFRangeMake(kCFNotFound, 0); + } + + if ((url->_flags & mask) == 0) { + rangeIncludingSeparators->location = insertionLocationForMask(url, mask); + rangeIncludingSeparators->length = 0; + return CFRangeMake(kCFNotFound, 0); + } else { + return _CFURLGetCharRangeForMask(url, mask, rangeIncludingSeparators); + } +} + +static CFRange _getCharRangeInNonDecomposableURL(CFURLRef url, CFURLComponentType component, CFRange *rangeIncludingSeparators) { + if (component == kCFURLComponentScheme) { + CFRange schemeRg = _rangeForComponent(url->_flags, url->ranges, HAS_SCHEME); + rangeIncludingSeparators->location = 0; + rangeIncludingSeparators->length = schemeRg.length + 1; + return schemeRg; + } else if (component == kCFURLComponentResourceSpecifier) { + CFRange schemeRg = _rangeForComponent(url->_flags, url->ranges, HAS_SCHEME); + CFIndex stringLength = CFStringGetLength(url->_string); + if (schemeRg.length + 1 == stringLength) { + rangeIncludingSeparators->location = schemeRg.length + 1; + rangeIncludingSeparators->length = 0; + return CFRangeMake(kCFNotFound, 0); + } else { + rangeIncludingSeparators->location = schemeRg.length; + rangeIncludingSeparators->length = stringLength - schemeRg.length; + return CFRangeMake(schemeRg.length + 1, rangeIncludingSeparators->length - 1); + } + } else { + rangeIncludingSeparators->location = kCFNotFound; + rangeIncludingSeparators->length = 0; + return CFRangeMake(kCFNotFound, 0); + } + +} + +CFRange CFURLGetByteRangeForComponent(CFURLRef url, CFURLComponentType component, CFRange *rangeIncludingSeparators) { + CFRange charRange, charRangeWithSeparators; + CFRange byteRange; + CFAssert2(component > 0 && component < 13, __kCFLogAssertion, "%s(): passed invalid component %d", __PRETTY_FUNCTION__, component); + url = _CFURLFromNSURL(url); + if (!(url->_flags & IS_PARSED)) { + _parseComponentsOfURL(url); + } + + if (!(url->_flags & IS_DECOMPOSABLE)) { + // Special-case this because non-decomposable URLs have a slightly strange flags setup + charRange = _getCharRangeInNonDecomposableURL(url, component, &charRangeWithSeparators); + } else { + charRange = _getCharRangeInDecomposableURL(url, component, &charRangeWithSeparators); + } + + if (charRangeWithSeparators.location == kCFNotFound) { + if (rangeIncludingSeparators) { + rangeIncludingSeparators->location = kCFNotFound; + rangeIncludingSeparators->length = 0; + } + return CFRangeMake(kCFNotFound, 0); + } else if (rangeIncludingSeparators) { + CFStringGetBytes(url->_string, CFRangeMake(0, charRangeWithSeparators.location), url->_encoding, 0, false, NULL, 0, &(rangeIncludingSeparators->location)); + + if (charRange.location == kCFNotFound) { + byteRange = charRange; + CFStringGetBytes(url->_string, charRangeWithSeparators, url->_encoding, 0, false, NULL, 0, &(rangeIncludingSeparators->length)); + } else { + CFIndex maxCharRange = charRange.location + charRange.length; + CFIndex maxCharRangeWithSeparators = charRangeWithSeparators.location + charRangeWithSeparators.length; + + if (charRangeWithSeparators.location == charRange.location) { + byteRange.location = rangeIncludingSeparators->location; + } else { + CFIndex numBytes; + CFStringGetBytes(url->_string, CFRangeMake(charRangeWithSeparators.location, charRange.location - charRangeWithSeparators.location), url->_encoding, 0, false, NULL, 0, &numBytes); + byteRange.location = charRangeWithSeparators.location + numBytes; + } + CFStringGetBytes(url->_string, charRange, url->_encoding, 0, false, NULL, 0, &(byteRange.length)); + if (maxCharRangeWithSeparators == maxCharRange) { + rangeIncludingSeparators->length = byteRange.location + byteRange.length - rangeIncludingSeparators->location; + } else { + CFIndex numBytes; + CFRange rg; + rg.location = maxCharRange; + rg.length = maxCharRangeWithSeparators - rg.location; + CFStringGetBytes(url->_string, rg, url->_encoding, 0, false, NULL, 0, &numBytes); + rangeIncludingSeparators->length = byteRange.location + byteRange.length + numBytes - rangeIncludingSeparators->location; + } + } + } else if (charRange.location == kCFNotFound) { + byteRange = charRange; + } else { + CFStringGetBytes(url->_string, CFRangeMake(0, charRange.location), url->_encoding, 0, false, NULL, 0, &(byteRange.location)); + CFStringGetBytes(url->_string, charRange, url->_encoding, 0, false, NULL, 0, &(byteRange.length)); + } + return byteRange; +} + +/* Component support */ + +/* We convert to the CFURL immediately at the beginning of decomposition, so all the decomposition routines need not worry about having an ObjC NSURL */ +static CFStringRef schemeSpecificString(CFURLRef url) { + Boolean isDir; + isDir = ((url->_flags & IS_DIRECTORY) != 0); + switch (URL_PATH_TYPE(url)) { + case kCFURLPOSIXPathStyle: + if (url->_flags & POSIX_AND_URL_PATHS_MATCH) { + return CFRetain(url->_string); + } else { + return POSIXPathToURLPath(url->_string, CFGetAllocator(url), isDir); + } + case kCFURLHFSPathStyle: + return HFSPathToURLPath(url->_string, CFGetAllocator(url), isDir); + case kCFURLWindowsPathStyle: + return WindowsPathToURLPath(url->_string, CFGetAllocator(url), isDir); + case FULL_URL_REPRESENTATION: + return CFURLCopyResourceSpecifier(url); + default: + return NULL; + } +} + +static Boolean decomposeToNonHierarchical(CFURLRef url, CFURLComponentsNonHierarchical *components) { + if (CFURLGetBaseURL(url) != NULL) { + components->scheme = NULL; + } else { + components->scheme = CFURLCopyScheme(url); + } + components->schemeSpecific = schemeSpecificString(url); + return true; +} + +static CFURLRef composeFromNonHierarchical(CFAllocatorRef alloc, const CFURLComponentsNonHierarchical *components) { + CFStringRef str; + if (components->scheme) { + UniChar ch = ':'; + str = CFStringCreateMutableCopy(alloc, CFStringGetLength(components->scheme) + 1 + (components->schemeSpecific ? CFStringGetLength(components->schemeSpecific): 0), components->scheme); + CFStringAppendCharacters((CFMutableStringRef)str, &ch, 1); + if (components->schemeSpecific) CFStringAppend((CFMutableStringRef)str, components->schemeSpecific); + } else if (components->schemeSpecific) { + str = components->schemeSpecific; + CFRetain(str); + } else { + str = NULL; + } + if (str) { + CFURLRef url = CFURLCreateWithString(alloc, str, NULL); + CFRelease(str); + return url; + } else { + return NULL; + } +} + +static Boolean decomposeToRFC1808(CFURLRef url, CFURLComponentsRFC1808 *components) { + CFAllocatorRef alloc = CFGetAllocator(url); + int pathType; + static CFStringRef emptyStr = NULL; + if (!emptyStr) { + emptyStr = CFSTR(""); + } + + if (!CFURLCanBeDecomposed(url)) { + return false; + } + if ((pathType = URL_PATH_TYPE(url)) == FULL_URL_REPRESENTATION) { + CFStringRef path = CFURLCopyPath(url); + if (path) { + components->pathComponents = CFStringCreateArrayBySeparatingStrings(alloc, path, CFSTR("/")); + CFRelease(path); + } else { + components->pathComponents = NULL; + } + components->baseURL = CFURLGetBaseURL(url); + if (components->baseURL) { + CFRetain(components->baseURL); + components->scheme = NULL; + } else { + components->scheme = _retainedComponentString(url, HAS_SCHEME, true, false); + } + components->user = _retainedComponentString(url, HAS_USER, false, false); + components->password = _retainedComponentString(url, HAS_PASSWORD, false, false); + components->host = _retainedComponentString(url, HAS_HOST, false, false); + if (url->_flags & HAS_PORT) { + components->port = CFURLGetPortNumber(url); + } else { + components->port = kCFNotFound; + } + components->parameterString = _retainedComponentString(url, HAS_PARAMETERS, false, false); + components->query = _retainedComponentString(url, HAS_QUERY, false, false); + components->fragment = _retainedComponentString(url, HAS_FRAGMENT, false, false); + } else { + switch (pathType) { + case kCFURLPOSIXPathStyle: { + CFStringRef pathStr; + if (url->_flags & POSIX_AND_URL_PATHS_MATCH) { + pathStr = url->_string; + CFRetain(pathStr); + } else { + pathStr = POSIXPathToURLPath(url->_string, alloc, url->_flags & IS_DIRECTORY); + } + components->pathComponents = CFStringCreateArrayBySeparatingStrings(alloc, pathStr, CFSTR("/")); + CFRelease(pathStr); + break; + } + case kCFURLHFSPathStyle: + components->pathComponents = HFSPathToURLComponents(url->_string, alloc, ((url->_flags & IS_DIRECTORY) != 0)); + break; + case kCFURLWindowsPathStyle: + components->pathComponents = WindowsPathToURLComponents(url->_string, alloc, ((url->_flags & IS_DIRECTORY) != 0)); + break; + default: + components->pathComponents = NULL; + } + if (!components->pathComponents) { + return false; + } + components->scheme = CFRetain(kCFURLFileScheme); + components->user = NULL; + components->password = NULL; + components->host = CFRetain(kCFURLLocalhost); + components->port = kCFNotFound; + components->parameterString = NULL; + components->query = NULL; + components->fragment = NULL; + components->baseURL = CFURLGetBaseURL(url); + if (components->baseURL) CFRetain(components->baseURL); + } + return true; +} + +static CFURLRef composeFromRFC1808(CFAllocatorRef alloc, const CFURLComponentsRFC1808 *comp) { + CFMutableStringRef urlString = CFStringCreateMutable(alloc, 0); + CFURLRef base = comp->baseURL; + CFURLRef url; + Boolean hadPrePathComponent = false; + if (comp->scheme) { + base = NULL; + CFStringAppend(urlString, comp->scheme); + CFStringAppend(urlString, CFSTR("://")); + hadPrePathComponent = true; + } + if (comp->user || comp->password) { + if (comp->user) { + CFStringAppend(urlString, comp->user); + } + if (comp->password) { + CFStringAppend(urlString, CFSTR(":")); + CFStringAppend(urlString, comp->password); + } + CFStringAppend(urlString, CFSTR("@")); + hadPrePathComponent = true; + } + if (comp->host) { + CFStringAppend(urlString, comp->host); + if (comp->port != kCFNotFound) { + CFStringAppendFormat(urlString, NULL, CFSTR(":%d"), comp->port); + } + hadPrePathComponent = true; + } + if (hadPrePathComponent && (comp->pathComponents == NULL || CFStringGetLength(CFArrayGetValueAtIndex(comp->pathComponents, 0)) != 0)) { + CFStringAppend(urlString, CFSTR("/")); + } + if (comp->pathComponents) { + CFStringRef pathStr = CFStringCreateByCombiningStrings(alloc, comp->pathComponents, CFSTR("/")); + CFStringAppend(urlString, pathStr); + CFRelease(pathStr); + } + if (comp->parameterString) { + CFStringAppend(urlString, CFSTR(";")); + CFStringAppend(urlString, comp->parameterString); + } + if (comp->query) { + CFStringAppend(urlString, CFSTR("?")); + CFStringAppend(urlString, comp->query); + } + if (comp->fragment) { + CFStringAppend(urlString, CFSTR("#")); + CFStringAppend(urlString, comp->fragment); + } + url = CFURLCreateWithString(alloc, urlString, base); + CFRelease(urlString); + return url; +} + +static Boolean decomposeToRFC2396(CFURLRef url, CFURLComponentsRFC2396 *comp) { + CFAllocatorRef alloc = CFGetAllocator(url); + CFURLComponentsRFC1808 oldComp; + CFStringRef tmpStr; + if (!decomposeToRFC1808(url, &oldComp)) { + return false; + } + comp->scheme = oldComp.scheme; + if (oldComp.user) { + if (oldComp.password) { + comp->userinfo = CFStringCreateWithFormat(alloc, NULL, CFSTR("%@:%@"), oldComp.user, oldComp.password); + CFRelease(oldComp.password); + CFRelease(oldComp.user); + } else { + comp->userinfo = oldComp.user; + } + } else { + comp->userinfo = NULL; + } + comp->host = oldComp.host; + comp->port = oldComp.port; + if (!oldComp.parameterString) { + comp->pathComponents = oldComp.pathComponents; + } else { + int length = CFArrayGetCount(oldComp.pathComponents); + comp->pathComponents = CFArrayCreateMutableCopy(alloc, length, oldComp.pathComponents); + tmpStr = CFStringCreateWithFormat(alloc, NULL, CFSTR("%@;%@"), CFArrayGetValueAtIndex(comp->pathComponents, length - 1), oldComp.parameterString); + CFArraySetValueAtIndex((CFMutableArrayRef)comp->pathComponents, length - 1, tmpStr); + CFRelease(tmpStr); + CFRelease(oldComp.pathComponents); + CFRelease(oldComp.parameterString); + } + comp->query = oldComp.query; + comp->fragment = oldComp.fragment; + comp->baseURL = oldComp.baseURL; + return true; +} + +static CFURLRef composeFromRFC2396(CFAllocatorRef alloc, const CFURLComponentsRFC2396 *comp) { + CFMutableStringRef urlString = CFStringCreateMutable(alloc, 0); + CFURLRef base = comp->baseURL; + CFURLRef url; + Boolean hadPrePathComponent = false; + if (comp->scheme) { + base = NULL; + CFStringAppend(urlString, comp->scheme); + CFStringAppend(urlString, CFSTR("://")); + hadPrePathComponent = true; + } + if (comp->userinfo) { + CFStringAppend(urlString, comp->userinfo); + CFStringAppend(urlString, CFSTR("@")); + hadPrePathComponent = true; + } + if (comp->host) { + CFStringAppend(urlString, comp->host); + if (comp->port != kCFNotFound) { + CFStringAppendFormat(urlString, NULL, CFSTR(":%d"), comp->port); + } + hadPrePathComponent = true; + } + if (hadPrePathComponent && (comp->pathComponents == NULL || CFStringGetLength(CFArrayGetValueAtIndex(comp->pathComponents, 0)) != 0)) { + CFStringAppend(urlString, CFSTR("/")); + } + if (comp->pathComponents) { + CFStringRef pathStr = CFStringCreateByCombiningStrings(alloc, comp->pathComponents, CFSTR("/")); + CFStringAppend(urlString, pathStr); + CFRelease(pathStr); + } + if (comp->query) { + CFStringAppend(urlString, CFSTR("?")); + CFStringAppend(urlString, comp->query); + } + if (comp->fragment) { + CFStringAppend(urlString, CFSTR("#")); + CFStringAppend(urlString, comp->fragment); + } + url = CFURLCreateWithString(alloc, urlString, base); + CFRelease(urlString); + return url; +} + +#undef CFURLCopyComponents +#undef CFURLCreateFromComponents +CF_EXPORT +Boolean _CFURLCopyComponents(CFURLRef url, CFURLComponentDecomposition decompositionType, void *components) { + url = _CFURLFromNSURL(url); + switch (decompositionType) { + case kCFURLComponentDecompositionNonHierarchical: + return decomposeToNonHierarchical(url, (CFURLComponentsNonHierarchical *)components); + case kCFURLComponentDecompositionRFC1808: + return decomposeToRFC1808(url, (CFURLComponentsRFC1808 *)components); + case kCFURLComponentDecompositionRFC2396: + return decomposeToRFC2396(url, (CFURLComponentsRFC2396 *)components); + default: + return false; + } +} + +CF_EXPORT +CFURLRef _CFURLCreateFromComponents(CFAllocatorRef alloc, CFURLComponentDecomposition decompositionType, const void *components) { + switch (decompositionType) { + case kCFURLComponentDecompositionNonHierarchical: + return composeFromNonHierarchical(alloc, (const CFURLComponentsNonHierarchical *)components); + case kCFURLComponentDecompositionRFC1808: + return composeFromRFC1808(alloc, (const CFURLComponentsRFC1808 *)components); + case kCFURLComponentDecompositionRFC2396: + return composeFromRFC2396(alloc, (const CFURLComponentsRFC2396 *)components); + default: + return NULL; + } +} + +CF_EXPORT void *__CFURLReservedPtr(CFURLRef url) { + return url->_reserved; +} + +CF_EXPORT void __CFURLSetReservedPtr(CFURLRef url, void *ptr) { + ((struct __CFURL *)url)->_reserved = ptr; +} + + +/* File system stuff */ + +/* HFSPath<->URLPath functions at the bottom of the file */ +static CFArrayRef WindowsPathToURLComponents(CFStringRef path, CFAllocatorRef alloc, Boolean isDir) { + CFArrayRef tmp; + CFMutableArrayRef urlComponents = NULL; + CFStringRef str; + UInt32 i=0; + + tmp = CFStringCreateArrayBySeparatingStrings(alloc, path, CFSTR("\\")); + urlComponents = CFArrayCreateMutableCopy(alloc, 0, tmp); + CFRelease(tmp); +/* We must not replace ".:" with ".|" on WIN32. + * (Sergey Zubarev) + */ +#if !defined(__WIN32__) + str = CFArrayGetValueAtIndex(urlComponents, 0); + if (CFStringGetLength(str) == 2 && CFStringGetCharacterAtIndex(str, 1) == ':') { + CFStringRef newComponent = CFStringCreateWithFormat(alloc, NULL, CFSTR("%c|"), CFStringGetCharacterAtIndex(str, 0)); + CFArraySetValueAtIndex(urlComponents, 0, newComponent); + CFRelease(newComponent); + CFArrayInsertValueAtIndex(urlComponents, 0, CFSTR("")); // So we get a leading '/' below + i = 2; // Skip over the drive letter and the empty string we just inserted + } +#endif // __WIN32__ +#if defined(__WIN32__) + // cjk: should this be done on all platforms? + int c; + for (c = CFArrayGetCount(urlComponents); i < c; i ++) { + CFStringRef fileComp = CFArrayGetValueAtIndex(urlComponents,i); + CFStringRef urlComp = _replacePathIllegalCharacters(fileComp, alloc, false); + if (!urlComp) { + // Couldn't decode fileComp + CFRelease(urlComponents); + return NULL; + } + if (urlComp != fileComp) { + CFArraySetValueAtIndex(urlComponents, i, urlComp); + } + CFRelease(urlComp); + } +#endif + if (isDir) { + if (CFStringGetLength(CFArrayGetValueAtIndex(urlComponents, CFArrayGetCount(urlComponents) - 1)) != 0) + CFArrayAppendValue(urlComponents, CFSTR("")); + } + return urlComponents; +} + +static CFStringRef WindowsPathToURLPath(CFStringRef path, CFAllocatorRef alloc, Boolean isDir) { + CFArrayRef urlComponents; + CFArrayRef newComponents; + CFStringRef str; + + if (CFStringGetLength(path) == 0) return CFStringCreateWithCString(alloc, "", kCFStringEncodingASCII); + urlComponents = WindowsPathToURLComponents(path, alloc, isDir); + if (!urlComponents) return CFStringCreateWithCString(alloc, "", kCFStringEncodingASCII); + + newComponents = copyStringArrayWithTransformation(urlComponents, escapeWindowsPathComponent); + if (newComponents) { + str = CFStringCreateByCombiningStrings(alloc, newComponents, CFSTR("/")); + CFRelease(newComponents); + } else { + str = CFStringCreateWithCString(alloc, "", kCFStringEncodingASCII); + } + CFRelease(urlComponents); + return str; +} + +static CFStringRef POSIXPathToURLPath(CFStringRef path, CFAllocatorRef alloc, Boolean isDirectory) { + CFStringRef pathString = _replacePathIllegalCharacters(path, alloc, true); + if (isDirectory && CFStringGetCharacterAtIndex(path, CFStringGetLength(path)-1) != '/') { + CFStringRef tmp = CFStringCreateWithFormat(alloc, NULL, CFSTR("%@/"), pathString); + CFRelease(pathString); + pathString = tmp; + } + return pathString; +} + +static CFStringRef URLPathToPOSIXPath(CFStringRef path, CFAllocatorRef allocator, CFStringEncoding encoding) { + // This is the easiest case; just remove the percent escape codes and we're done + CFStringRef result = CFURLCreateStringByReplacingPercentEscapesUsingEncoding(allocator, path, CFSTR(""), encoding); + if (result) { + CFIndex length = CFStringGetLength(result); + if (length > 1 && CFStringGetCharacterAtIndex(result, length-1) == '/') { + CFStringRef tmp = CFStringCreateWithSubstring(allocator, result, CFRangeMake(0, length-1)); + CFRelease(result); + result = tmp; + } + } + return result; +} + + +static CFStringRef URLPathToWindowsPath(CFStringRef path, CFAllocatorRef allocator, CFStringEncoding encoding) { + // Check for a drive letter, then flip all the slashes + CFStringRef result; + CFArrayRef tmp = CFStringCreateArrayBySeparatingStrings(allocator, path, CFSTR("/")); + SInt32 count = CFArrayGetCount(tmp); + CFMutableArrayRef components = CFArrayCreateMutableCopy(allocator, count, tmp); + CFStringRef newPath; + + CFRelease(tmp); + if (CFStringGetLength(CFArrayGetValueAtIndex(components,count-1)) == 0) { + CFArrayRemoveValueAtIndex(components, count-1); + count --; + } + if (count > 1 && CFStringGetLength(CFArrayGetValueAtIndex(components, 0)) == 0) { + // Absolute path; we need to remove the first component, and check for a drive letter in the second component + CFStringRef firstComponent = CFURLCreateStringByReplacingPercentEscapesUsingEncoding(allocator, CFArrayGetValueAtIndex(components, 1), CFSTR(""), encoding); + CFArrayRemoveValueAtIndex(components, 0); + if (CFStringGetLength(firstComponent) == 2 && CFStringGetCharacterAtIndex(firstComponent, 1) == '|') { + // Drive letter + CFStringRef driveStr = CFStringCreateWithFormat(allocator, NULL, CFSTR("%c:"), CFStringGetCharacterAtIndex(firstComponent, 0)); + CFArraySetValueAtIndex(components, 0, driveStr); + CFRelease(driveStr); + } + CFRelease(firstComponent); + } + + newPath = CFStringCreateByCombiningStrings(allocator, components, CFSTR("\\")); + CFRelease(components); + result = CFURLCreateStringByReplacingPercentEscapesUsingEncoding(allocator, newPath, CFSTR(""), encoding); + CFRelease(newPath); + return result; +} + +// converts url from a file system path representation to a standard representation +static void _convertToURLRepresentation(struct __CFURL *url) { + CFStringRef path = NULL; + Boolean isDir = ((url->_flags & IS_DIRECTORY) != 0); + CFAllocatorRef alloc = CFGetAllocator(url); + +#if DEBUG_URL_MEMORY_USAGE + numFileURLsConverted ++; +#endif + + switch (URL_PATH_TYPE(url)) { + case kCFURLPOSIXPathStyle: + if (url->_flags & POSIX_AND_URL_PATHS_MATCH) { + path = CFRetain(url->_string); + } else { + path = POSIXPathToURLPath(url->_string, alloc, isDir); + } + break; + case kCFURLHFSPathStyle: + path = HFSPathToURLPath(url->_string, alloc, isDir); + break; + case kCFURLWindowsPathStyle: + path = WindowsPathToURLPath(url->_string, alloc, isDir); + break; + } + CFAssert2(path != NULL, __kCFLogAssertion, "%s(): Encountered malformed file system URL %@", __PRETTY_FUNCTION__, url); + if (!url->_base) { + CFStringRef str; + str = CFStringCreateWithFormat(alloc, NULL, CFSTR("file://localhost%@"), path); + url->_flags = (url->_flags & (IS_DIRECTORY)) | (FULL_URL_REPRESENTATION << 16) | IS_DECOMPOSABLE | IS_ABSOLUTE | IS_PARSED | HAS_SCHEME | HAS_HOST | HAS_PATH | ORIGINAL_AND_URL_STRINGS_MATCH; + CFRelease(url->_string); + url->_string = str; + url->ranges = (CFRange *)CFAllocatorAllocate(alloc, sizeof(CFRange) * 3, 0); + url->ranges[0] = CFRangeMake(0, 4); + url->ranges[1] = CFRangeMake(7, 9); + url->ranges[2] = CFRangeMake(16, CFStringGetLength(path)); + CFRelease(path); + } else { + CFRelease(url->_string); + url->_flags = (url->_flags & (IS_DIRECTORY)) | (FULL_URL_REPRESENTATION << 16) | IS_DECOMPOSABLE | IS_PARSED | HAS_PATH | ORIGINAL_AND_URL_STRINGS_MATCH; + url->_string = path; + url->ranges = (CFRange *)CFAllocatorAllocate(alloc, sizeof(CFRange), 0); + *(url->ranges) = CFRangeMake(0, CFStringGetLength(path)); + } +} + +// relativeURL is known to be a file system URL whose base is a matching file system URL +static CFURLRef _CFURLCopyAbsoluteFileURL(CFURLRef relativeURL) { + CFAllocatorRef alloc = CFGetAllocator(relativeURL); + CFURLPathStyle fsType = URL_PATH_TYPE(relativeURL); + CFURLRef base = relativeURL->_base; + CFStringRef newPath = _resolveFileSystemPaths(relativeURL->_string, base->_string, (base->_flags & IS_DIRECTORY) != 0, fsType, alloc); + CFURLRef result = CFURLCreateWithFileSystemPath(alloc, newPath, fsType, (relativeURL->_flags & IS_DIRECTORY) != 0); + CFRelease(newPath); + return result; +} + +// Caller must release the returned string +static CFStringRef _resolveFileSystemPaths(CFStringRef relativePath, CFStringRef basePath, Boolean baseIsDir, CFURLPathStyle fsType, CFAllocatorRef alloc) { + CFIndex baseLen = CFStringGetLength(basePath); + CFIndex relLen = CFStringGetLength(relativePath); + UniChar pathDelimiter = PATH_DELIM_FOR_TYPE(fsType); + UniChar *buf = CFAllocatorAllocate(alloc, sizeof(UniChar)*(relLen + baseLen + 2), 0); + CFStringGetCharacters(basePath, CFRangeMake(0, baseLen), buf); + if (baseIsDir) { + if (buf[baseLen-1] != pathDelimiter) { + buf[baseLen] = pathDelimiter; + baseLen ++; + } + } else { + UniChar *ptr = buf + baseLen - 1; + while (ptr > buf && *ptr != pathDelimiter) { + ptr --; + } + baseLen = ptr - buf + 1; + } + if (fsType == kCFURLHFSPathStyle) { + // HFS relative paths will begin with a colon, so we must remove the trailing colon from the base path first. + baseLen --; + } + CFStringGetCharacters(relativePath, CFRangeMake(0, relLen), buf + baseLen); + *(buf + baseLen + relLen) = '\0'; + return _resolvedPath(buf, buf + baseLen + relLen, pathDelimiter, false, true, alloc); +} + +CFURLRef _CFURLCreateCurrentDirectoryURL(CFAllocatorRef allocator) { + CFURLRef url = NULL; +#if defined(__MACOS8__) + short vRefNum; + long dirID; + FSSpec fsSpec; + if (HGetVol(NULL, &vRefNum, &dirID) == noErr && FSMakeFSSpec(vRefNum, dirID, NULL, &fsSpec) == noErr) { + url = _CFCreateURLFromFSSpec(allocator, (void *)(&fsSpec), true); + } +#else + uint8_t buf[CFMaxPathSize + 1]; + if (_CFGetCurrentDirectory(buf, CFMaxPathLength)) { + url = CFURLCreateFromFileSystemRepresentation(allocator, buf, strlen(buf), true); + } +#endif + return url; +} + +CFURLRef CFURLCreateWithFileSystemPath(CFAllocatorRef allocator, CFStringRef filePath, CFURLPathStyle fsType, Boolean isDirectory) { + Boolean isAbsolute = true; + CFIndex len = CFStringGetLength(filePath); + CFURLRef baseURL, result; + + CFAssert2(fsType == kCFURLPOSIXPathStyle || fsType == kCFURLHFSPathStyle || fsType == kCFURLWindowsPathStyle, __kCFLogAssertion, "%s(): encountered unknown path style %d", __PRETTY_FUNCTION__, fsType); + CFAssert1(filePath != NULL, __kCFLogAssertion, "%s(): NULL filePath argument not permitted", __PRETTY_FUNCTION__); + + switch(fsType) { + case kCFURLPOSIXPathStyle: + isAbsolute = (len > 0 && CFStringGetCharacterAtIndex(filePath, 0) == '/'); + break; + case kCFURLWindowsPathStyle: + isAbsolute = (len > 3 && CFStringGetCharacterAtIndex(filePath, 1) == ':' && CFStringGetCharacterAtIndex(filePath, 2) == '\\'); + /* Absolute path under Win32 can begin with "\\" + * (Sergey Zubarev) + */ + if (!isAbsolute) isAbsolute = (len > 2 && CFStringGetCharacterAtIndex(filePath, 0) == '\\' && CFStringGetCharacterAtIndex(filePath, 1) == '\\'); + break; + case kCFURLHFSPathStyle: + isAbsolute = (len > 0 && CFStringGetCharacterAtIndex(filePath, 0) != ':'); + break; + } + if (isAbsolute) { + baseURL = NULL; + } else { + baseURL = _CFURLCreateCurrentDirectoryURL(allocator); + } + result = CFURLCreateWithFileSystemPathRelativeToBase(allocator, filePath, fsType, isDirectory, baseURL); + if (baseURL) CFRelease(baseURL); + return result; +} + +CF_EXPORT CFURLRef CFURLCreateWithFileSystemPathRelativeToBase(CFAllocatorRef allocator, CFStringRef filePath, CFURLPathStyle fsType, Boolean isDirectory, CFURLRef baseURL) { + CFURLRef url; + Boolean isAbsolute = true, releaseFilePath = false; + UniChar pathDelim = '\0'; + CFIndex len = CFStringGetLength(filePath); + + CFAssert1(filePath != NULL, __kCFLogAssertion, "%s(): NULL path string not permitted", __PRETTY_FUNCTION__); + CFAssert2(fsType == kCFURLPOSIXPathStyle || fsType == kCFURLHFSPathStyle || fsType == kCFURLWindowsPathStyle, __kCFLogAssertion, "%s(): encountered unknown path style %d", __PRETTY_FUNCTION__, fsType); + + switch(fsType) { + case kCFURLPOSIXPathStyle: + isAbsolute = (len > 0 && CFStringGetCharacterAtIndex(filePath, 0) == '/'); + pathDelim = '/'; + break; + case kCFURLWindowsPathStyle: + isAbsolute = (len > 3 && CFStringGetCharacterAtIndex(filePath, 1) == ':' && CFStringGetCharacterAtIndex(filePath, 2) == '\\'); + /* Absolute path under Win32 can begin with "\\" + * (Sergey Zubarev) + */ + if (!isAbsolute) isAbsolute = (len > 2 && CFStringGetCharacterAtIndex(filePath, 0) == '\\' && CFStringGetCharacterAtIndex(filePath, 1) == '\\'); + pathDelim = '\\'; + break; + case kCFURLHFSPathStyle: + isAbsolute = (len > 0 && CFStringGetCharacterAtIndex(filePath, 0) != ':'); + pathDelim = ':'; + break; + } + if (isAbsolute) { + baseURL = NULL; + } + if (isDirectory && len > 0 && CFStringGetCharacterAtIndex(filePath, len-1) != pathDelim) { + filePath = CFStringCreateWithFormat(allocator, NULL, CFSTR("%@%c"), filePath, pathDelim); + releaseFilePath = true; + } else if (!isDirectory && len > 0 && CFStringGetCharacterAtIndex(filePath, len-1) == pathDelim) { + if (len == 1 || CFStringGetCharacterAtIndex(filePath, len-2) == pathDelim) { + // Override isDirectory + isDirectory = true; + } else { + filePath = CFStringCreateWithSubstring(allocator, filePath, CFRangeMake(0, len-1)); + releaseFilePath = true; + } + } + if (!filePath || CFStringGetLength(filePath) == 0) { + if (releaseFilePath && filePath) CFRelease(filePath); + return NULL; + } + url = _CFURLAlloc(allocator); + _CFURLInit((struct __CFURL *)url, filePath, fsType, baseURL); + if (releaseFilePath) CFRelease(filePath); + if (isDirectory) ((struct __CFURL *)url)->_flags |= IS_DIRECTORY; + if (fsType == kCFURLPOSIXPathStyle) { + // Check if relative path is equivalent to URL representation; this will be true if url->_string contains only characters from the unreserved character set, plus '/' to delimit the path, plus ':', '@', '&', '=', '+', '$', ',' (according to RFC 2396) -- REW, 12/1/2000 + // Per Section 5 of RFC 2396, there's a special problem if a colon apears in the first path segment - in this position, it can be mistaken for the scheme name. Otherwise, it's o.k., and can be safely identified as part of the path. In this one case, we need to prepend "./" to make it clear what's going on.... -- REW, 8/24/2001 + CFStringInlineBuffer buf; + Boolean sawSlash = FALSE; + Boolean mustPrependDotSlash = FALSE; + CFIndex idx, length = CFStringGetLength(url->_string); + CFStringInitInlineBuffer(url->_string, &buf, CFRangeMake(0, length)); + for (idx = 0; idx < length; idx ++) { + UniChar ch = CFStringGetCharacterFromInlineBuffer(&buf, idx); + if (!isPathLegalCharacter(ch)) break; + if (!sawSlash) { + if (ch == '/') { + sawSlash = TRUE; + } else if (ch == ':') { + mustPrependDotSlash = TRUE; + } + } + } + if (idx == length) { + ((struct __CFURL *)url)->_flags |= POSIX_AND_URL_PATHS_MATCH; + } + if (mustPrependDotSlash) { + CFStringRef newString = CFStringCreateWithFormat(allocator, NULL, CFSTR("./%@"), url->_string); + CFRelease(url->_string); + ((struct __CFURL *)url)->_string = newString; + } + } + return url; +} + +CFStringRef CFURLCopyFileSystemPath(CFURLRef anURL, CFURLPathStyle pathStyle) { + CFAssert2(pathStyle == kCFURLPOSIXPathStyle || pathStyle == kCFURLHFSPathStyle || pathStyle == kCFURLWindowsPathStyle, __kCFLogAssertion, "%s(): Encountered unknown path style %d", __PRETTY_FUNCTION__, pathStyle); + return CFURLCreateStringWithFileSystemPath(CFGetAllocator(anURL), anURL, pathStyle, false); +} + +// There is no matching ObjC method for this functionality; because this function sits on top of the CFURL primitives, it's o.k. not to check for the need to dispatch an ObjC method instead, but this means care must be taken that this function never call anything that will result in dereferencing anURL without first checking for an ObjC dispatch. -- REW, 10/29/98 +CFStringRef CFURLCreateStringWithFileSystemPath(CFAllocatorRef allocator, CFURLRef anURL, CFURLPathStyle fsType, Boolean resolveAgainstBase) { + CFURLRef base = resolveAgainstBase ? CFURLGetBaseURL(anURL) : NULL; + CFStringRef basePath = base ? CFURLCreateStringWithFileSystemPath(allocator, base, fsType, false) : NULL; + CFStringRef relPath = NULL; + + if (!CF_IS_OBJC(__kCFURLTypeID, anURL)) { + // We can grope the ivars + CFURLPathStyle myType = URL_PATH_TYPE(anURL); + if (myType == fsType) { + relPath = CFRetain(anURL->_string); + } else if (fsType == kCFURLPOSIXPathStyle && myType == FULL_URL_REPRESENTATION) { + if (!(anURL->_flags & IS_PARSED)) { + _parseComponentsOfURL(anURL); + } + if (anURL->_flags & POSIX_AND_URL_PATHS_MATCH) { + relPath = _retainedComponentString(anURL, HAS_PATH, true, true); + } + } + } + + if (relPath == NULL) { + CFStringRef urlPath = CFURLCopyPath(anURL); + CFStringEncoding enc = (anURL->_flags & IS_OLD_UTF8_STYLE) ? kCFStringEncodingUTF8 : anURL->_encoding; + if (urlPath) { + switch (fsType) { + case kCFURLPOSIXPathStyle: + relPath = URLPathToPOSIXPath(urlPath, allocator, enc); + break; + case kCFURLHFSPathStyle: + relPath = URLPathToHFSPath(urlPath, allocator, enc); + break; + case kCFURLWindowsPathStyle: + relPath = URLPathToWindowsPath(urlPath, allocator, enc); + break; + default: + CFAssert2(true, __kCFLogAssertion, "%s(): Received unknown path type %d", __PRETTY_FUNCTION__, fsType); + } + CFRelease(urlPath); + } + } + if (relPath && CFURLHasDirectoryPath(anURL) && CFStringGetLength(relPath) > 1 && CFStringGetCharacterAtIndex(relPath, CFStringGetLength(relPath)-1) == PATH_DELIM_FOR_TYPE(fsType)) { + CFStringRef tmp = CFStringCreateWithSubstring(allocator, relPath, CFRangeMake(0, CFStringGetLength(relPath)-1)); + CFRelease(relPath); + relPath = tmp; + } + + // Note that !resolveAgainstBase implies !base + if (!basePath || !relPath) { + return relPath; + } else { + CFStringRef result = _resolveFileSystemPaths(relPath, basePath, CFURLHasDirectoryPath(base), fsType, allocator); + CFRelease(basePath); + CFRelease(relPath); + return result; + } +} + +Boolean CFURLGetFileSystemRepresentation(CFURLRef url, Boolean resolveAgainstBase, uint8_t *buffer, CFIndex bufLen) { + CFStringRef path; + CFAllocatorRef alloc = CFGetAllocator(url); + + if (!url) return false; +#if defined(__WIN32__) + path = CFURLCreateStringWithFileSystemPath(alloc, url, kCFURLWindowsPathStyle, resolveAgainstBase); +#elif defined(__MACOS8__) + path = CFURLCreateStringWithFileSystemPath(alloc, url, kCFURLHFSPathStyle, resolveAgainstBase); +#else + path = CFURLCreateStringWithFileSystemPath(alloc, url, kCFURLPOSIXPathStyle, resolveAgainstBase); +#endif + if (path) { +#if defined(__MACH__) + Boolean convResult = _CFStringGetFileSystemRepresentation(path, buffer, bufLen); + CFRelease(path); + return convResult; +#else + CFIndex usedLen; + CFIndex pathLen = CFStringGetLength(path); + CFIndex numConverted = CFStringGetBytes(path, CFRangeMake(0, pathLen), CFStringFileSystemEncoding(), 0, true, buffer, bufLen-1, &usedLen); // -1 because we need one byte to zero-terminate. + CFRelease(path); + if (numConverted == pathLen) { + buffer[usedLen] = '\0'; + return true; + } +#endif + } + return false; +} + +CFURLRef CFURLCreateFromFileSystemRepresentation(CFAllocatorRef allocator, const uint8_t *buffer, CFIndex bufLen, Boolean isDirectory) { + CFStringRef path = CFStringCreateWithBytes(allocator, buffer, bufLen, CFStringFileSystemEncoding(), false); + CFURLRef newURL; + if (!path) return NULL; +#if defined(__WIN32__) + newURL = CFURLCreateWithFileSystemPath(allocator, path, kCFURLWindowsPathStyle, isDirectory); +#elif defined(__MACOS8__) + newURL = CFURLCreateWithFileSystemPath(allocator, path, kCFURLHFSPathStyle, isDirectory); +#else + newURL = CFURLCreateWithFileSystemPath(allocator, path, kCFURLPOSIXPathStyle, isDirectory); +#endif + CFRelease(path); + return newURL; +} + +CF_EXPORT CFURLRef CFURLCreateFromFileSystemRepresentationRelativeToBase(CFAllocatorRef allocator, const uint8_t *buffer, CFIndex bufLen, Boolean isDirectory, CFURLRef baseURL) { + CFStringRef path = CFStringCreateWithBytes(allocator, buffer, bufLen, CFStringFileSystemEncoding(), false); + CFURLRef newURL; + if (!path) return NULL; +#if defined(__WIN32__) + newURL = CFURLCreateWithFileSystemPathRelativeToBase(allocator, path, kCFURLWindowsPathStyle, isDirectory, baseURL); +#elif defined(__MACOS8__) + newURL = CFURLCreateWithFileSystemPathRelativeToBase(allocator, path, kCFURLHFSPathStyle, isDirectory, baseURL); +#else + newURL = CFURLCreateWithFileSystemPathRelativeToBase(allocator, path, kCFURLPOSIXPathStyle, isDirectory, baseURL); +#endif + CFRelease(path); + return newURL; +} + + +/******************************/ +/* Support for path utilities */ +/******************************/ + +// Assumes url is a CFURL (not an Obj-C NSURL) +static CFRange _rangeOfLastPathComponent(CFURLRef url) { + UInt32 pathType = URL_PATH_TYPE(url); + CFRange pathRg, componentRg; + + if (pathType == FULL_URL_REPRESENTATION) { + if (!(url->_flags & IS_PARSED)) _parseComponentsOfURL(url); + pathRg = _rangeForComponent(url->_flags, url->ranges, HAS_PATH); + } else { + pathRg = CFRangeMake(0, CFStringGetLength(url->_string)); + } + + if (pathRg.location == kCFNotFound || pathRg.length == 0) { + // No path + return pathRg; + } + if (CFStringGetCharacterAtIndex(url->_string, pathRg.location + pathRg.length - 1) == PATH_DELIM_FOR_TYPE(pathType)) { + pathRg.length --; + if (pathRg.length == 0) { + pathRg.length ++; + return pathRg; + } + } + if (CFStringFindWithOptions(url->_string, PATH_DELIM_AS_STRING_FOR_TYPE(pathType), pathRg, kCFCompareBackwards, &componentRg)) { + componentRg.location ++; + componentRg.length = pathRg.location + pathRg.length - componentRg.location; + } else { + componentRg = pathRg; + } + return componentRg; +} + +CFStringRef CFURLCopyLastPathComponent(CFURLRef url) { + CFStringRef result; + + if (CF_IS_OBJC(__kCFURLTypeID, url)) { + CFStringRef path = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle); + CFIndex length; + CFRange rg, compRg; + if (!path) return NULL; + rg = CFRangeMake(0, CFStringGetLength(path)); + length = rg.length; // Remember this for comparison later + if (CFStringGetCharacterAtIndex(path, rg.length - 1) == '/') { + rg.length --; + } + if (CFStringFindWithOptions(path, CFSTR("/"), rg, kCFCompareBackwards, &compRg)) { + rg.length = rg.location + rg.length - (compRg.location+1); + rg.location = compRg.location + 1; + } + if (rg.location == 0 && rg.length == length) { + result = path; + } else { + result = CFStringCreateWithSubstring(NULL, path, rg); + CFRelease(path); + } + } else { + CFRange rg = _rangeOfLastPathComponent(url); + if (rg.location == kCFNotFound || rg.length == 0) { + // No path + return CFRetain(CFSTR("")); + } + if (rg.length == 1 && CFStringGetCharacterAtIndex(url->_string, rg.location) == PATH_DELIM_FOR_TYPE(URL_PATH_TYPE(url))) { + return CFRetain(CFSTR("/")); + } + result = CFStringCreateWithSubstring(CFGetAllocator(url), url->_string, rg); + if (URL_PATH_TYPE(url) == FULL_URL_REPRESENTATION && !(url->_flags & POSIX_AND_URL_PATHS_MATCH)) { + CFStringRef tmp; + if (url->_flags & IS_OLD_UTF8_STYLE || url->_encoding == kCFStringEncodingUTF8) { + tmp = CFURLCreateStringByReplacingPercentEscapes(CFGetAllocator(url), result, CFSTR("")); + } else { + tmp = CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFGetAllocator(url), result, CFSTR(""), url->_encoding); + } + CFRelease(result); + result = tmp; + } + } + return result; +} + +CFStringRef CFURLCopyPathExtension(CFURLRef url) { + CFStringRef lastPathComp = CFURLCopyLastPathComponent(url); + CFStringRef ext = NULL; + + if (lastPathComp) { + CFRange rg = CFStringFind(lastPathComp, CFSTR("."), kCFCompareBackwards); + if (rg.location != kCFNotFound) { + rg.location ++; + rg.length = CFStringGetLength(lastPathComp) - rg.location; + if (rg.length > 0) { + ext = CFStringCreateWithSubstring(CFGetAllocator(url), lastPathComp, rg); + } else { + ext = CFRetain(CFSTR("")); + } + } + CFRelease(lastPathComp); + } + return ext; +} + +CFURLRef CFURLCreateCopyAppendingPathComponent(CFAllocatorRef allocator, CFURLRef url, CFStringRef pathComponent, Boolean isDirectory) { + UInt32 fsType; + CFURLRef result; + url = _CFURLFromNSURL(url); + __CFGenericValidateType(url, __kCFURLTypeID); + CFAssert1(pathComponent != NULL, __kCFLogAssertion, "%s(): Cannot be called with a NULL component to append", __PRETTY_FUNCTION__); + + fsType = URL_PATH_TYPE(url); + if (fsType != FULL_URL_REPRESENTATION && CFStringFindWithOptions(pathComponent, PATH_DELIM_AS_STRING_FOR_TYPE(fsType), CFRangeMake(0, CFStringGetLength(pathComponent)), 0, NULL)) { + // Must convert to full representation, and then work with it + fsType = FULL_URL_REPRESENTATION; + _convertToURLRepresentation((struct __CFURL *)url); + } + + if (fsType == FULL_URL_REPRESENTATION) { + CFMutableStringRef newString; + CFStringRef newComp; + CFRange pathRg; + if (!(url->_flags & IS_PARSED)) _parseComponentsOfURL(url); + if (!(url->_flags & HAS_PATH)) return NULL; + + newString = CFStringCreateMutableCopy(allocator, 0, url->_string); + newComp = CFURLCreateStringByAddingPercentEscapes(allocator, pathComponent, NULL, CFSTR(";?"), (url->_flags & IS_OLD_UTF8_STYLE) ? kCFStringEncodingUTF8 : url->_encoding); + pathRg = _rangeForComponent(url->_flags, url->ranges, HAS_PATH); + if (!pathRg.length || CFStringGetCharacterAtIndex(url->_string, pathRg.location + pathRg.length - 1) != '/') { + CFStringInsert(newString, pathRg.location + pathRg.length, CFSTR("/")); + pathRg.length ++; + } + CFStringInsert(newString, pathRg.location + pathRg.length, newComp); + if (isDirectory) { + CFStringInsert(newString, pathRg.location + pathRg.length + CFStringGetLength(newComp), CFSTR("/")); + } + CFRelease(newComp); + result = _CFURLCreateWithArbitraryString(allocator, newString, url->_base); + CFRelease(newString); + } else { + UniChar pathDelim = PATH_DELIM_FOR_TYPE(fsType); + CFStringRef newString; + if (CFStringGetCharacterAtIndex(url->_string, CFStringGetLength(url->_string) - 1) != pathDelim) { + if (isDirectory) { + newString = CFStringCreateWithFormat(allocator, NULL, CFSTR("%@%c%@%c"), url->_string, pathDelim, pathComponent, pathDelim); + } else { + newString = CFStringCreateWithFormat(allocator, NULL, CFSTR("%@%c%@"), url->_string, pathDelim, pathComponent); + } + } else { + if (isDirectory) { + newString = CFStringCreateWithFormat(allocator, NULL, CFSTR("%@%@%c"), url->_string, pathComponent, pathDelim); + } else { + newString = CFStringCreateWithFormat(allocator, NULL, CFSTR("%@%@"), url->_string, pathComponent); + } + } + result = CFURLCreateWithFileSystemPathRelativeToBase(allocator, newString, fsType, isDirectory, url->_base); + CFRelease(newString); + } + return result; +} + +CFURLRef CFURLCreateCopyDeletingLastPathComponent(CFAllocatorRef allocator, CFURLRef url) { + CFURLRef result; + CFMutableStringRef newString; + CFRange lastCompRg, pathRg; + Boolean appendDotDot = false; + UInt32 fsType; + + url = _CFURLFromNSURL(url); + CFAssert1(url != NULL, __kCFLogAssertion, "%s(): NULL argument not allowed", __PRETTY_FUNCTION__); + __CFGenericValidateType(url, __kCFURLTypeID); + + fsType = URL_PATH_TYPE(url); + if (fsType == FULL_URL_REPRESENTATION) { + if (!(url->_flags & IS_PARSED)) _parseComponentsOfURL(url); + if (!(url->_flags & HAS_PATH)) return NULL; + pathRg = _rangeForComponent(url->_flags, url->ranges, HAS_PATH); + } else { + pathRg = CFRangeMake(0, CFStringGetLength(url->_string)); + } + lastCompRg = _rangeOfLastPathComponent(url); + if (lastCompRg.length == 0) { + appendDotDot = true; + } else if (lastCompRg.length == 1) { + UniChar ch = CFStringGetCharacterAtIndex(url->_string, lastCompRg.location); + if (ch == '.' || ch == PATH_DELIM_FOR_TYPE(fsType)) { + appendDotDot = true; + } + } else if (lastCompRg.length == 2 && CFStringGetCharacterAtIndex(url->_string, lastCompRg.location) == '.' && CFStringGetCharacterAtIndex(url->_string, lastCompRg.location+1) == '.') { + appendDotDot = true; + } + + newString = CFStringCreateMutableCopy(allocator, 0, url->_string); + if (appendDotDot) { + CFIndex delta = 0; + if (pathRg.length > 0 && CFStringGetCharacterAtIndex(url->_string, pathRg.location + pathRg.length - 1) != PATH_DELIM_FOR_TYPE(fsType)) { + CFStringInsert(newString, pathRg.location + pathRg.length, PATH_DELIM_AS_STRING_FOR_TYPE(fsType)); + delta ++; + } + CFStringInsert(newString, pathRg.location + pathRg.length + delta, CFSTR("..")); + delta += 2; + CFStringInsert(newString, pathRg.location + pathRg.length + delta, PATH_DELIM_AS_STRING_FOR_TYPE(fsType)); + delta ++; + // We know we have "/../" at the end of the path; we wish to know if that's immediately preceded by "/." (but that "/." doesn't start the string), in which case we want to delete the "/.". + if (pathRg.length + delta > 4 && CFStringGetCharacterAtIndex(newString, pathRg.location + pathRg.length + delta - 5) == '.') { + if (pathRg.length+delta > 7 && CFStringGetCharacterAtIndex(newString, pathRg.location + pathRg.length + delta - 6) == PATH_DELIM_FOR_TYPE(fsType)) { + CFStringDelete(newString, CFRangeMake(pathRg.location + pathRg.length + delta - 6, 2)); + } else if (pathRg.length+delta == 5) { + CFStringDelete(newString, CFRangeMake(pathRg.location + pathRg.length + delta - 5, 2)); + } + } + } else if (lastCompRg.location == pathRg.location) { + CFStringReplace(newString, pathRg, CFSTR(".")); + CFStringInsert(newString, 1, PATH_DELIM_AS_STRING_FOR_TYPE(fsType)); + } else { + CFStringDelete(newString, CFRangeMake(lastCompRg.location, pathRg.location + pathRg.length - lastCompRg.location)); + } + if (fsType == FULL_URL_REPRESENTATION) { + result = _CFURLCreateWithArbitraryString(allocator, newString, url->_base); + } else { + result = CFURLCreateWithFileSystemPathRelativeToBase(allocator, newString, fsType, true, url->_base); + } + CFRelease(newString); + return result; +} + +CFURLRef CFURLCreateCopyAppendingPathExtension(CFAllocatorRef allocator, CFURLRef url, CFStringRef extension) { + CFMutableStringRef newString; + CFURLRef result; + CFRange rg; + CFURLPathStyle fsType; + + CFAssert1(url != NULL && extension != NULL, __kCFLogAssertion, "%s(): NULL argument not allowed", __PRETTY_FUNCTION__); + url = _CFURLFromNSURL(url); + __CFGenericValidateType(url, __kCFURLTypeID); + __CFGenericValidateType(extension, CFStringGetTypeID()); + + rg = _rangeOfLastPathComponent(url); + if (rg.location < 0) return NULL; // No path + fsType = URL_PATH_TYPE(url); + if (fsType != FULL_URL_REPRESENTATION && CFStringFindWithOptions(extension, PATH_DELIM_AS_STRING_FOR_TYPE(fsType), CFRangeMake(0, CFStringGetLength(extension)), 0, NULL)) { + _convertToURLRepresentation((struct __CFURL *)url); + fsType = FULL_URL_REPRESENTATION; + rg = _rangeOfLastPathComponent(url); + } + + newString = CFStringCreateMutableCopy(allocator, 0, url->_string); + CFStringInsert(newString, rg.location + rg.length, CFSTR(".")); + if (fsType == FULL_URL_REPRESENTATION) { + CFStringRef newExt = CFURLCreateStringByAddingPercentEscapes(allocator, extension, NULL, CFSTR(";?/"), (url->_flags & IS_OLD_UTF8_STYLE) ? kCFStringEncodingUTF8 : url->_encoding); + CFStringInsert(newString, rg.location + rg.length + 1, newExt); + CFRelease(newExt); + result = _CFURLCreateWithArbitraryString(allocator, newString, url->_base); + } else { + CFStringInsert(newString, rg.location + rg.length + 1, extension); + result = CFURLCreateWithFileSystemPathRelativeToBase(allocator, newString, fsType, (url->_flags & IS_DIRECTORY) != 0 ? true : false, url->_base); + } + CFRelease(newString); + return result; +} + +CFURLRef CFURLCreateCopyDeletingPathExtension(CFAllocatorRef allocator, CFURLRef url) { + CFRange rg, dotRg; + CFURLRef result; + + CFAssert1(url != NULL, __kCFLogAssertion, "%s(): NULL argument not allowed", __PRETTY_FUNCTION__); + url = _CFURLFromNSURL(url); + __CFGenericValidateType(url, __kCFURLTypeID); + rg = _rangeOfLastPathComponent(url); + if (rg.location < 0) { + result = NULL; + } else if (rg.length && CFStringFindWithOptions(url->_string, CFSTR("."), rg, kCFCompareBackwards, &dotRg)) { + CFMutableStringRef newString = CFStringCreateMutableCopy(allocator, 0, url->_string); + dotRg.length = rg.location + rg.length - dotRg.location; + CFStringDelete(newString, dotRg); + if (URL_PATH_TYPE(url) == FULL_URL_REPRESENTATION) { + result = _CFURLCreateWithArbitraryString(allocator, newString, url->_base); + } else { + result = CFURLCreateWithFileSystemPathRelativeToBase(allocator, newString, URL_PATH_TYPE(url), (url->_flags & IS_DIRECTORY) != 0 ? true : false, url->_base); + } + CFRelease(newString); + } else { + result = CFRetain(url); + } + return result; +} + +#if defined(HAVE_CARBONCORE) +// We deal in FSRefs because they handle Unicode strings. +// FSSpecs handle a much more limited set of characters. +static Boolean __CFFSRefForVolumeName(CFStringRef volName, FSRef *spec, CFAllocatorRef alloc) { + HFSUniStr255 name; + CFIndex volIndex; + Boolean success = false; + CFMutableStringRef str = CFStringCreateMutableWithExternalCharactersNoCopy(alloc, NULL, 0, 0, kCFAllocatorNull); + + for (volIndex = 1; FSGetVolumeInfo(0, volIndex, NULL, kFSVolInfoNone, NULL, &name, spec) == noErr; volIndex ++) { + CFStringSetExternalCharactersNoCopy(str, name.unicode, name.length, name.length); + if (CFStringCompare(str, volName, 0) == kCFCompareEqualTo) { + success = true; + break; + } + } + CFRelease(str); + return success; +} +#else +#define __CFFSRefForVolumeName(A, B, C) (-3296) +#endif + +static CFArrayRef HFSPathToURLComponents(CFStringRef path, CFAllocatorRef alloc, Boolean isDir) { + CFArrayRef components = CFStringCreateArrayBySeparatingStrings(alloc, path, CFSTR(":")); + CFMutableArrayRef newComponents = CFArrayCreateMutableCopy(alloc, 0, components); + Boolean doSpecialLeadingColon = false; + UniChar firstChar = CFStringGetCharacterAtIndex(path, 0); + UInt32 i, cnt; + CFRelease(components); + +#if defined(HAVE_CARBONCORE) && !defined(__MACOS8__) + doSpecialLeadingColon = true; +#endif + + if (!doSpecialLeadingColon && firstChar != ':') { + CFArrayInsertValueAtIndex(newComponents, 0, CFSTR("")); + } else if (firstChar != ':') { + // see what we need to add at the beginning. Under MacOS, if the + // first character isn't a ':', then the first component is the + // volume name, and we need to find the mount point. Bleah. If we + // don't find a mount point, we're going to have to lie, and make something up. + CFStringRef firstComp = CFArrayGetValueAtIndex(newComponents, 0); + if (CFStringGetLength(firstComp) == 1 && CFStringGetCharacterAtIndex(firstComp, 0) == '/') { + // "/" is the "magic" path for a UFS root directory + CFArrayRemoveValueAtIndex(newComponents, 0); + CFArrayInsertValueAtIndex(newComponents, 0, CFSTR("")); + } else { + // See if we can get a mount point. + Boolean foundMountPoint = false; + uint8_t buf[CFMaxPathLength]; + FSRef volSpec; + // Now produce an FSSpec from the volume, then try and get the mount point + if (__CFFSRefForVolumeName(firstComp, &volSpec, alloc) && (FSRefMakePath(&volSpec, buf, CFMaxPathLength) == noErr)) { + // We win! Ladies and gentlemen, we have a mount point. + if (buf[0] == '/' && buf[1] == '\0') { + // Special case this common case + foundMountPoint = true; + CFArrayRemoveValueAtIndex(newComponents, 0); + CFArrayInsertValueAtIndex(newComponents, 0, CFSTR("")); + } else { + // This is pretty inefficient; we can do better. + CFStringRef mountPoint = CFStringCreateWithCString(alloc, buf, CFStringFileSystemEncoding()); + CFArrayRef mountComponents = mountPoint ? CFStringCreateArrayBySeparatingStrings(alloc, mountPoint, CFSTR("/")) : NULL; + if (mountComponents) { + CFIndex idx = CFArrayGetCount(mountComponents) - 1; + CFArrayRemoveValueAtIndex(newComponents, 0); + for ( ; idx >= 0; idx --) { + CFArrayInsertValueAtIndex(newComponents, 0, CFArrayGetValueAtIndex(mountComponents, idx)); + } + CFRelease(mountComponents); + foundMountPoint = true; + } + if (mountPoint) CFRelease(mountPoint); + } + } + if (!foundMountPoint) { + // Fall back to treating the volume name as the top level directory + CFArrayInsertValueAtIndex(newComponents, 0, CFSTR("")); + } + } + } else { + CFArrayRemoveValueAtIndex(newComponents, 0); + } + + cnt = CFArrayGetCount(newComponents); + for (i = 0; i < cnt; i ++) { + CFStringRef comp = CFArrayGetValueAtIndex(newComponents, i); + CFStringRef newComp = NULL; + CFRange searchRg, slashRg; + searchRg.location = 0; + searchRg.length = CFStringGetLength(comp); + while (CFStringFindWithOptions(comp, CFSTR("/"), searchRg, 0, &slashRg)) { + if (!newComp) { + newComp = CFStringCreateMutableCopy(alloc, searchRg.location + searchRg.length, comp); + } + CFStringReplace((CFMutableStringRef)newComp, slashRg, CFSTR(":")); + searchRg.length = searchRg.location + searchRg.length - slashRg.location - 1; + searchRg.location = slashRg.location + 1; + } + if (newComp) { + CFArraySetValueAtIndex(newComponents, i, newComp); + CFRelease(newComp); + } + } + if (isDir && CFStringGetLength(CFArrayGetValueAtIndex(newComponents, cnt-1)) != 0) { + CFArrayAppendValue(newComponents, CFSTR("")); + } + return newComponents; +} + +static CFStringRef HFSPathToURLPath(CFStringRef path, CFAllocatorRef alloc, Boolean isDir) { + CFArrayRef components = HFSPathToURLComponents(path, alloc, isDir); + CFArrayRef newComponents = components ? copyStringArrayWithTransformation(components, escapePathComponent) : NULL; + CFIndex cnt; + CFStringRef result; + if (components) CFRelease(components); + if (!newComponents) return NULL; + + cnt = CFArrayGetCount(newComponents); + if (cnt == 1 && CFStringGetLength(CFArrayGetValueAtIndex(newComponents, 0)) == 0) { + result = CFRetain(CFSTR("/")); + } else { + result = CFStringCreateByCombiningStrings(alloc, newComponents, CFSTR("/")); + } + CFRelease(newComponents); + return result; +} + +static CFMutableStringRef filePathToHFSPath(unsigned char *buf, CFAllocatorRef allocator); +static CFStringRef colonToSlash(CFStringRef comp, CFAllocatorRef alloc); + +static CFStringRef URLPathToHFSPath(CFStringRef path, CFAllocatorRef allocator, CFStringEncoding encoding) { + CFStringRef result = NULL; +#if defined(__MACOS8__) + // Slashes become colons; escaped slashes stay slashes. + CFArrayRef components = CFStringCreateArrayBySeparatingStrings(allocator, path, CFSTR("/")); + CFMutableArrayRef mutableComponents = CFArrayCreateMutableCopy(allocator, 0, components); + SInt32 count = CFArrayGetCount(mutableComponents); + CFStringRef newPath; + CFRelease(components); + + if (count && CFStringGetLength(CFArrayGetValueAtIndex(mutableComponents, count-1)) == 0) { + CFArrayRemoveValueAtIndex(mutableComponents, count-1); + count --; + } + // On MacOS absolute paths do NOT begin with colon while relative paths DO. + if ((count > 0) && CFEqual(CFArrayGetValueAtIndex(mutableComponents, 0), CFSTR(""))) { + CFArrayRemoveValueAtIndex(mutableComponents, 0); + } else { + CFArrayInsertValueAtIndex(mutableComponents, 0, CFSTR("")); + } + newPath = CFStringCreateByCombiningStrings(allocator, mutableComponents, CFSTR(":")); + CFRelease(mutableComponents); + result = CFURLCreateStringByReplacingPercentEscapesUsingEncoding(allocator, newPath, CFSTR(""), encoding); + CFRelease(newPath); +#elif defined(HAVE_CARBONCORE) + if (CFStringGetLength(path) > 0 && CFStringGetCharacterAtIndex(path, 0) == '/') { + // Absolute path; to do this properly, we need to go to the file system, generate an FSRef, then generate a path from there. That's what filePathToHFSPath does. + CFStringRef nativePath = URLPathToPOSIXPath(path, allocator, encoding); + unsigned char buf[CFMaxPathLength]; + if (nativePath && _CFStringGetFileSystemRepresentation(nativePath, buf, CFMaxPathLength)) { + result = filePathToHFSPath(buf, allocator); + } + if (nativePath) CFRelease(nativePath); + } else if (CFStringGetLength(path) == 0) { + CFRetain(path); + result = path; + } else { + // Relative path + CFArrayRef components = CFStringCreateArrayBySeparatingStrings(allocator, path, CFSTR("/")); + CFMutableArrayRef mutableComponents = CFArrayCreateMutableCopy(allocator, 0, components); + SInt32 count = CFArrayGetCount(mutableComponents); + CFIndex i, c; + CFRelease(components); + + if (CFStringGetLength(CFArrayGetValueAtIndex(mutableComponents, count-1)) == 0) { + // Strip off the trailing slash + CFArrayRemoveValueAtIndex(mutableComponents, count-1); + } + // On MacOS absolute paths do NOT begin with colon while relative paths DO. + CFArrayInsertValueAtIndex(mutableComponents, 0, CFSTR("")); + for (i = 0, c = CFArrayGetCount(mutableComponents); i < c; i ++) { + CFStringRef origComp, comp, newComp; + origComp = CFArrayGetValueAtIndex(mutableComponents, i); + comp = CFURLCreateStringByReplacingPercentEscapesUsingEncoding(allocator, origComp, CFSTR(""), encoding); + newComp = colonToSlash(comp, allocator); + if (newComp != origComp) { + CFArraySetValueAtIndex(mutableComponents, i, newComp); + } + CFRelease(comp); + CFRelease(newComp); + } + result = CFStringCreateByCombiningStrings(allocator, mutableComponents, CFSTR(":")); + CFRelease(mutableComponents); + } +#endif + return result; +} + +static CFStringRef colonToSlash(CFStringRef comp, CFAllocatorRef alloc) { + CFStringRef newComp = NULL; + CFRange searchRg, colonRg; + searchRg.location = 0; + searchRg.length = CFStringGetLength(comp); + while (CFStringFindWithOptions(comp, CFSTR(":"), searchRg, 0, &colonRg)) { + if (!newComp) { + newComp = CFStringCreateMutableCopy(alloc, searchRg.location + searchRg.length, comp); + } + CFStringReplace((CFMutableStringRef)newComp, colonRg, CFSTR("/")); + searchRg.length = searchRg.location + searchRg.length - colonRg.location - 1; + searchRg.location = colonRg.location + 1; + } + if (newComp) { + return newComp; + } else { + CFRetain(comp); + return comp; + } +} + +static CFMutableStringRef filePathToHFSPath(unsigned char *buf, CFAllocatorRef allocator) { +#if defined(HAVE_CARBONCORE) + // The only way to do this right is to get an FSSpec, and then work up the path from there. This is problematic, of course, if the URL doesn't actually represent a file on the disk, but there's no way around that. So - first get the POSIX path, then run it through NativePathNameToFSSpec to get a valid FSSpec. If this succeeds, we iterate upwards using FSGetCatalogInfo to find the names of the parent directories until we reach the volume. REW, 10/29/99 + FSRef fsRef; + if (FSPathMakeRef(buf, &fsRef, NULL) == noErr) { + FSRef fsRef2, *parRef, *fileRef; + CFMutableStringRef mString = CFStringCreateMutable(allocator, 0); + CFMutableStringRef extString = CFStringCreateMutableWithExternalCharactersNoCopy(allocator, NULL, 0, 0, kCFAllocatorNull); + OSErr err = noErr; + + fileRef = &fsRef; + parRef = &fsRef2; + while (err == noErr) { + HFSUniStr255 name; + FSCatalogInfo catInfo; + err = FSGetCatalogInfo(fileRef, kFSCatInfoParentDirID, &catInfo, &name, NULL, parRef); + if (err == noErr) { + CFStringSetExternalCharactersNoCopy(extString, name.unicode, name.length, 255); + CFStringInsert(mString, 0, extString); + if (catInfo.parentDirID == fsRtParID) { + break; + } else { + CFStringInsert(mString, 0, CFSTR(":")); + } + fileRef = parRef; + parRef = (fileRef == &fsRef) ? &fsRef2 : &fsRef; + } + } + CFRelease(extString); + if (err == noErr) { + return mString; + } else { + CFRelease(mString); + return NULL; + } + } else { + // recurse + unsigned char *lastPathComponent = buf + strlen(buf); + unsigned char *parentPath; + CFMutableStringRef parentHFSPath; + lastPathComponent --; + if (*lastPathComponent == '/') { + // We're not interested in trailing slashes + *lastPathComponent = '\0'; + lastPathComponent --; + } + while (lastPathComponent > buf && *lastPathComponent != '/') { + if (*lastPathComponent == ':') { + *lastPathComponent = '/'; + } + lastPathComponent --; + } + if (lastPathComponent == buf) { + parentPath = (char *)"/"; + lastPathComponent ++; + } else { + *lastPathComponent = '\0'; + lastPathComponent ++; + parentPath = buf; + } + parentHFSPath = filePathToHFSPath(parentPath, allocator); + if (parentHFSPath) { + CFStringAppendCString(parentHFSPath, ":", kCFStringEncodingASCII); + CFStringAppendCString(parentHFSPath, lastPathComponent, CFStringFileSystemEncoding()); + } + return parentHFSPath; + } +#else +//#warning filePathToHFSPath unimplemented in the non-CarbonCore case + return CFStringCreateMutable(allocator, 0); +#endif +} + +// FSSpec stuff +Boolean _CFGetFSSpecFromURL(CFAllocatorRef alloc, CFURLRef url, struct FSSpec *voidspec) { + Boolean result = false; +#if defined (__MACOS8__) + CFURLRef absURL = CFURLCopyAbsoluteURL(url); + CFStringRef filePath; + filePath = CFURLCopyFileSystemPath(absURL, kCFURLHFSPathStyle); + CFRelease(absURL); + if (filePath) { + result = _CFGetFSSpecFromPathString(alloc, filePath, voidspec); + CFRelease(filePath); + } +#elif defined(HAVE_CARBONCORE) + FSRef fileRef; + if (_CFGetFSRefFromURL(alloc, url, &fileRef)) { + result = (FSGetCatalogInfo(&fileRef, 0, NULL, NULL, (FSSpec *)voidspec, NULL) == noErr); + } +#endif + return result; +} + +static Boolean _CFGetFSRefFromHFSPath(CFAllocatorRef alloc, CFStringRef path, void *voidRef) { + CFArrayRef components = CFStringCreateArrayBySeparatingStrings(alloc, path, CFSTR(":")); + CFIndex idx, count, bufferLen = 0; + UniChar *buffer = NULL; + Boolean result = false; + if (components && (count = CFArrayGetCount(components)) > 0 && __CFFSRefForVolumeName(CFArrayGetValueAtIndex(components, 0), voidRef, alloc)) { + FSRef ref2, *parentRef, *newRef; + parentRef = voidRef; + newRef = &ref2; + for (idx = 1; idx < count; idx ++ ) { + CFStringRef comp = CFArrayGetValueAtIndex(components, idx); + CFIndex compLength = CFStringGetLength(comp); + UniChar *chars = (UniChar *)CFStringGetCharactersPtr(comp); + if (!chars) { + if (!buffer) { + bufferLen = (compLength < 32) ? 32 : compLength; + buffer = CFAllocatorAllocate(alloc, bufferLen * sizeof(UniChar), 0); + } else if (bufferLen < compLength) { + buffer = CFAllocatorReallocate(alloc, buffer, compLength * sizeof(UniChar), 0); + bufferLen = compLength; + } + chars = buffer; + CFStringGetCharacters(comp, CFRangeMake(0, compLength), chars); + } + if (FSMakeFSRefUnicode(parentRef, compLength, chars, CFStringGetSystemEncoding(), newRef) != noErr) { + break; + } + parentRef = newRef; + newRef = (newRef == &ref2) ? voidRef : &ref2; + } + if (idx == count) { + result = true; + if (parentRef != voidRef) { + *((FSRef *)voidRef) = *parentRef; + } + } + if (components) CFRelease(components); + if (buffer) CFAllocatorDeallocate(alloc, buffer); + } + return result; +} + +static Boolean _CFGetFSRefFromURL(CFAllocatorRef alloc, CFURLRef url, void *voidRef) { + Boolean result = false; +#if defined(__MACOS8__) + CFURLRef absURL; + CFStringRef hfsPath; + if (!__CFMacOS8HasFSRefs()) return false; + absURL = CFURLCopyAbsoluteURL(url); + hfsPath = absURL ? CFURLCopyFileSystemPath(url, kCFURLHFSPathStyle) : NULL; + result = hfsPath ? _CFGetFSRefFromHFSPath(alloc, hfsPath, voidRef) : false; + if (absURL) CFRelease(absURL); + if (hfsPath) CFRelease(hfsPath); +#elif defined(HAVE_CARBONCORE) + CFURLRef absURL; + CFStringRef filePath, scheme; + scheme = CFURLCopyScheme(url); + if (scheme && !CFEqual(scheme, kCFURLFileScheme)) { + CFRelease(scheme); + return FALSE; + } else if (scheme) { + CFRelease(scheme); + } + absURL = CFURLCopyAbsoluteURL(url); + if (!CF_IS_OBJC(__kCFURLTypeID, absURL) && URL_PATH_TYPE(absURL) == kCFURLHFSPathStyle) { + // We special case kCFURLHFSPathStyle because we can avoid the expensive conversion to a POSIX native path -- REW, 2/23/2000 + result = _CFGetFSRefFromHFSPath(alloc, absURL->_string, voidRef); + CFRelease(absURL); + } else { + filePath = CFURLCopyFileSystemPath(absURL, kCFURLPOSIXPathStyle); + CFRelease(absURL); + if (filePath) { + char buf[CFMaxPathLength]; + + result = (_CFStringGetFileSystemRepresentation(filePath, buf, CFMaxPathLength) && (FSPathMakeRef(buf, voidRef, NULL) == noErr) ? true : false); + CFRelease(filePath); + } + } +#endif + return result; +} + +CFURLRef _CFCreateURLFromFSSpec(CFAllocatorRef alloc, const struct FSSpec *voidspec, Boolean isDirectory) { + CFURLRef url = NULL; +#if defined(__MACOS8__) + CFStringRef str = _CFCreateStringWithHFSPathFromFSSpec(alloc, voidspec); + if (str) { + url = CFURLCreateWithFileSystemPath(alloc, str, kCFURLHFSPathStyle, isDirectory); + CFRelease(str); + } +#elif defined(HAVE_CARBONCORE) + FSRef ref; + if (FSpMakeFSRef((const FSSpec *)voidspec, &ref) == noErr) { + url = _CFCreateURLFromFSRef(alloc, (void *)(&ref), isDirectory); + } +#endif + return url; +} + +static CFURLRef _CFCreateURLFromFSRef(CFAllocatorRef alloc, const void *voidRef, Boolean isDirectory) { + CFURLRef url = NULL; +#if defined(__MACOS8__) + CFStringRef path = _CFCreateStringWithHFSPathFromFSRef(alloc, voidRef); + if (path) { + url = CFURLCreateWithFileSystemPath(alloc, path, kCFURLHFSPathStyle, isDirectory); + CFRelease(path); + } +#elif defined(HAVE_CARBONCORE) + uint8_t buf[CFMaxPathLength]; + if (FSRefMakePath((const FSRef *)voidRef, buf, CFMaxPathLength) == noErr) { + url = CFURLCreateFromFileSystemRepresentation(alloc, buf, strlen(buf), isDirectory); + } +#endif + return url; +} + +CFURLRef CFURLCreateFromFSRef(CFAllocatorRef allocator, const FSRef *fsRef) { +#if defined(HAVE_CARBONCORE) || defined(__MACOS8__) + Boolean isDirectory; + FSCatalogInfo catInfo; +#if defined(__MACOS8__) + if (!__CFMacOS8HasFSRefs()) return NULL; +#endif + if (FSGetCatalogInfo(fsRef, kFSCatInfoNodeFlags, &catInfo, NULL, NULL, NULL) != noErr) { + return NULL; + } + isDirectory = catInfo.nodeFlags & kFSNodeIsDirectoryMask; + return _CFCreateURLFromFSRef(allocator, fsRef, isDirectory); +#else + return NULL; +#endif +} + +Boolean CFURLGetFSRef(CFURLRef url, FSRef *fsRef) { +#if defined(__MACOS8__) + return __CFMacOS8HasFSRefs() ? _CFGetFSRefFromURL(CFGetAllocator(url), url, fsRef) : false; +#else + Boolean result = false; + + if ( url ) { + result = _CFGetFSRefFromURL(CFGetAllocator(url), url, fsRef); + } + + return result; +#endif +} + + diff --git a/URL.subproj/CFURL.h b/URL.subproj/CFURL.h new file mode 100644 index 0000000..445936f --- /dev/null +++ b/URL.subproj/CFURL.h @@ -0,0 +1,416 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFURL.h + Copyright (c) 1998-2003, Apple, Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFURL__) +#define __COREFOUNDATION_CFURL__ 1 + +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +typedef enum { + kCFURLPOSIXPathStyle = 0, + kCFURLHFSPathStyle, + kCFURLWindowsPathStyle +} CFURLPathStyle; + +typedef const struct __CFURL * CFURLRef; + +/* CFURLs are composed of two fundamental pieces - their string, and a */ +/* (possibly NULL) base URL. A relative URL is one in which the string */ +/* by itself does not fully specify the URL (for instance "myDir/image.tiff"); */ +/* an absolute URL is one in which the string does fully specify the URL */ +/* ("file://localhost/myDir/image.tiff"). Absolute URLs always have NULL */ +/* base URLs; however, it is possible for a URL to have a NULL base, and still */ +/* not be absolute. Such a URL has only a relative string, and cannot be */ +/* resolved. Two CFURLs are considered equal if and only if their strings */ +/* are equal and their bases are equal. In other words, */ +/* "file://localhost/myDir/image.tiff" is NOT equal to the URL with relative */ +/* string "myDir/image.tiff" and base URL "file://localhost/". Clients that */ +/* need these less strict form of equality should convert all URLs to their */ +/* absolute form via CFURLCopyAbsoluteURL(), then compare the absolute forms. */ + +CF_EXPORT +CFTypeID CFURLGetTypeID(void); + +/* encoding will be used both to interpret the bytes of URLBytes, and to */ +/* interpret any percent-escapes within the bytes. */ +CF_EXPORT +CFURLRef CFURLCreateWithBytes(CFAllocatorRef allocator, const UInt8 *URLBytes, CFIndex length, CFStringEncoding encoding, CFURLRef baseURL); + +/* Escapes any character that is not 7-bit ASCII with the byte-code */ +/* for the given encoding. If escapeWhitespace is true, whitespace */ +/* characters (' ', '\t', '\r', '\n') will be escaped also (desirable */ +/* if embedding the URL into a larger text stream like HTML) */ +CF_EXPORT +CFDataRef CFURLCreateData(CFAllocatorRef allocator, CFURLRef url, CFStringEncoding encoding, Boolean escapeWhitespace); + +/* Any escape sequences in URLString will be interpreted via UTF-8. */ +CF_EXPORT +CFURLRef CFURLCreateWithString(CFAllocatorRef allocator, CFStringRef URLString, CFURLRef baseURL); + +#if MAC_OS_X_VERSION_10_3 <= MAC_OS_X_VERSION_MAX_ALLOWED + +/* Create an absolute URL directly, without requiring the extra step */ +/* of calling CFURLCopyAbsoluteURL(). If useCompatibilityMode is */ +/* true, the rules historically used on the web are used to resolve */ +/* relativeString against baseURL - these rules are generally listed */ +/* in the RFC as optional or alternate interpretations. Otherwise, */ +/* the strict rules from the RFC are used. The major differences are */ +/* that in compatibility mode, we are lenient of the scheme appearing */ +/* in relative portion, leading "../" components are removed from the */ +/* final URL's path, and if the relative portion contains only */ +/* resource specifier pieces (query, parameters, and fragment), then */ +/* the last path component of the base URL will not be deleted */ +CFURLRef CFURLCreateAbsoluteURLWithBytes(CFAllocatorRef alloc, const UInt8 *relativeURLBytes, CFIndex length, CFStringEncoding encoding, CFURLRef baseURL, Boolean useCompatibilityMode) AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; +#endif + +/* filePath should be the URL's path expressed as a path of the type */ +/* fsType. If filePath is not absolute, the resulting URL will be */ +/* considered relative to the current working directory (evaluated */ +/* at creation time). isDirectory determines whether filePath is */ +/* treated as a directory path when resolving against relative path */ +/* components */ +CF_EXPORT +CFURLRef CFURLCreateWithFileSystemPath(CFAllocatorRef allocator, CFStringRef filePath, CFURLPathStyle pathStyle, Boolean isDirectory); + +CF_EXPORT +CFURLRef CFURLCreateFromFileSystemRepresentation(CFAllocatorRef allocator, const UInt8 *buffer, CFIndex bufLen, Boolean isDirectory); + +CF_EXPORT +CFURLRef CFURLCreateWithFileSystemPathRelativeToBase(CFAllocatorRef allocator, CFStringRef filePath, CFURLPathStyle pathStyle, Boolean isDirectory, CFURLRef baseURL); + +CF_EXPORT +CFURLRef CFURLCreateFromFileSystemRepresentationRelativeToBase(CFAllocatorRef allocator, const UInt8 *buffer, CFIndex bufLen, Boolean isDirectory, CFURLRef baseURL); + +/* Fills buffer with the file system's native representation of */ +/* url's path. No more than maxBufLen bytes are written to buffer. */ +/* The buffer should be at least the maximum path length for */ +/* the file system in question to avoid failures for insufficiently */ +/* large buffers. If resolveAgainstBase is true, the url's relative */ +/* portion is resolved against its base before the path is computed. */ +/* Returns success or failure. */ +CF_EXPORT +Boolean CFURLGetFileSystemRepresentation(CFURLRef url, Boolean resolveAgainstBase, UInt8 *buffer, CFIndex maxBufLen); + +/* Creates a new URL by resolving the relative portion of relativeURL against its base. */ +CF_EXPORT +CFURLRef CFURLCopyAbsoluteURL(CFURLRef relativeURL); + +/* Returns the URL's string. */ +CF_EXPORT +CFStringRef CFURLGetString(CFURLRef anURL); + +/* Returns the base URL if it exists */ +CF_EXPORT +CFURLRef CFURLGetBaseURL(CFURLRef anURL); + +/* +All URLs can be broken into two pieces - the scheme (preceding the +first colon) and the resource specifier (following the first colon). +Most URLs are also "standard" URLs conforming to RFC 1808 (available +from www.w3c.org). This category includes URLs of the file, http, +https, and ftp schemes, to name a few. Standard URLs start the +resource specifier with two slashes ("//"), and can be broken into +four distinct pieces - the scheme, the net location, the path, and +further resource specifiers (typically an optional parameter, query, +and/or fragment). The net location appears immediately following +the two slashes and goes up to the next slash; it's format is +scheme-specific, but is usually composed of some or all of a username, +password, host name, and port. The path is a series of path components +separated by slashes; if the net location is present, the path always +begins with a slash. Standard URLs can be relative to another URL, +in which case at least the scheme and possibly other pieces as well +come from the base URL (see RFC 1808 for precise details when resolving +a relative URL against its base). The full URL is therefore + + "://" + +If a given CFURL can be decomposed (that is, conforms to RFC 1808), you +can ask for each of the four basic pieces (scheme, net location, path, +and resource specifer) separately, as well as for its base URL. The +basic pieces are returned with any percent escape sequences still in +place (although note that the scheme may not legally include any +percent escapes); this is to allow the caller to distinguish between +percent sequences that may have syntactic meaning if replaced by the +character being escaped (for instance, a '/' in a path component). +Since only the individual schemes know which characters are +syntactically significant, CFURL cannot safely replace any percent +escape sequences. However, you can use +CFURLCreateStringByReplacingPercentEscapes() to create a new string with +the percent escapes removed; see below. + +If a given CFURL can not be decomposed, you can ask for its scheme and its +resource specifier; asking it for its net location or path will return NULL. + +To get more refined information about the components of a decomposable +CFURL, you may ask for more specific pieces of the URL, expressed with +the percent escapes removed. The available functions are CFURLCopyHostName(), +CFURLGetPortNumber() (returns an Int32), CFURLCopyUserName(), +CFURLCopyPassword(), CFURLCopyQuery(), CFURLCopyParameters(), and +CFURLCopyFragment(). Because the parameters, query, and fragment of an +URL may contain scheme-specific syntaxes, these methods take a second +argument, giving a list of characters which should NOT be replaced if +percent escaped. For instance, the ftp parameter syntax gives simple +key-value pairs as "=;" Clearly if a key or value includes +either '=' or ';', it must be escaped to avoid corrupting the meaning of +the parameters, so the caller may request the parameter string as + +CFStringRef myParams = CFURLCopyParameters(ftpURL, CFSTR("=;%")); + +requesting that all percent escape sequences be replaced by the represented +characters, except for escaped '=', '%' or ';' characters. Pass the empty +string (CFSTR("")) to request that all percent escapes be replaced, or NULL +to request that none be. +*/ + +/* Returns true if anURL conforms to RFC 1808 */ +CF_EXPORT +Boolean CFURLCanBeDecomposed(CFURLRef anURL); + +/* The next several methods leave any percent escape sequences intact */ + +CF_EXPORT +CFStringRef CFURLCopyScheme(CFURLRef anURL); + +/* NULL if CFURLCanBeDecomposed(anURL) is false */ +CF_EXPORT +CFStringRef CFURLCopyNetLocation(CFURLRef anURL); + +/* NULL if CFURLCanBeDecomposed(anURL) is false; also does not resolve the URL */ +/* against its base. See also CFURLCopyAbsoluteURL(). Note that, strictly */ +/* speaking, any leading '/' is not considered part of the URL's path, although */ +/* its presence or absence determines whether the path is absolute. */ +/* CFURLCopyPath()'s return value includes any leading slash (giving the path */ +/* the normal POSIX appearance); CFURLCopyStrictPath()'s return value omits any */ +/* leading slash, and uses isAbsolute to report whether the URL's path is absolute. */ + +/* CFURLCopyFileSystemPath() returns the URL's path as a file system path for the */ +/* given path style. All percent escape sequences are replaced. The URL is not */ +/* resolved against its base before computing the path. */ +CF_EXPORT +CFStringRef CFURLCopyPath(CFURLRef anURL); + +CF_EXPORT +CFStringRef CFURLCopyStrictPath(CFURLRef anURL, Boolean *isAbsolute); + +CF_EXPORT +CFStringRef CFURLCopyFileSystemPath(CFURLRef anURL, CFURLPathStyle pathStyle); + +/* Returns whether anURL's path represents a directory */ +/* (true returned) or a simple file (false returned) */ +CF_EXPORT +Boolean CFURLHasDirectoryPath(CFURLRef anURL); + +/* Any additional resource specifiers after the path. For URLs */ +/* that cannot be decomposed, this is everything except the scheme itself. */ +CF_EXPORT +CFStringRef CFURLCopyResourceSpecifier(CFURLRef anURL); + +CF_EXPORT +CFStringRef CFURLCopyHostName(CFURLRef anURL); + +CF_EXPORT +SInt32 CFURLGetPortNumber(CFURLRef anURL); /* Returns -1 if no port number is specified */ + +CF_EXPORT +CFStringRef CFURLCopyUserName(CFURLRef anURL); + +CF_EXPORT +CFStringRef CFURLCopyPassword(CFURLRef anURL); + +/* These remove all percent escape sequences except those for */ +/* characters in charactersToLeaveEscaped. If charactersToLeaveEscaped */ +/* is empty (""), all percent escape sequences are replaced by their */ +/* corresponding characters. If charactersToLeaveEscaped is NULL, */ +/* then no escape sequences are removed at all */ +CF_EXPORT +CFStringRef CFURLCopyParameterString(CFURLRef anURL, CFStringRef charactersToLeaveEscaped); + +CF_EXPORT +CFStringRef CFURLCopyQueryString(CFURLRef anURL, CFStringRef charactersToLeaveEscaped); + +CF_EXPORT +CFStringRef CFURLCopyFragment(CFURLRef anURL, CFStringRef charactersToLeaveEscaped); + +CF_EXPORT +CFStringRef CFURLCopyLastPathComponent(CFURLRef url); + +CF_EXPORT +CFStringRef CFURLCopyPathExtension(CFURLRef url); + +/* These functions all treat the base URL of the supplied url as */ +/* invariant. In other words, the URL returned will always have */ +/* the same base as the URL supplied as an argument. */ + +CF_EXPORT +CFURLRef CFURLCreateCopyAppendingPathComponent(CFAllocatorRef allocator, CFURLRef url, CFStringRef pathComponent, Boolean isDirectory); + +CF_EXPORT +CFURLRef CFURLCreateCopyDeletingLastPathComponent(CFAllocatorRef allocator, CFURLRef url); + +CF_EXPORT +CFURLRef CFURLCreateCopyAppendingPathExtension(CFAllocatorRef allocator, CFURLRef url, CFStringRef extension); + +CF_EXPORT +CFURLRef CFURLCreateCopyDeletingPathExtension(CFAllocatorRef allocator, CFURLRef url); + +#if MAC_OS_X_VERSION_10_3 <= MAC_OS_X_VERSION_MAX_ALLOWED +/* Fills buffer with the bytes for url, returning the number of bytes */ +/* filled. If buffer is of insufficient size, returns -1 and no bytes */ +/* are placed in buffer. If buffer is NULL, the needed length is */ +/* computed and returned. The returned bytes are the original bytes */ +/* from which the URL was created; if the URL was created from a */ +/* string, the bytes will be the bytes of the string encoded via UTF-8 */ +CFIndex CFURLGetBytes(CFURLRef url, UInt8 *buffer, CFIndex bufferLength) AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; + +typedef enum { + kCFURLComponentScheme = 1, + kCFURLComponentNetLocation = 2, + kCFURLComponentPath = 3, + kCFURLComponentResourceSpecifier = 4, + + kCFURLComponentUser = 5, + kCFURLComponentPassword = 6, + kCFURLComponentUserInfo = 7, + kCFURLComponentHost = 8, + kCFURLComponentPort = 9, + kCFURLComponentParameterString = 10, + kCFURLComponentQuery = 11, + kCFURLComponentFragment = 12 +} CFURLComponentType; + +/* +Gets the range of the requested component in the bytes of url, as +returned by CFURLGetBytes(). This range is only good for use in the +bytes returned by CFURLGetBytes! + +If non-NULL, rangeIncludingSeparators gives the range of component +including the sequences that separate component from the previous and +next components. If there is no previous or next component, that end of +rangeIncludingSeparators will match the range of the component itself. +If url does not contain the given component type, (kCFNotFound, 0) is +returned, and rangeIncludingSeparators is set to the location where the +component would be inserted. Some examples - + +For the URL http://www.apple.com/hotnews/ + +Component returned range rangeIncludingSeparators +scheme (0, 4) (0, 7) +net location (7, 13) (4, 16) +path (20, 9) (20, 9) +resource specifier (kCFNotFound, 0) (29, 0) +user (kCFNotFound, 0) (7, 0) +password (kCFNotFound, 0) (7, 0) +user info (kCFNotFound, 0) (7, 0) +host (7, 13) (4, 16) +port (kCFNotFound, 0) (20, 0) +parameter (kCFNotFound, 0) (29, 0) +query (kCFNotFound, 0) (29, 0) +fragment (kCFNotFound, 0) (29, 0) + + +For the URL ./relPath/file.html#fragment + +Component returned range rangeIncludingSeparators +scheme (kCFNotFound, 0) (0, 0) +net location (kCFNotFound, 0) (0, 0) +path (0, 19) (0, 20) +resource specifier (20, 8) (19, 9) +user (kCFNotFound, 0) (0, 0) +password (kCFNotFound, 0) (0, 0) +user info (kCFNotFound, 0) (0, 0) +host (kCFNotFound, 0) (0, 0) +port (kCFNotFound, 0) (0, 0) +parameter (kCFNotFound, 0) (19, 0) +query (kCFNotFound, 0) (19, 0) +fragment (20, 8) (19, 9) + + +For the URL scheme://user:pass@host:1/path/path2/file.html;params?query#fragment + +Component returned range rangeIncludingSeparators +scheme (0, 6) (0, 9) +net location (9, 16) (6, 19) +path (25, 21) (25, 22) +resource specifier (47, 21) (46, 22) +user (9, 4) (6, 8) +password (14, 4) (13, 6) +user info (9, 9) (6, 13) +host (19, 4) (18, 6) +port (24, 1) (23, 2) +parameter (47, 6) (46, 8) +query (54, 5) (53, 7) +fragment (60, 8) (59, 9) +*/ +CFRange CFURLGetByteRangeForComponent(CFURLRef url, CFURLComponentType component, CFRange *rangeIncludingSeparators) AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; +#endif + +/* Returns a string with any percent escape sequences that do NOT */ +/* correspond to characters in charactersToLeaveEscaped with their */ +/* equivalent. Returns NULL on failure (if an invalid percent sequence */ +/* is encountered), or the original string (retained) if no characters */ +/* need to be replaced. Pass NULL to request that no percent escapes be */ +/* replaced, or the empty string (CFSTR("")) to request that all percent */ +/* escapes be replaced. Uses UTF8 to interpret percent escapes. */ +CF_EXPORT +CFStringRef CFURLCreateStringByReplacingPercentEscapes(CFAllocatorRef allocator, CFStringRef originalString, CFStringRef charactersToLeaveEscaped); + +#if MAC_OS_X_VERSION_10_3 <= MAC_OS_X_VERSION_MAX_ALLOWED +/* As above, but allows you to specify the encoding to use when interpreting percent escapes */ +CF_EXPORT +CFStringRef CFURLCreateStringByReplacingPercentEscapesUsingEncoding(CFAllocatorRef allocator, CFStringRef origString, CFStringRef charsToLeaveEscaped, CFStringEncoding encoding) AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; +#endif + +/* Creates a copy or originalString, replacing certain characters with */ +/* the equivalent percent escape sequence based on the encoding specified. */ +/* If the originalString does not need to be modified (no percent escape */ +/* sequences are missing), may retain and return originalString. */ +/* If you are uncertain of the correct encoding, you should use UTF-8, */ +/* which is the encoding designated by RFC 2396 as the correct encoding */ +/* for use in URLs. The characters so escaped are all characters that */ +/* are not legal URL characters (based on RFC 2396), plus any characters */ +/* in legalURLCharactersToBeEscaped, less any characters in */ +/* charactersToLeaveUnescaped. To simply correct any non-URL characters */ +/* in an otherwise correct URL string, do: */ + +/* newString = CFURLCreateStringByAddingPercentEscapes(NULL, origString, NULL, NULL, kCFStringEncodingUTF8); */ +CF_EXPORT +CFStringRef CFURLCreateStringByAddingPercentEscapes(CFAllocatorRef allocator, CFStringRef originalString, CFStringRef charactersToLeaveUnescaped, CFStringRef legalURLCharactersToBeEscaped, CFStringEncoding encoding); + + +#if defined(__cplusplus) +} +#endif + +#endif /* !__COREFOUNDATION_CFURL__ */ + diff --git a/URL.subproj/CFURLAccess.c b/URL.subproj/CFURLAccess.c new file mode 100644 index 0000000..ea55c0c --- /dev/null +++ b/URL.subproj/CFURLAccess.c @@ -0,0 +1,483 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFURLAccess.c + Copyright 1999-2002, Apple, Inc. All rights reserved. + Responsibility: Becky Willrich +*/ + +/*------ +CFData read/write routines +-------*/ + +#include "CFInternal.h" +#include +#include +#include +#include +#include +#include +#include + +#if defined(__WIN32__) +#include +#include +#include +#include +#include +#include +#define timeval xxx_timeval +#define BOOLEAN xxx_BOOLEAN +#include +#undef BOOLEAN +#undef timeval +#else +#include +#include +#include +#include +#include +#include +#endif + +#if defined(__MACH__) + +#include + +extern char *getenv(const char *name); + +static void *__CFLoadCFNetwork(void) { + static const void *image = NULL; + if (NULL == image) { + // OS 10.3 change to NSAddImage options here: + // a) Use NSADDIMAGE_OPTION_WITH_SEARCHING to support setting common DYLD_ environment variables + // including DYLD_IMAGE_SUFFIX and DYLD_LIBRARY_PATH. + // b) Use NSADDIMAGE_OPTION_MATCH_FILENAME_BY_INSTALLNAME to fix a nasty problem where two copies of + // a given framework are loaded into the same address space (See bug # 3060641). + image = ((void*)NSAddImage("/System/Library/Frameworks/CoreServices.framework/Frameworks/CFNetwork.framework/CFNetwork", NSADDIMAGE_OPTION_WITH_SEARCHING | NSADDIMAGE_OPTION_MATCH_FILENAME_BY_INSTALLNAME)); + } + return (void *)image; +} + +static Boolean __CFURLCreateDataAndPropertiesFromResource(CFAllocatorRef A, CFURLRef B, CFDataRef *C, CFDictionaryRef *D, CFArrayRef E, SInt32 *F) { + static Boolean (*dyfunc)(CFAllocatorRef, CFURLRef, CFDataRef *, CFDictionaryRef *, CFArrayRef, SInt32 *) = NULL; + if (NULL == dyfunc) { + void *image = __CFLoadCFNetwork(); + dyfunc = NSAddressOfSymbol(NSLookupSymbolInImage(image, "__CFURLCreateDataAndPropertiesFromResource", NSLOOKUPSYMBOLINIMAGE_OPTION_BIND)); + } + if (dyfunc) { + return dyfunc(A, B, C, D, E, F); + } else { + if (C) *C = NULL; + if (D) *D = NULL; + if (F) *F = kCFURLUnknownSchemeError; + return false; + } +} + +static Boolean __CFURLWriteDataAndPropertiesToResource(CFURLRef A, CFDataRef B, CFDictionaryRef C, SInt32 *D) { + static Boolean (*dyfunc)(CFURLRef, CFDataRef, CFDictionaryRef, SInt32 *) = NULL; + if (NULL == dyfunc) { + void *image = __CFLoadCFNetwork(); + dyfunc = NSAddressOfSymbol(NSLookupSymbolInImage(image, "__CFURLWriteDataAndPropertiesToResource", NSLOOKUPSYMBOLINIMAGE_OPTION_BIND)); + } + if (dyfunc) { + return dyfunc(A, B, C, D); + } else { + if (D) *D = kCFURLUnknownSchemeError; + return false; + } +} + +static Boolean __CFURLDestroyResource(CFURLRef A, SInt32 *B) { + static Boolean (*dyfunc)(CFURLRef, SInt32 *) = NULL; + if (NULL == dyfunc) { + void *image = __CFLoadCFNetwork(); + dyfunc = NSAddressOfSymbol(NSLookupSymbolInImage(image, "__CFURLDestroyResource", NSLOOKUPSYMBOLINIMAGE_OPTION_BIND)); + } + if (dyfunc) { + return dyfunc(A, B); + } else { + if (B) *B = kCFURLUnknownSchemeError; + return false; + } +} + +#endif + + +CONST_STRING_DECL(kCFURLFileExists, "kCFURLFileExists") +CONST_STRING_DECL(kCFURLFilePOSIXMode, "kCFURLFilePOSIXMode") +CONST_STRING_DECL(kCFURLFileDirectoryContents, "kCFURLFileDirectoryContents") +CONST_STRING_DECL(kCFURLFileLength, "kCFURLFileLength") +CONST_STRING_DECL(kCFURLFileLastModificationTime, "kCFURLFileLastModificationTime") +CONST_STRING_DECL(kCFURLFileOwnerID, "kCFURLFileOwnerID") +CONST_STRING_DECL(kCFURLHTTPStatusCode, "kCFURLHTTPStatusCode") +CONST_STRING_DECL(kCFURLHTTPStatusLine, "kCFURLHTTPStatusLine") + +// Compatibility property strings -- we obsoleted these names pre-DP4. REW, 5/22/2000 +CONST_STRING_DECL(kCFFileURLExists, "kCFURLFileExists") +CONST_STRING_DECL(kCFFileURLPOSIXMode, "kCFURLFilePOSIXMode") +CONST_STRING_DECL(kCFFileURLDirectoryContents, "kCFURLFileDirectoryContents") +CONST_STRING_DECL(kCFFileURLSize, "kCFURLFileLength") +CONST_STRING_DECL(kCFFileURLLastModificationTime, "kCFURLFileLastModificationTime") +CONST_STRING_DECL(kCFHTTPURLStatusCode, "kCFURLHTTPStatusCode") +CONST_STRING_DECL(kCFHTTPURLStatusLine, "kCFURLHTTPStatusLine") + +// Copied pretty much verbatim from NSData; note that files are still special cased in this code. Ultimately, we probably want to treat file URLs the same way as any other URL (go through the URL Access layer). -- REW, 10/21/98 + +/*************************/ +/* file: access routines */ +/*************************/ + +//#warning CF:For the moment file access failures are ill defined and set the error code to kCFURLUnknownError + +static CFDictionaryRef _CFFileURLCreatePropertiesFromResource(CFAllocatorRef alloc, CFURLRef url, CFArrayRef desiredProperties, SInt32 *errorCode) { + // MF:!!! This could/should be changed to use _CFGetFileProperties() to do the actual figuring. + static CFArrayRef _allProps = NULL; + CFRange arrayRange; + SInt32 idx; + CFMutableDictionaryRef propertyDict = NULL; + + Boolean exists; + SInt32 posixMode; + int64_t size; + CFDateRef modTime = NULL, *modTimePtr = NULL; + CFArrayRef contents = NULL, *contentsPtr = NULL; + SInt32 ownerID; + + if (errorCode) *errorCode = 0; + if (!desiredProperties) { + // Cheap and dirty hack to make this work for the moment; ultimately we need to do something more sophisticated. This will result in an error return whenever a property key is defined which isn't applicable to all file URLs. REW, 3/2/99 + if (!_allProps) { + const void *values[9]; + values[0] = kCFURLFileExists; + values[1] = kCFURLFilePOSIXMode; + values[2] = kCFURLFileDirectoryContents; + values[3] = kCFURLFileLength; + values[4] = kCFURLFileLastModificationTime; + values[5] = kCFURLFileOwnerID; + _allProps = CFArrayCreate(NULL, values, 6, &kCFTypeArrayCallBacks); + } + desiredProperties = _allProps; + } + + arrayRange.location = 0; + arrayRange.length = CFArrayGetCount(desiredProperties); + propertyDict = CFDictionaryCreateMutable(alloc, 0, & kCFTypeDictionaryKeyCallBacks, & kCFTypeDictionaryValueCallBacks); + if (arrayRange.length == 0) return propertyDict; + + if (CFArrayContainsValue(desiredProperties, arrayRange, kCFURLFileDirectoryContents)) { + contentsPtr = &contents; + } + if (CFArrayContainsValue(desiredProperties, arrayRange, kCFURLFileLastModificationTime)) { + modTimePtr = &modTime; + } + + if (_CFGetFileProperties(alloc, url, &exists, &posixMode, &size, modTimePtr, &ownerID, contentsPtr) != 0) { + if (errorCode) { + *errorCode = kCFURLUnknownError; + } + return propertyDict; + } + + for (idx = 0; idx < arrayRange.length; idx ++) { + CFStringRef key = (CFMutableStringRef )CFArrayGetValueAtIndex(desiredProperties, idx); + if (key == kCFURLFilePOSIXMode || CFEqual(kCFURLFilePOSIXMode, key)) { + if (exists) { + CFNumberRef num = CFNumberCreate(alloc, kCFNumberSInt32Type, &posixMode); + CFDictionarySetValue(propertyDict, kCFURLFilePOSIXMode, num); + CFRelease(num); + } else if (errorCode) { + *errorCode = kCFURLUnknownError; + } + } else if (key == kCFURLFileDirectoryContents || CFEqual(kCFURLFileDirectoryContents, key)) { + if (exists && (posixMode & S_IFMT) == S_IFDIR && contents) { + CFDictionarySetValue(propertyDict, kCFURLFileDirectoryContents, contents); + } else if (errorCode) { + *errorCode = kCFURLUnknownError; + } + } else if (key == kCFURLFileLength || CFEqual(kCFURLFileLength, key)) { + if (exists) { + CFNumberRef num = CFNumberCreate(alloc, kCFNumberSInt64Type, &size); + CFDictionarySetValue(propertyDict, kCFURLFileLength, num); + CFRelease(num); + } else if (errorCode) { + *errorCode = kCFURLUnknownError; + } + } else if (key == kCFURLFileLastModificationTime || CFEqual(kCFURLFileLastModificationTime, key)) { + if (exists && modTime) { + CFDictionarySetValue(propertyDict, kCFURLFileLastModificationTime, modTime); + } else if (errorCode) { + *errorCode = kCFURLUnknownError; + } + } else if (key == kCFURLFileExists || CFEqual(kCFURLFileExists, key)) { + if (exists) { + CFDictionarySetValue(propertyDict, kCFURLFileExists, kCFBooleanTrue); + } else { + CFDictionarySetValue(propertyDict, kCFURLFileExists, kCFBooleanFalse); + } + } else if (key == kCFURLFileOwnerID || CFEqual(kCFURLFileOwnerID, key)) { + if (exists) { + CFNumberRef num = CFNumberCreate(alloc, kCFNumberSInt32Type, &ownerID); + CFDictionarySetValue(propertyDict, kCFURLFileOwnerID, num); + CFRelease(num); + } else if (errorCode) { + *errorCode = kCFURLUnknownError; + } + // Add more properties here + } else if (errorCode) { + *errorCode = kCFURLUnknownPropertyKeyError; + } + } + if (modTime) CFRelease(modTime); + if (contents) CFRelease(contents); + return propertyDict; +} + +static Boolean _CFFileURLWritePropertiesToResource(CFURLRef url, CFDictionaryRef propertyDict, SInt32 *errorCode) { +#if defined(__MACOS8__) + return false; // No properties are writable on OS 8 +#else + CFTypeRef buffer[16]; + CFTypeRef *keys; + CFTypeRef *values; + Boolean result = true; + SInt32 idx, count; + char cPath[CFMaxPathSize]; + + if (!CFURLGetFileSystemRepresentation(url, true, cPath, CFMaxPathSize)) { + if (errorCode) *errorCode = kCFURLImproperArgumentsError; + return false; + } + + count = CFDictionaryGetCount(propertyDict); + if (count < 8) { + (CFTypeRef)keys = buffer; + (CFTypeRef)values = buffer+8; + } else { + keys = CFAllocatorAllocate(CFGetAllocator(url), sizeof(void *) * count * 2, 0); + values = keys + count; + } + CFDictionaryGetKeysAndValues(propertyDict, keys, values); + + for (idx = 0; idx < count; idx ++) { + CFStringRef key = keys[idx]; + CFTypeRef value = values[idx]; + if (key == kCFURLFilePOSIXMode || CFEqual(kCFURLFilePOSIXMode, key)) { + SInt32 mode; + int err; + if (CFNumberGetTypeID() == CFGetTypeID(value)) { + CFNumberRef modeNum = (CFNumberRef)value; + CFNumberGetValue(modeNum, kCFNumberSInt32Type, &mode); + } else { +#if defined(__WIN32__) + const unsigned short *modePtr = (const unsigned short *)CFDataGetBytePtr((CFDataRef)value); +#else + const mode_t *modePtr = (const mode_t *)CFDataGetBytePtr((CFDataRef)value); +#endif + mode = *modePtr; + } + err = chmod(cPath, mode); + if (err != 0) result = false; + } else { + result = false; + } + } + + if ((CFTypeRef)keys != buffer) CFAllocatorDeallocate(CFGetAllocator(url), keys); + + if (errorCode) *errorCode = result ? 0 : kCFURLUnknownError; + return result; +#endif +} + +static Boolean _CFFileURLCreateDataAndPropertiesFromResource(CFAllocatorRef alloc, CFURLRef url, CFDataRef *fetchedData, CFArrayRef desiredProperties, CFDictionaryRef *fetchedProperties, SInt32 *errorCode) { + Boolean success = true; + + if (errorCode) *errorCode = 0; + if (fetchedData) { + void *bytes; + CFIndex length; + Boolean releaseAlloc = false; + + if (alloc == NULL) { + // We need a real allocator to pass to _CFReadBytesFromFile + alloc = CFRetain(__CFGetDefaultAllocator()); + releaseAlloc = true; + } + if (!_CFReadBytesFromFile(alloc, url, &bytes, &length, 0)) { + if (errorCode) *errorCode = kCFURLUnknownError; + *fetchedData = NULL; + success = false; + } else { + *fetchedData = CFDataCreateWithBytesNoCopy(alloc, bytes, length, alloc); + } + if (releaseAlloc) { + // Now the CFData should be hanging on to it. + CFRelease(alloc); + } + } + + if (fetchedProperties) { + *fetchedProperties = _CFFileURLCreatePropertiesFromResource(alloc, url, desiredProperties, errorCode); + if (!*fetchedProperties) success = false; + } + + return success; +} +/*************************/ +/* Public routines */ +/*************************/ + +Boolean CFURLCreateDataAndPropertiesFromResource(CFAllocatorRef alloc, CFURLRef url, CFDataRef *fetchedData, CFDictionaryRef *fetchedProperties, CFArrayRef desiredProperties, SInt32 *errorCode) { + CFStringRef scheme = CFURLCopyScheme(url); + + if (!scheme) { + if (errorCode) *errorCode = kCFURLImproperArgumentsError; + if (fetchedData) *fetchedData = NULL; + if (fetchedProperties) *fetchedProperties = NULL; + return false; + } else { + Boolean result; + if (CFStringCompare(scheme, CFSTR("file"), 0) == kCFCompareEqualTo) { + result = _CFFileURLCreateDataAndPropertiesFromResource(alloc, url, fetchedData, desiredProperties, fetchedProperties, errorCode); + } else { +#if defined(__MACH__) + result = __CFURLCreateDataAndPropertiesFromResource(alloc, url, fetchedData, fetchedProperties, desiredProperties, errorCode); +#else + if (fetchedData) *fetchedData = NULL; + if (fetchedProperties) *fetchedProperties = NULL; + if (errorCode) *errorCode = kCFURLUnknownSchemeError; + result = false; +#endif + } + CFRelease(scheme); + return result; + } +} + +CFTypeRef CFURLCreatePropertyFromResource(CFAllocatorRef alloc, CFURLRef url, CFStringRef property, SInt32 *errorCode) { + CFArrayRef array = CFArrayCreate(alloc, (const void **)&property, 1, &kCFTypeArrayCallBacks); + CFDictionaryRef dict; + + if (CFURLCreateDataAndPropertiesFromResource(alloc, url, NULL, &dict, array, errorCode)) { + CFTypeRef result = CFDictionaryGetValue(dict, property); + if (result) CFRetain(result); + CFRelease(array); + CFRelease(dict); + return result; + } else { + if (dict) CFRelease(dict); + CFRelease(array); + return NULL; + } +} + +Boolean CFURLWriteDataAndPropertiesToResource(CFURLRef url, CFDataRef data, CFDictionaryRef propertyDict, SInt32 *errorCode) { + CFStringRef scheme = CFURLCopyScheme(url); + if (!scheme) { + if (errorCode) *errorCode = kCFURLImproperArgumentsError; + return false; + } else if (CFStringCompare(scheme, CFSTR("file"), 0) == kCFCompareEqualTo) { + Boolean success = true; + CFRelease(scheme); + if (errorCode) *errorCode = 0; + if (data) { + if (CFURLHasDirectoryPath(url)) { + // Create a directory + char cPath[CFMaxPathSize]; + if (!CFURLGetFileSystemRepresentation(url, true, cPath, CFMaxPathSize)) { + if (errorCode) *errorCode = kCFURLImproperArgumentsError; + success = false; + } else { + success = _CFCreateDirectory(cPath); + if (!success && errorCode) *errorCode = kCFURLUnknownError; + } + } else { + // Write data + SInt32 length = CFDataGetLength(data); + const void *bytes = (0 == length) ? (const void *)"" : CFDataGetBytePtr(data); + success = _CFWriteBytesToFile(url, bytes, length); + if (!success && errorCode) *errorCode = kCFURLUnknownError; + } + } + if (propertyDict) { + if (!_CFFileURLWritePropertiesToResource(url, propertyDict, errorCode)) + success = false; + } + return success; + } else { + CFRelease(scheme); +#if defined(__MACH__) + return __CFURLWriteDataAndPropertiesToResource(url, data, propertyDict, errorCode); +#else + if (errorCode) *errorCode = kCFURLUnknownSchemeError; + return false; +#endif + } +} + +Boolean CFURLDestroyResource(CFURLRef url, SInt32 *errorCode) { + CFStringRef scheme = CFURLCopyScheme(url); + char cPath[CFMaxPathSize]; + + if (!scheme) { + if (errorCode) *errorCode = kCFURLImproperArgumentsError; + return false; + } else if (CFStringCompare(scheme, CFSTR("file"), 0) == kCFCompareEqualTo) { + CFRelease(scheme); + if (!CFURLGetFileSystemRepresentation(url, true, cPath, CFMaxPathSize)) { + if (errorCode) *errorCode = kCFURLImproperArgumentsError; + return false; + } + + if (CFURLHasDirectoryPath(url)) { + if (_CFRemoveDirectory(cPath)) { + if (errorCode) *errorCode = 0; + return true; + } else { + if (errorCode) *errorCode = kCFURLUnknownError; + return false; + } + } else { + if (_CFDeleteFile(cPath)) { + if (errorCode) *errorCode = 0; + return true; + } else { + if (errorCode) *errorCode = kCFURLUnknownError; + return false; + } + } + } else { + CFRelease(scheme); +#if defined(__MACH__) + return __CFURLDestroyResource(url, errorCode); +#else + if (errorCode) *errorCode = kCFURLUnknownSchemeError; + return false; +#endif + } +} + diff --git a/URL.subproj/CFURLAccess.h b/URL.subproj/CFURLAccess.h new file mode 100644 index 0000000..079a254 --- /dev/null +++ b/URL.subproj/CFURLAccess.h @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFURLAccess.h + Copyright (c) 1998-2003, Apple, Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFURLACCESS__) +#define __COREFOUNDATION_CFURLACCESS__ 1 + +#include +#include +#include +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +/* Attempts to read the data and properties for the given URL. If +only interested in one of the resourceData and properties, pass NULL +for the other. If properties is non-NULL and desiredProperties is +NULL, then all properties are fetched. Returns success or failure; +note that as much work as possible is done even if false is returned. +So for instance if one property is not available, the others are +fetched anyway. errorCode is set to 0 on success, and some other +value on failure. If non-NULL, it is the caller 's responsibility +to release resourceData and properties. + + Apple reserves for its use all negative error code values; these +values represent errors common to any scheme. Scheme-specific error +codes should be positive, non-zero, and should be used only if one of +the predefined Apple error codes does not apply. Error codes should +be publicized and documented with the scheme-specific properties. +*/ +CF_EXPORT +Boolean CFURLCreateDataAndPropertiesFromResource(CFAllocatorRef alloc, CFURLRef url, CFDataRef *resourceData, CFDictionaryRef *properties, CFArrayRef desiredProperties, SInt32 *errorCode); + +/* Attempts to write the given data and properties to the given URL. +If dataToWrite is NULL, only properties are written out (use +CFURLDestroyResource() to delete a resource). Properties not present +in propertiesToWrite are left unchanged, hence if propertiesToWrite +is NULL or empty, the URL's properties are not changed at all. +Returns success or failure; errorCode is set as for +CFURLCreateDataAndPropertiesFromResource(), above. +*/ +CF_EXPORT +Boolean CFURLWriteDataAndPropertiesToResource(CFURLRef url, CFDataRef dataToWrite, CFDictionaryRef propertiesToWrite, SInt32 *errorCode); + +/* Destroys the resource indicated by url. */ +/* Returns success or failure; errorCode set as above. */ +CF_EXPORT +Boolean CFURLDestroyResource(CFURLRef url, SInt32 *errorCode); + +/* Convenience method which calls through to CFURLCreateDataAndPropertiesFromResource(). */ +/* Returns NULL on error and sets errorCode accordingly. */ +CF_EXPORT +CFTypeRef CFURLCreatePropertyFromResource(CFAllocatorRef alloc, CFURLRef url, CFStringRef property, SInt32 *errorCode); + +/* Common error codes; this list is expected to grow */ +typedef enum { + kCFURLUnknownError = -10, + kCFURLUnknownSchemeError = -11, + kCFURLResourceNotFoundError = -12, + kCFURLResourceAccessViolationError = -13, + kCFURLRemoteHostUnavailableError = -14, + kCFURLImproperArgumentsError = -15, + kCFURLUnknownPropertyKeyError = -16, + kCFURLPropertyKeyUnavailableError = -17, + kCFURLTimeoutError = -18 +} CFURLError; + +/* Property keys */ + +CF_EXPORT +const CFStringRef kCFURLFileExists; +CF_EXPORT +const CFStringRef kCFURLFileDirectoryContents; +CF_EXPORT +const CFStringRef kCFURLFileLength; +CF_EXPORT +const CFStringRef kCFURLFileLastModificationTime; +CF_EXPORT +const CFStringRef kCFURLFilePOSIXMode; +CF_EXPORT +const CFStringRef kCFURLFileOwnerID; +CF_EXPORT +const CFStringRef kCFURLHTTPStatusCode; +CF_EXPORT +const CFStringRef kCFURLHTTPStatusLine; + +/* The value of kCFURLFileExists is a CFBoolean */ +/* The value of kCFURLFileDirectoryContents is a CFArray containing CFURLs. An empty array means the directory exists, but is empty */ +/* The value of kCFURLFileLength is a CFNumber giving the file's length in bytes */ +/* The value of kCFURLFileLastModificationTime is a CFDate */ +/* The value of kCFURLFilePOSIXMode is a CFNumber as given in stat.h */ +/* The value of kCFURLFileOwnerID is a CFNumber representing the owner's uid */ + +/* Properties for the http: scheme. Except for the common error codes, above, errorCode will be set to the HTTP response status code upon failure. Any HTTP header name can also be used as a property */ +/* The value of kCFURLHTTPStatusCode is a CFNumber */ +/* The value of kCFURLHTTPStatusLine is a CFString */ + +#if defined(__cplusplus) +} +#endif + +#endif /* !__COREFOUNDATION_CFURLACCESS__ */ + diff --git a/version.c b/version.c new file mode 100644 index 0000000..c86fd5c --- /dev/null +++ b/version.c @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#define _STRINGIFY(X) #X +#define STRINGIFY(V) _STRINGIFY(V) + +const unsigned char kCFCoreFoundationVersionString[] = "@(#)PROGRAM:CoreFoundation PROJECT:CoreFoundation-" STRINGIFY(VERSION) " SYSTEM:Darwin DEVELOPER:" STRINGIFY(USER) " BUILT:" __DATE__ " " __TIME__ "\n"; +const double kCFCoreFoundationVersionNumber = (double)VERSION; + +#undef _STRINGIFY +#undef STRINGIFY + -- 2.45.2