From d89253836d02fdbaf48cc7f9e7d94de7adad229f Mon Sep 17 00:00:00 2001 From: Apple Date: Tue, 12 Jul 2005 21:00:18 +0000 Subject: [PATCH] CF-368.tar.gz --- APPLE_LICENSE | 0 AppServices.subproj/CFStream.h | 280 ++++ AppServices.subproj/CFStreamPriv.h | 173 ++ AppServices.subproj/CFUserNotification.c | 52 +- AppServices.subproj/CFUserNotification.h | 6 +- Base.subproj/CFBase.c | 161 +- Base.subproj/CFBase.h | 81 +- Base.subproj/CFByteOrder.h | 8 +- Base.subproj/CFFileUtilities.c | 105 +- Base.subproj/CFInternal.h | 261 ++- Base.subproj/CFPlatform.c | 115 +- Base.subproj/CFPriv.h | 25 +- Base.subproj/CFRuntime.c | 457 +++--- Base.subproj/CFRuntime.h | 66 +- Base.subproj/CFSortFunctions.c | 4 +- Base.subproj/CFSystemDirectories.c | 112 ++ Base.subproj/CFUUID.c | 42 +- Base.subproj/CFUUID.h | 6 +- Base.subproj/CFUtilities.c | 183 ++- Base.subproj/CFUtilities.h | 49 +- Base.subproj/CFUtilitiesPriv.h | 67 + Base.subproj/CoreFoundation.h | 67 +- Base.subproj/ForFoundationOnly.h | 206 ++- Base.subproj/auto_stubs.h | 44 + Base.subproj/uuid.c | 439 +++-- CharacterSets/CFCharacterSetBitmaps.bitmap | Bin 352454 -> 352454 bytes CharacterSets/CFUniCharPropertyDatabase.data | Bin 17688 -> 17688 bytes CharacterSets/CFUnicodeData-B.mapping | Bin 81316 -> 81316 bytes CharacterSets/CFUnicodeData-L.mapping | Bin 81316 -> 81316 bytes Collections.subproj/CFArray.c | 298 +++- Collections.subproj/CFArray.h | 6 +- Collections.subproj/CFBag.c | 142 +- Collections.subproj/CFBag.h | 6 +- Collections.subproj/CFBinaryHeap.c | 84 +- Collections.subproj/CFBinaryHeap.h | 6 +- Collections.subproj/CFBitVector.c | 18 +- Collections.subproj/CFBitVector.h | 6 +- Collections.subproj/CFData.c | 28 +- Collections.subproj/CFData.h | 6 +- Collections.subproj/CFDictionary.c | 380 +++-- Collections.subproj/CFDictionary.h | 6 +- Collections.subproj/CFSet.c | 825 ++++++---- Collections.subproj/CFSet.h | 6 +- Collections.subproj/CFStorage.c | 314 ++-- Collections.subproj/CFStorage.h | 6 +- Collections.subproj/CFTree.c | 114 +- Collections.subproj/CFTree.h | 6 +- Locale.subproj/CFLocale.h | 14 +- Makefile | 468 ++---- NumberDate.subproj/CFDate.c | 98 +- NumberDate.subproj/CFDate.h | 6 +- NumberDate.subproj/CFNumber.c | 113 +- NumberDate.subproj/CFNumber.h | 6 +- NumberDate.subproj/CFTimeZone.c | 368 +++-- NumberDate.subproj/CFTimeZone.h | 6 +- Parsing.subproj/CFBinaryPList.c | 181 ++- Parsing.subproj/CFPropertyList.c | 215 ++- Parsing.subproj/CFPropertyList.h | 32 +- Parsing.subproj/CFXMLInputStream.c | 11 +- Parsing.subproj/CFXMLInputStream.h | 6 +- Parsing.subproj/CFXMLNode.c | 4 +- Parsing.subproj/CFXMLNode.h | 8 +- Parsing.subproj/CFXMLParser.c | 47 +- Parsing.subproj/CFXMLParser.h | 13 +- Parsing.subproj/CFXMLTree.c | 4 +- PlugIn.subproj/CFBundle.c | 694 +++++--- PlugIn.subproj/CFBundle.h | 9 +- PlugIn.subproj/CFBundlePriv.h | 16 +- PlugIn.subproj/CFBundle_BinaryTypes.h | 18 +- PlugIn.subproj/CFBundle_Internal.h | 9 +- PlugIn.subproj/CFBundle_Resources.c | 847 ++++++---- PlugIn.subproj/CFPlugIn.c | 4 +- PlugIn.subproj/CFPlugIn.h | 6 +- PlugIn.subproj/CFPlugInCOM.h | 6 +- PlugIn.subproj/CFPlugIn_Factory.c | 6 +- PlugIn.subproj/CFPlugIn_Factory.h | 6 +- PlugIn.subproj/CFPlugIn_Instance.c | 4 +- PlugIn.subproj/CFPlugIn_PlugIn.c | 17 +- .../CFApplicationPreferences.c | 718 +++++++++ Preferences.subproj/CFPreferences.c | 843 ++++++++++ Preferences.subproj/CFPreferences.h | 147 ++ Preferences.subproj/CFXMLPreferencesDomain.c | 570 +++++++ README | 94 ++ RunLoop.subproj/CFMachPort.c | 19 +- RunLoop.subproj/CFMachPort.h | 6 +- RunLoop.subproj/CFMessagePort.c | 8 +- RunLoop.subproj/CFMessagePort.h | 6 +- RunLoop.subproj/CFRunLoop.c | 862 +++++++--- RunLoop.subproj/CFRunLoop.h | 34 +- RunLoop.subproj/CFRunLoopPriv.h | 48 + RunLoop.subproj/CFSocket.c | 1125 +++++++++---- RunLoop.subproj/CFSocket.h | 21 +- RunLoop.subproj/CFWindowsMessageQueue.c | 69 +- RunLoop.subproj/CFWindowsMessageQueue.h | 6 +- Stream.subproj/CFConcreteStreams.c | 798 ++++++++++ Stream.subproj/CFSocketStream.c | 211 +++ Stream.subproj/CFStream.c | 1407 +++++++++++++++++ Stream.subproj/CFStream.h | 280 ++++ Stream.subproj/CFStreamAbstract.h | 156 ++ Stream.subproj/CFStreamPriv.h | 173 ++ String.subproj/CFCharacterSet.c | 56 +- String.subproj/CFCharacterSet.h | 6 +- String.subproj/CFCharacterSetPriv.h | 7 +- String.subproj/CFString.c | 1326 ++++++++++------ String.subproj/CFString.h | 69 +- String.subproj/CFStringDefaultEncoding.h | 202 +++ String.subproj/CFStringEncodingExt.h | 209 +++ String.subproj/CFStringEncodings.c | 612 ++++--- String.subproj/CFStringScanner.c | 8 +- String.subproj/CFStringUtilities.c | 361 +++-- StringEncodings.subproj/CFBuiltinConverters.c | 184 ++- .../CFStringEncodingConverter.c | 24 +- .../CFStringEncodingConverter.h | 6 +- .../CFStringEncodingConverterExt.h | 8 +- .../CFStringEncodingConverterPriv.h | 20 +- StringEncodings.subproj/CFUniChar.c | 103 +- StringEncodings.subproj/CFUniChar.h | 83 +- StringEncodings.subproj/CFUniCharPriv.h | 8 +- .../CFUnicodeDecomposition.c | 70 +- .../CFUnicodeDecomposition.h | 20 +- .../CFUnicodePrecomposition.c | 70 +- .../CFUnicodePrecomposition.h | 12 +- URL.subproj/CFURL.c | 1303 ++++++--------- URL.subproj/CFURL.h | 9 +- URL.subproj/CFURLAccess.c | 95 +- URL.subproj/CFURLAccess.h | 12 +- framework.make | 626 ++++++++ version.c | 7 +- 128 files changed, 15985 insertions(+), 5640 deletions(-) mode change 100644 => 100755 APPLE_LICENSE create mode 100644 AppServices.subproj/CFStream.h create mode 100644 AppServices.subproj/CFStreamPriv.h create mode 100644 Base.subproj/CFSystemDirectories.c create mode 100644 Base.subproj/CFUtilitiesPriv.h create mode 100644 Base.subproj/auto_stubs.h create mode 100644 Preferences.subproj/CFApplicationPreferences.c create mode 100644 Preferences.subproj/CFPreferences.c create mode 100644 Preferences.subproj/CFPreferences.h create mode 100644 Preferences.subproj/CFXMLPreferencesDomain.c create mode 100644 README create mode 100644 RunLoop.subproj/CFRunLoopPriv.h create mode 100644 Stream.subproj/CFConcreteStreams.c create mode 100644 Stream.subproj/CFSocketStream.c create mode 100644 Stream.subproj/CFStream.c create mode 100644 Stream.subproj/CFStream.h create mode 100644 Stream.subproj/CFStreamAbstract.h create mode 100644 Stream.subproj/CFStreamPriv.h create mode 100644 String.subproj/CFStringDefaultEncoding.h create mode 100644 String.subproj/CFStringEncodingExt.h create mode 100755 framework.make diff --git a/APPLE_LICENSE b/APPLE_LICENSE old mode 100644 new mode 100755 diff --git a/AppServices.subproj/CFStream.h b/AppServices.subproj/CFStream.h new file mode 100644 index 0000000..471deaf --- /dev/null +++ b/AppServices.subproj/CFStream.h @@ -0,0 +1,280 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFStream.h + Copyright (c) 2000-2005, Apple, Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFSTREAM__) +#define __COREFOUNDATION_CFSTREAM__ 1 + +#include +#include +#include +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +typedef enum { + kCFStreamStatusNotOpen = 0, + kCFStreamStatusOpening, /* open is in-progress */ + kCFStreamStatusOpen, + kCFStreamStatusReading, + kCFStreamStatusWriting, + kCFStreamStatusAtEnd, /* no further bytes can be read/written */ + kCFStreamStatusClosed, + kCFStreamStatusError +} CFStreamStatus; + +typedef enum { + kCFStreamErrorDomainCustom = -1, /* custom to the kind of stream in question */ + kCFStreamErrorDomainPOSIX = 1, /* POSIX errno; interpret using */ + kCFStreamErrorDomainMacOSStatus /* OSStatus type from Carbon APIs; interpret using */ +} CFStreamErrorDomain; + +typedef struct { + CFStreamErrorDomain domain; + SInt32 error; +} CFStreamError; + +typedef enum { + kCFStreamEventNone = 0, + kCFStreamEventOpenCompleted = 1, + kCFStreamEventHasBytesAvailable = 2, + kCFStreamEventCanAcceptBytes = 4, + kCFStreamEventErrorOccurred = 8, + kCFStreamEventEndEncountered = 16 +} CFStreamEventType; + +typedef struct { + CFIndex version; + void *info; + void *(*retain)(void *info); + void (*release)(void *info); + CFStringRef (*copyDescription)(void *info); +} CFStreamClientContext; + +typedef struct __CFReadStream * CFReadStreamRef; +typedef struct __CFWriteStream * CFWriteStreamRef; + +typedef void (*CFReadStreamClientCallBack)(CFReadStreamRef stream, CFStreamEventType type, void *clientCallBackInfo); +typedef void (*CFWriteStreamClientCallBack)(CFWriteStreamRef stream, CFStreamEventType type, void *clientCallBackInfo); + +CF_EXPORT +CFTypeID CFReadStreamGetTypeID(void); +CF_EXPORT +CFTypeID CFWriteStreamGetTypeID(void); + +/* Memory streams */ + +/* Value will be a CFData containing all bytes thusfar written; used to recover the data written to a memory write stream. */ +CF_EXPORT +const CFStringRef kCFStreamPropertyDataWritten; + +/* Pass kCFAllocatorNull for bytesDeallocator to prevent CFReadStream from deallocating bytes; otherwise, CFReadStream will deallocate bytes when the stream is destroyed */ +CF_EXPORT +CFReadStreamRef CFReadStreamCreateWithBytesNoCopy(CFAllocatorRef alloc, const UInt8 *bytes, CFIndex length, CFAllocatorRef bytesDeallocator); + +/* The stream writes into the buffer given; when bufferCapacity is exhausted, the stream is exhausted (status becomes kCFStreamStatusAtEnd) */ +CF_EXPORT +CFWriteStreamRef CFWriteStreamCreateWithBuffer(CFAllocatorRef alloc, UInt8 *buffer, CFIndex bufferCapacity); + +/* New buffers are allocated from bufferAllocator as bytes are written to the stream. At any point, you can recover the bytes thusfar written by asking for the property kCFStreamPropertyDataWritten, above */ +CF_EXPORT +CFWriteStreamRef CFWriteStreamCreateWithAllocatedBuffers(CFAllocatorRef alloc, CFAllocatorRef bufferAllocator); + +/* File streams */ +CF_EXPORT +CFReadStreamRef CFReadStreamCreateWithFile(CFAllocatorRef alloc, CFURLRef fileURL); +CF_EXPORT +CFWriteStreamRef CFWriteStreamCreateWithFile(CFAllocatorRef alloc, CFURLRef fileURL); + +#if MAC_OS_X_VERSION_10_2 <= MAC_OS_X_VERSION_MAX_ALLOWED +/* Property for file write streams; value should be a CFBoolean. Set to TRUE to append to a file, rather than to replace its contents */ +CF_EXPORT +const CFStringRef kCFStreamPropertyAppendToFile; +#endif + +#if MAC_OS_X_VERSION_10_3 <= MAC_OS_X_VERSION_MAX_ALLOWED + +CF_EXPORT const CFStringRef kCFStreamPropertyFileCurrentOffset AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; // Value is a CFNumber + +#endif + +/* Socket stream properties */ + +/* Value will be a CFData containing the native handle */ +CF_EXPORT +const CFStringRef kCFStreamPropertySocketNativeHandle; + +/* Value will be a CFString, or NULL if unknown */ +CF_EXPORT +const CFStringRef kCFStreamPropertySocketRemoteHostName; + +/* Value will be a CFNumber, or NULL if unknown */ +CF_EXPORT +const CFStringRef kCFStreamPropertySocketRemotePortNumber; + +/* Socket streams; the returned streams are paired such that they use the same socket; pass NULL if you want only the read stream or the write stream */ +CF_EXPORT +void CFStreamCreatePairWithSocket(CFAllocatorRef alloc, CFSocketNativeHandle sock, CFReadStreamRef *readStream, CFWriteStreamRef *writeStream); +CF_EXPORT +void CFStreamCreatePairWithSocketToHost(CFAllocatorRef alloc, CFStringRef host, UInt32 port, CFReadStreamRef *readStream, CFWriteStreamRef *writeStream); +#if MAC_OS_X_VERSION_10_2 <= MAC_OS_X_VERSION_MAX_ALLOWED +CF_EXPORT +void CFStreamCreatePairWithPeerSocketSignature(CFAllocatorRef alloc, const CFSocketSignature *signature, CFReadStreamRef *readStream, CFWriteStreamRef *writeStream); +#endif + + +/* Returns the current state of the stream */ +CF_EXPORT +CFStreamStatus CFReadStreamGetStatus(CFReadStreamRef stream); +CF_EXPORT +CFStreamStatus CFWriteStreamGetStatus(CFWriteStreamRef stream); + +/* 0 is returned if no error has occurred. errorDomain specifies the domain + in which the error code should be interpretted; pass NULL if you are not + interested. */ +CF_EXPORT +CFStreamError CFReadStreamGetError(CFReadStreamRef stream); +CF_EXPORT +CFStreamError CFWriteStreamGetError(CFWriteStreamRef stream); + +/* Returns success/failure. Opening a stream causes it to reserve all the system + resources it requires. If the stream can open non-blocking, this will always + return TRUE; listen to the run loop source to find out when the open completes + and whether it was successful, or poll using CFRead/WriteStreamGetStatus(), waiting + for a status of kCFStreamStatusOpen or kCFStreamStatusError. */ +CF_EXPORT +Boolean CFReadStreamOpen(CFReadStreamRef stream); +CF_EXPORT +Boolean CFWriteStreamOpen(CFWriteStreamRef stream); + +/* Terminates the flow of bytes; releases any system resources required by the + stream. The stream may not fail to close. You may call CFStreamClose() to + effectively abort a stream. */ +CF_EXPORT +void CFReadStreamClose(CFReadStreamRef stream); +CF_EXPORT +void CFWriteStreamClose(CFWriteStreamRef stream); + +/* Whether there is data currently available for reading; returns TRUE if it's + impossible to tell without trying */ +CF_EXPORT +Boolean CFReadStreamHasBytesAvailable(CFReadStreamRef stream); + +/* Returns the number of bytes read, or -1 if an error occurs preventing any + bytes from being read, or 0 if the stream's end was encountered. + It is an error to try and read from a stream that hasn't been opened first. + This call will block until at least one byte is available; it will NOT block + until the entire buffer can be filled. To avoid blocking, either poll using + CFReadStreamHasBytesAvailable() or use the run loop and listen for the + kCFStreamCanRead event for notification of data available. */ +CF_EXPORT +CFIndex CFReadStreamRead(CFReadStreamRef stream, UInt8 *buffer, CFIndex bufferLength); + +/* Returns a pointer to an internal buffer if possible (setting *numBytesRead + to the length of the returned buffer), otherwise returns NULL; guaranteed + to return in O(1). Bytes returned in the buffer are considered read from + the stream; if maxBytesToRead is greater than 0, not more than maxBytesToRead + will be returned. If maxBytesToRead is less than or equal to zero, as many bytes + as are readily available will be returned. The returned buffer is good only + until the next stream operation called on the stream. Caller should neither + change the contents of the returned buffer nor attempt to deallocate the buffer; + it is still owned by the stream. */ +CF_EXPORT +const UInt8 *CFReadStreamGetBuffer(CFReadStreamRef stream, CFIndex maxBytesToRead, CFIndex *numBytesRead); + +/* Whether the stream can currently be written to without blocking; + returns TRUE if it's impossible to tell without trying */ +CF_EXPORT +Boolean CFWriteStreamCanAcceptBytes(CFWriteStreamRef stream); + +/* Returns the number of bytes successfully written, -1 if an error has + occurred, or 0 if the stream has been filled to capacity (for fixed-length + streams). If the stream is not full, this call will block until at least + one byte is written. To avoid blocking, either poll via CFWriteStreamCanAcceptBytes + or use the run loop and listen for the kCFStreamCanWrite event. */ +CF_EXPORT +CFIndex CFWriteStreamWrite(CFWriteStreamRef stream, const UInt8 *buffer, CFIndex bufferLength); + +/* Particular streams can name properties and assign meanings to them; you + access these properties through the following calls. A property is any interesting + information about the stream other than the data being transmitted itself. + Examples include the headers from an HTTP transmission, or the expected + number of bytes, or permission information, etc. Properties that can be set + configure the behavior of the stream, and may only be settable at particular times + (like before the stream has been opened). See the documentation for particular + properties to determine their get- and set-ability. */ +CF_EXPORT +CFTypeRef CFReadStreamCopyProperty(CFReadStreamRef stream, CFStringRef propertyName); +CF_EXPORT +CFTypeRef CFWriteStreamCopyProperty(CFWriteStreamRef stream, CFStringRef propertyName); + +#if MAC_OS_X_VERSION_10_2 <= MAC_OS_X_VERSION_MAX_ALLOWED +/* Returns TRUE if the stream recognizes and accepts the given property-value pair; + FALSE otherwise. */ +CF_EXPORT +Boolean CFReadStreamSetProperty(CFReadStreamRef stream, CFStringRef propertyName, CFTypeRef propertyValue); +CF_EXPORT +Boolean CFWriteStreamSetProperty(CFWriteStreamRef stream, CFStringRef propertyName, CFTypeRef propertyValue); +#endif + +/* Asynchronous processing - If you wish to neither poll nor block, you may register + a client to hear about interesting events that occur on a stream. Only one client + per stream is allowed; registering a new client replaces the previous one. + + Once you have set a client, you need to schedule a run loop on which that client + can be notified. You may schedule multiple run loops (for instance, if you are + using a thread pool). The client callback will be triggered via one of the scheduled + run loops; It is the caller's responsibility to ensure that at least one of the + scheduled run loops is being run. + + NOTE: not all streams provide these notifications. If a stream does not support + asynchronous notification, CFStreamSetClient() will return NO; typically, such + streams will never block for device I/O (e.g. a stream on memory) +*/ + +CF_EXPORT +Boolean CFReadStreamSetClient(CFReadStreamRef stream, CFOptionFlags streamEvents, CFReadStreamClientCallBack clientCB, CFStreamClientContext *clientContext); +CF_EXPORT +Boolean CFWriteStreamSetClient(CFWriteStreamRef stream, CFOptionFlags streamEvents, CFWriteStreamClientCallBack clientCB, CFStreamClientContext *clientContext); + +CF_EXPORT +void CFReadStreamScheduleWithRunLoop(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode); +CF_EXPORT +void CFWriteStreamScheduleWithRunLoop(CFWriteStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode); + +CF_EXPORT +void CFReadStreamUnscheduleFromRunLoop(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode); +CF_EXPORT +void CFWriteStreamUnscheduleFromRunLoop(CFWriteStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode); + +#if defined(__cplusplus) +} +#endif + +#endif /* ! __COREFOUNDATION_CFSTREAM__ */ diff --git a/AppServices.subproj/CFStreamPriv.h b/AppServices.subproj/CFStreamPriv.h new file mode 100644 index 0000000..3106dc5 --- /dev/null +++ b/AppServices.subproj/CFStreamPriv.h @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFStreamPriv.h + Copyright (c) 2000-2005, Apple, Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFSTREAMPRIV__) +#define __COREFOUNDATION_CFSTREAMPRIV__ 1 + +#include +#include +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +struct _CFStream; +struct _CFStreamClient { + CFStreamClientContext cbContext; + void (*cb)(struct _CFStream *, CFStreamEventType, void *); + CFOptionFlags when; + CFRunLoopSourceRef rlSource; + CFMutableArrayRef runLoopsAndModes; + CFOptionFlags whatToSignal; +}; + +// A unified set of callbacks so we can use a single structure for all struct _CFStreams. +struct _CFStreamCallBacks { + CFIndex version; + void *(*create)(struct _CFStream *stream, void *info); + void (*finalize)(struct _CFStream *stream, void *info); + CFStringRef (*copyDescription)(struct _CFStream *stream, void *info); + Boolean (*open)(struct _CFStream *stream, CFStreamError *error, Boolean *openComplete, void *info); + Boolean (*openCompleted)(struct _CFStream *stream, CFStreamError *error, void *info); + CFIndex (*read)(CFReadStreamRef stream, UInt8 *buffer, CFIndex bufferLength, CFStreamError *error, Boolean *atEOF, void *info); + const UInt8 *(*getBuffer)(CFReadStreamRef sream, CFIndex maxBytesToRead, CFIndex *numBytesRead, CFStreamError *error, Boolean *atEOF, void *info); + Boolean (*canRead)(CFReadStreamRef, void *info); + CFIndex (*write)(CFWriteStreamRef, const UInt8 *buffer, CFIndex bufferLength, CFStreamError *error, void *info); + Boolean (*canWrite)(CFWriteStreamRef, void *info); + void (*close)(struct _CFStream *stream, void *info); + CFTypeRef (*copyProperty)(struct _CFStream *stream, CFStringRef propertyName, void *info); + Boolean (*setProperty)(struct _CFStream *stream, CFStringRef propertyName, CFTypeRef propertyValue, void *info); + void (*requestEvents)(struct _CFStream *stream, CFOptionFlags events, void *info); + void (*schedule)(struct _CFStream *stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info); + void (*unschedule)(struct _CFStream *stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info); +}; + +struct _CFStream { + CFRuntimeBase _cfBase; + CFOptionFlags flags; + CFStreamError error; + struct _CFStreamClient *client; + void *info; + const struct _CFStreamCallBacks *callBacks; // This will not exist (will not be allocated) if the callbacks are from our known, "blessed" set. + void *_reserved1; +}; + +CF_INLINE void *_CFStreamGetInfoPointer(struct _CFStream *stream) { + return stream->info; +} + +// cb version must be 1 +CF_EXPORT struct _CFStream *_CFStreamCreateWithConstantCallbacks(CFAllocatorRef alloc, void *info, const struct _CFStreamCallBacks *cb, Boolean isReading); + +// Only available for streams created with _CFStreamCreateWithConstantCallbacks, above. cb's version must be 1 +CF_EXPORT void _CFStreamSetInfoPointer(struct _CFStream *stream, void *info, const struct _CFStreamCallBacks *cb); + + +/* +** _CFStreamSourceScheduleWithRunLoop +** +** Schedules the given run loop source on the given run loop and mode. It then +** adds the loop and mode pair to the runLoopsAndModes list. The list is +** simply a linear list of a loop reference followed by a mode reference. +** +** source Run loop source to be scheduled +** +** runLoopsAndModes List of run loop/mode pairs on which the source is scheduled +** +** runLoop Run loop on which the source is being scheduled +** +** runLoopMode Run loop mode on which the source is being scheduled +*/ +CF_EXPORT +void _CFStreamSourceScheduleWithRunLoop(CFRunLoopSourceRef source, CFMutableArrayRef runLoopsAndModes, CFRunLoopRef runLoop, CFStringRef runLoopMode); + + +/* +** _CFStreamSourceUnscheduleFromRunLoop +** +** Unschedule the given source from the given run loop and mode. It then will +** guarantee that the source remains scheduled on the list of run loop and mode +** pairs in the runLoopsAndModes list. The list is simply a linear list of a +** loop reference followed by a mode reference. +** +** source Run loop source to be unscheduled +** +** runLoopsAndModes List of run loop/mode pairs on which the source is scheduled +** +** runLoop Run loop from which the source is being unscheduled +** +** runLoopMode Run loop mode from which the source is being unscheduled +*/ +CF_EXPORT +void _CFStreamSourceUnscheduleFromRunLoop(CFRunLoopSourceRef source, CFMutableArrayRef runLoopsAndModes, CFRunLoopRef runLoop, CFStringRef runLoopMode); + + +/* +** _CFStreamSourceScheduleWithAllRunLoops +** +** Schedules the given run loop source on all the run loops and modes in the list. +** The list is simply a linear list of a loop reference followed by a mode reference. +** +** source Run loop source to be unscheduled +** +** runLoopsAndModes List of run loop/mode pairs on which the source is scheduled +*/ +CF_EXPORT +void _CFStreamSourceScheduleWithAllRunLoops(CFRunLoopSourceRef source, CFArrayRef runLoopsAndModes); + + +/* +** _CFStreamSourceUnscheduleFromRunLoop +** +** Unschedule the given source from all the run loops and modes in the list. +** The list is simply a linear list of a loop reference followed by a mode +** reference. +** +** source Run loop source to be unscheduled +** +** runLoopsAndModes List of run loop/mode pairs on which the source is scheduled +*/ +CF_EXPORT +void _CFStreamSourceUncheduleFromAllRunLoops(CFRunLoopSourceRef source, CFArrayRef runLoopsAndModes); + + +#define SECURITY_NONE (0) +#define SECURITY_SSLv2 (1) +#define SECURITY_SSLv3 (2) +#define SECURITY_SSLv32 (3) +#define SECURITY_TLS (4) + +extern const int kCFStreamErrorDomainSSL; + +#if defined(__cplusplus) +} +#endif + +#endif /* ! __COREFOUNDATION_CFSTREAMPRIV__ */ + diff --git a/AppServices.subproj/CFUserNotification.c b/AppServices.subproj/CFUserNotification.c index fca188f..35e9d19 100644 --- a/AppServices.subproj/CFUserNotification.c +++ b/AppServices.subproj/CFUserNotification.c @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -78,7 +76,9 @@ struct __CFUserNotification { CFOptionFlags _responseFlags; CFStringRef _sessionID; CFDictionaryRef _responseDictionary; +#if defined(__MACH__) CFMachPortRef _machPort; +#endif CFUserNotificationCallBack _callout; }; @@ -108,20 +108,10 @@ static CFStringRef __CFUserNotificationCopyDescription(CFTypeRef cf) { #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 void __CFUserNotificationDeallocate(CFTypeRef cf); + static const CFRuntimeClass __CFUserNotificationClass = { 0, "CFUserNotification", @@ -144,6 +134,18 @@ CFTypeID CFUserNotificationGetTypeID(void) { #if defined(__MACH__) +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); +} + static void _CFUserNotificationAddToDictionary(const void *key, const void *value, void *context) { if (CFGetTypeID(key) == CFStringGetTypeID()) CFDictionarySetValue((CFMutableDictionaryRef)context, key, value); } @@ -298,10 +300,12 @@ static void _CFUserNotificationMachPortCallBack(CFMachPortRef port, void *m, CFI 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); + if (msg->header.msgh_size > sizeof(mach_msg_base_t)) { + 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); @@ -331,10 +335,12 @@ SInt32 CFUserNotificationReceiveResponse(CFUserNotificationRef userNotification, } 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 (msg->header.msgh_size > sizeof(mach_msg_base_t)) { + 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); diff --git a/AppServices.subproj/CFUserNotification.h b/AppServices.subproj/CFUserNotification.h index 6fd7f62..417cdd5 100644 --- a/AppServices.subproj/CFUserNotification.h +++ b/AppServices.subproj/CFUserNotification.h @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -23,7 +21,7 @@ * @APPLE_LICENSE_HEADER_END@ */ /* CFUserNotification.h - Copyright (c) 2000-2003, Apple, Inc. All rights reserved. + Copyright (c) 2000-2005, Apple, Inc. All rights reserved. */ #if !defined(__COREFOUNDATION_CFUSERNOTIFICATION__) diff --git a/Base.subproj/CFBase.c b/Base.subproj/CFBase.c index 076924c..92691f9 100644 --- a/Base.subproj/CFBase.c +++ b/Base.subproj/CFBase.c @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -38,14 +36,17 @@ #include #endif #if defined(__MACH__) - #include #include + extern size_t malloc_good_size(size_t size); #include + #include #endif #include #include -extern size_t malloc_good_size(size_t size); +#if defined(__CYGWIN32__) || defined (D__CYGWIN_) +#error CoreFoundation is currently built with the Microsoft C Runtime, which is incompatible with the Cygwin DLL. You must either use the -mno-cygwin flag, or complete a port of CF to the Cygwin environment. +#endif // -------- -------- -------- -------- -------- -------- -------- -------- @@ -244,6 +245,10 @@ static void *__CFAllocatorSystemReallocate(void *ptr, CFIndex newsize, CFOptionF } static void __CFAllocatorSystemDeallocate(void *ptr, void *info) { +#if defined(DEBUG) + size_t size = malloc_size(ptr); + if (size) memset(ptr, 0xCC, size); +#endif malloc_zone_free(info, ptr); } @@ -294,6 +299,26 @@ static struct __CFAllocator __kCFAllocatorMalloc = { {0, NULL, NULL, NULL, NULL, (void *)malloc, (void *)realloc, (void *)free, NULL} }; +static struct __CFAllocator __kCFAllocatorMallocZone = { + {NULL, 0, 0x0080}, +#if defined(__MACH__) + __CFAllocatorCustomSize, + __CFAllocatorCustomMalloc, + __CFAllocatorCustomCalloc, + __CFAllocatorCustomValloc, + __CFAllocatorCustomFree, + __CFAllocatorCustomRealloc, + __CFAllocatorNullDestroy, + "kCFAllocatorMallocZone", + NULL, + NULL, + &__CFAllocatorZoneIntrospect, + NULL, +#endif + NULL, // _allocator + {0, NULL, NULL, NULL, NULL, __CFAllocatorSystemAllocate, __CFAllocatorSystemReallocate, __CFAllocatorSystemDeallocate, NULL} +}; + static struct __CFAllocator __kCFAllocatorSystemDefault = { {NULL, 0, 0x0080}, #if defined(__MACH__) @@ -337,9 +362,12 @@ static struct __CFAllocator __kCFAllocatorNull = { const CFAllocatorRef kCFAllocatorDefault = NULL; const CFAllocatorRef kCFAllocatorSystemDefault = &__kCFAllocatorSystemDefault; const CFAllocatorRef kCFAllocatorMalloc = &__kCFAllocatorMalloc; +const CFAllocatorRef kCFAllocatorMallocZone = &__kCFAllocatorMallocZone; const CFAllocatorRef kCFAllocatorNull = &__kCFAllocatorNull; const CFAllocatorRef kCFAllocatorUseContext = (CFAllocatorRef)0x0227; +bool kCFUseCollectableAllocator = false; + static CFStringRef __CFAllocatorCopyDescription(CFTypeRef cf) { CFAllocatorRef self = cf; CFAllocatorRef allocator = (kCFAllocatorUseContext == self->_allocator) ? self : self->_allocator; @@ -396,15 +424,20 @@ __private_extern__ void __CFAllocatorInitialize(void) { _CFRuntimeSetInstanceTypeID(&__kCFAllocatorSystemDefault, __kCFAllocatorTypeID); __kCFAllocatorSystemDefault._base._isa = __CFISAForTypeID(__kCFAllocatorTypeID); #if defined(__MACH__) - __kCFAllocatorSystemDefault._context.info = malloc_default_zone(); + __kCFAllocatorSystemDefault._context.info = (CF_USING_COLLECTABLE_MEMORY ? __CFCollectableZone : malloc_default_zone()); + memset(malloc_default_zone(), 0, 8); #endif __kCFAllocatorSystemDefault._allocator = kCFAllocatorSystemDefault; - memset(malloc_default_zone(), 0, 8); _CFRuntimeSetInstanceTypeID(&__kCFAllocatorMalloc, __kCFAllocatorTypeID); __kCFAllocatorMalloc._base._isa = __CFISAForTypeID(__kCFAllocatorTypeID); __kCFAllocatorMalloc._allocator = kCFAllocatorSystemDefault; + _CFRuntimeSetInstanceTypeID(&__kCFAllocatorMallocZone, __kCFAllocatorTypeID); + __kCFAllocatorMallocZone._base._isa = __CFISAForTypeID(__kCFAllocatorTypeID); + __kCFAllocatorMallocZone._allocator = kCFAllocatorSystemDefault; + __kCFAllocatorMallocZone._context.info = malloc_default_zone(); + _CFRuntimeSetInstanceTypeID(&__kCFAllocatorNull, __kCFAllocatorTypeID); __kCFAllocatorNull._base._isa = __CFISAForTypeID(__kCFAllocatorTypeID); __kCFAllocatorNull._allocator = kCFAllocatorSystemDefault; @@ -445,16 +478,11 @@ void CFAllocatorSetDefault(CFAllocatorRef allocator) { } } -CFAllocatorRef CFAllocatorCreate(CFAllocatorRef allocator, CFAllocatorContext *context) { - struct __CFAllocator *memory; +static CFAllocatorRef __CFAllocatorCreate(CFAllocatorRef allocator, CFAllocatorContext *context) { + struct __CFAllocator *memory = NULL; 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 @@ -471,7 +499,10 @@ CFAllocatorRef CFAllocatorCreate(CFAllocatorRef allocator, CFAllocatorContext *c } // We don't use _CFRuntimeCreateInstance() if (kCFAllocatorUseContext == allocator) { - memory = (void *)INVOKE_CALLBACK3(allocateFunc, sizeof(struct __CFAllocator), 0, retainedInfo); + memory = NULL; + if (allocateFunc) { + memory = (void *)INVOKE_CALLBACK3(allocateFunc, sizeof(struct __CFAllocator), 0, retainedInfo); + } if (NULL == memory) { return NULL; } @@ -521,9 +552,22 @@ CFAllocatorRef CFAllocatorCreate(CFAllocatorRef allocator, CFAllocatorContext *c return memory; } +CFAllocatorRef CFAllocatorCreate(CFAllocatorRef allocator, CFAllocatorContext *context) { + CFAssert1(!CF_USING_COLLECTABLE_MEMORY, __kCFLogAssertion, "%s(): Shouldn't be called when GC is enabled!", __PRETTY_FUNCTION__); +#if defined(DEBUG) + if (CF_USING_COLLECTABLE_MEMORY) + HALT; +#endif + return __CFAllocatorCreate(allocator, context); +} + +CFAllocatorRef _CFAllocatorCreateGC(CFAllocatorRef allocator, CFAllocatorContext *context) { + return __CFAllocatorCreate(allocator, context); +} + void *CFAllocatorAllocate(CFAllocatorRef allocator, CFIndex size, CFOptionFlags hint) { CFAllocatorAllocateCallBack allocateFunc; - void *newptr; + void *newptr = NULL; allocator = (NULL == allocator) ? __CFGetDefaultAllocator() : allocator; #if defined(__MACH__) && defined(DEBUG) if (allocator->_base._isa == __CFISAForTypeID(__kCFAllocatorTypeID)) { @@ -538,8 +582,15 @@ void *CFAllocatorAllocate(CFAllocatorRef allocator, CFIndex size, CFOptionFlags return malloc_zone_malloc((malloc_zone_t *)allocator, size); } #endif - allocateFunc = __CFAllocatorGetAllocateFunction(&allocator->_context); - newptr = (void *)INVOKE_CALLBACK3(allocateFunc, size, hint, allocator->_context.info); + if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { + newptr = auto_zone_allocate_object((auto_zone_t*)allocator->_context.info, size, (auto_memory_type_t)hint, true, false); + } else { + newptr = NULL; + allocateFunc = __CFAllocatorGetAllocateFunction(&allocator->_context); + if (allocateFunc) { + newptr = (void *)INVOKE_CALLBACK3(allocateFunc, size, hint, allocator->_context.info); + } + } return newptr; } @@ -562,13 +613,20 @@ void *CFAllocatorReallocate(CFAllocatorRef allocator, void *ptr, CFIndex newsize return malloc_zone_malloc((malloc_zone_t *)allocator, newsize); } #endif + newptr = NULL; allocateFunc = __CFAllocatorGetAllocateFunction(&allocator->_context); - newptr = (void *)INVOKE_CALLBACK3(allocateFunc, newsize, hint, allocator->_context.info); + if (allocateFunc) { + 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 * +#if defined(DEBUG) + size_t size = malloc_size(ptr); + if (size) memset(ptr, 0xCC, size); +#endif malloc_zone_free((malloc_zone_t *)allocator, ptr); return NULL; } @@ -603,6 +661,10 @@ void CFAllocatorDeallocate(CFAllocatorRef allocator, void *ptr) { #endif #if defined(__MACH__) if (allocator->_base._isa != __CFISAForTypeID(__kCFAllocatorTypeID)) { // malloc_zone_t * +#if defined(DEBUG) + size_t size = malloc_size(ptr); + if (size) memset(ptr, 0xCC, size); +#endif return malloc_zone_free((malloc_zone_t *)allocator, ptr); } #endif @@ -669,6 +731,34 @@ void CFAllocatorGetContext(CFAllocatorRef allocator, CFAllocatorContext *context context->preferredSize = (void *)((uintptr_t)context->preferredSize & ~0x3); } +void *_CFAllocatorAllocateGC(CFAllocatorRef allocator, CFIndex size, CFOptionFlags hint) +{ + if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) + return auto_zone_allocate_object((auto_zone_t*)kCFAllocatorSystemDefault->_context.info, size, (auto_memory_type_t)hint, false, false); + else + return CFAllocatorAllocate(allocator, size, hint); +} + +void *_CFAllocatorReallocateGC(CFAllocatorRef allocator, void *ptr, CFIndex newsize, CFOptionFlags hint) +{ + if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { + if (ptr && (newsize == 0)) { + return NULL; // equivalent to _CFAllocatorDeallocateGC. + } + if (ptr == NULL) { + return auto_zone_allocate_object((auto_zone_t*)kCFAllocatorSystemDefault->_context.info, newsize, (auto_memory_type_t)hint, false, false); // eq. to _CFAllocator + } + } + // otherwise, auto_realloc() now preserves layout type and refCount. + return CFAllocatorReallocate(allocator, ptr, newsize, hint); +} + +void _CFAllocatorDeallocateGC(CFAllocatorRef allocator, void *ptr) +{ + // when running GC, don't deallocate. + if (!CF_IS_COLLECTABLE_ALLOCATOR(allocator)) CFAllocatorDeallocate(allocator, ptr); +} + // -------- -------- -------- -------- -------- -------- -------- -------- #if defined(__MACH__) || defined(__LINUX__) || defined(__FREEBSD__) @@ -679,8 +769,13 @@ __private_extern__ DWORD __CFTSDKey = 0xFFFFFFFF; #endif // Called for each thread as it exits -static void __CFFinalizeThreadData(void *arg) { +__private_extern__ void __CFFinalizeThreadData(void *arg) { +#if defined(__MACH__) || defined(__LINUX__) || defined(__FREEBSD__) __CFThreadSpecificData *tsd = (__CFThreadSpecificData *)arg; +#elif defined(__WIN32) + __CFThreadSpecificData *tsd = TlsGetValue(__CFTSDKey); + TlsSetValue(__CFTSDKey, NULL); +#endif if (NULL == tsd) return; if (tsd->_allocator) CFRelease(tsd->_allocator); if (tsd->_runLoop) CFRelease(tsd->_runLoop); @@ -720,7 +815,14 @@ __private_extern__ void __CFBaseInitialize(void) { #if defined(__WIN32__) __CFTSDKey = TlsAlloc(); #endif + //kCFUseCollectableAllocator = objc_collecting_enabled(); +} + +#if defined(__WIN32__) +__private_extern__ void __CFBaseCleanup(void) { + TlsFree(__CFTSDKey); } +#endif CFRange __CFRangeMake(CFIndex loc, CFIndex len) { @@ -744,7 +846,7 @@ struct __CFNull { }; static struct __CFNull __kCFNull = { - {NULL, 0, 0x0080} + INIT_CFRUNTIME_BASE(NULL, 0, 0x0080) }; const CFNullRef kCFNull = &__kCFNull; @@ -787,7 +889,7 @@ CFTypeID CFNullGetTypeID(void) { static int hasCFM = 0; -void _CFRuntimeSetCFMPresent(int a) { +void _CFRuntimeSetCFMPresent(void *addr) { hasCFM = 1; } @@ -797,10 +899,12 @@ void _CFRuntimeSetCFMPresent(int a) { __private_extern__ void __CF_FAULT_CALLBACK(void **ptr) { uintptr_t p = (uintptr_t)*ptr; if ((0 == p) || (p & 0x1)) return; - if (0 == hasCFM) { +// warning: revisit this address check in Chablis, and for 64-bit + if (0 == hasCFM || (0x90000000 <= p && p < 0xA0000000)) { *ptr = (void *)(p | 0x1); } else { - int __known = _dyld_image_containing_address(p); + Dl_info info; + int __known = dladdr(p, &info); *ptr = (void *)(p | (__known ? 0x1 : 0x3)); } } @@ -846,7 +950,7 @@ __asm__ ( // void __HALT(void); -#if defined(__ppc__) +#if defined(__ppc__) || defined(__ppc64__) __asm__ ( ".text\n" " .align 2\n" @@ -861,6 +965,12 @@ __asm__ ( #endif #if defined(__i386__) +#if defined(_MSC_VER) || defined(__MWERKS__) +__private_extern__ void __HALT() +{ + __asm int 3; +} +#else __asm__ ( ".text\n" " .align 2, 0x90\n" @@ -873,4 +983,5 @@ __asm__ ( " int3\n" ); #endif +#endif diff --git a/Base.subproj/CFBase.h b/Base.subproj/CFBase.h index 09f1990..1de0fff 100644 --- a/Base.subproj/CFBase.h +++ b/Base.subproj/CFBase.h @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -23,23 +21,39 @@ * @APPLE_LICENSE_HEADER_END@ */ /* CFBase.h - Copyright (c) 1998-2003, Apple, Inc. All rights reserved. + Copyright (c) 1998-2005, Apple, Inc. All rights reserved. */ #if !defined(__COREFOUNDATION_CFBASE__) #define __COREFOUNDATION_CFBASE__ 1 +#if (defined(__CYGWIN32__) || defined(_WIN32)) && !defined (__WIN32__) +#define __WIN32__ 1 +#endif + +#if defined(_MSC_VER) && defined(_M_IX86) +#define __i386__ 1 +#endif + #if defined(__WIN32__) #include #endif +#if defined(__GNUC__) #include #include +#elif defined(__WIN32__) +// mostly for the benefit of MSVC +#include +#include +#endif #include -#if defined(__MACH__) - #include -#else + #if defined(__MACH__) + #include + #endif + +#if !defined(__MACTYPES__) typedef unsigned char Boolean; typedef unsigned char UInt8; typedef signed char SInt8; @@ -58,18 +72,29 @@ typedef const unsigned char * ConstStr255Param; typedef SInt16 OSErr; typedef SInt32 OSStatus; +#endif +#if !defined(__MACTYPES__) || (defined(UNIVERSAL_INTERFACES_VERSION) && UNIVERSAL_INTERFACES_VERSION < 0x0340) typedef UInt32 UTF32Char; typedef UInt16 UTF16Char; typedef UInt8 UTF8Char; #endif + #if defined(__cplusplus) extern "C" { #endif -#if !defined(NULL) - #define NULL 0 -#endif +#ifndef NULL +#ifdef __GNUG__ +#define NULL __null +#else /* ! __GNUG__ */ +#ifndef __cplusplus +#define NULL ((void *)0) +#else /* __cplusplus */ +#define NULL 0 +#endif /* ! __cplusplus */ +#endif /* __GNUG__ */ +#endif /* ! NULL */ #if !defined(TRUE) #define TRUE 1 @@ -81,6 +106,14 @@ extern "C" { #if defined(__WIN32__) #undef CF_EXPORT +/* + We don't build as a library now, but this would be the starting point. + #if defined(CF_BUILDING_CF_AS_LIB) + // we're building CF as a library + #define CF_EXPORT extern + #elif defined(CF_BUILDING_CF) + // we're building CF as a DLL +*/ #if defined(CF_BUILDING_CF) #define CF_EXPORT __declspec(dllexport) extern #else @@ -101,6 +134,8 @@ extern "C" { #define CF_INLINE static __inline__ #elif defined(__MWERKS__) || defined(__cplusplus) #define CF_INLINE static inline + #elif defined(_MSC_VER) + #define CF_INLINE static __inline #elif defined(__WIN32__) #define CF_INLINE static __inline__ #endif @@ -111,20 +146,26 @@ 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 */ +/* Note the next two 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 +#define kCFCoreFoundationVersionNumber10_3 299.0 +#define kCFCoreFoundationVersionNumber10_3_3 299.3 +#define kCFCoreFoundationVersionNumber10_3_4 299.31 +#if defined(__ppc64__) +typedef UInt32 CFTypeID; +typedef UInt64 CFOptionFlags; +typedef UInt32 CFHashCode; +typedef SInt64 CFIndex; +#else typedef UInt32 CFTypeID; typedef UInt32 CFOptionFlags; typedef UInt32 CFHashCode; typedef SInt32 CFIndex; +#endif /* Base "type" of all "CF objects", and polymorphic functions on them */ typedef const void * CFTypeRef; @@ -220,6 +261,13 @@ const CFAllocatorRef kCFAllocatorSystemDefault; CF_EXPORT const CFAllocatorRef kCFAllocatorMalloc; +/* This allocator explicitly uses the default malloc zone, returned by + malloc_default_zone(). It should only be used when an object is + safe to be allocated in non-scanned memory. + */ +CF_EXPORT +const CFAllocatorRef kCFAllocatorMallocZone AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; + /* Null allocator which does nothing and allocates no memory. This allocator is useful as the "bytesDeallocator" in CFData or "contentsDeallocator" in CFString where the memory should not be freed. @@ -321,6 +369,9 @@ void CFRelease(CFTypeRef cf); CF_EXPORT CFIndex CFGetRetainCount(CFTypeRef cf); +CF_EXPORT +CFTypeRef CFMakeCollectable(CFTypeRef cf) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; + CF_EXPORT Boolean CFEqual(CFTypeRef cf1, CFTypeRef cf2); diff --git a/Base.subproj/CFByteOrder.h b/Base.subproj/CFByteOrder.h index af3b0d7..0cf01b1 100644 --- a/Base.subproj/CFByteOrder.h +++ b/Base.subproj/CFByteOrder.h @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -23,13 +21,13 @@ * @APPLE_LICENSE_HEADER_END@ */ /* CFByteOrder.h - Copyright (c) 1995-2003, Apple, Inc. All rights reserved. + Copyright (c) 1995-2005, Apple, Inc. All rights reserved. */ #if !defined(__COREFOUNDATION_CFBYTEORDER__) #define __COREFOUNDATION_CFBYTEORDER__ 1 -#if defined(__i386) && !defined(__LITTLE_ENDIAN__) +#if defined(__i386__) && !defined(__LITTLE_ENDIAN__) #define __LITTLE_ENDIAN__ 1 #endif diff --git a/Base.subproj/CFFileUtilities.c b/Base.subproj/CFFileUtilities.c index 64c672a..21913a3 100644 --- a/Base.subproj/CFFileUtilities.c +++ b/Base.subproj/CFFileUtilities.c @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -104,8 +102,6 @@ __private_extern__ Boolean _CFReadBytesFromFile(CFAllocatorRef alloc, CFURLRef u *bytes = NULL; -__CFSetNastyFile(url); - #if defined(__WIN32__) fd = open(path, O_RDONLY|CF_OPENFLGS, 0666|_S_IREAD); #else @@ -138,6 +134,7 @@ __CFSetNastyFile(url); } *bytes = CFAllocatorAllocate(alloc, desiredLength, 0); if (__CFOASafe) __CFSetLastAllocationEventName(*bytes, "CFUtilities (file-bytes)"); +// fcntl(fd, F_NOCACHE, 1); if (read(fd, *bytes, desiredLength) < 0) { CFAllocatorDeallocate(alloc, *bytes); close(fd); @@ -204,43 +201,27 @@ __private_extern__ CFMutableArrayRef _CFContentsOfDirectory(CFAllocatorRef alloc 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'; } + + uint8_t pathBuf[CFMaxPathSize]; + + if (!dirPath) { + if (!CFURLGetFileSystemRepresentation(dirURL, true, pathBuf, CFMaxPathLength)) { + if (extension) CFRelease(extension); + return NULL; + } else { + dirPath = pathBuf; + pathLength = strlen(dirPath); + } + } #if defined(__WIN32__) - /* Windows Implementation */ + WIN32_FIND_DATA file; + HANDLE handle; if (pathLength + 2 >= CFMaxPathLength) { if (extension) { @@ -248,21 +229,19 @@ __private_extern__ CFMutableArrayRef _CFContentsOfDirectory(CFAllocatorRef alloc } 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; + + 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; } + files = CFArrayCreateMutable(alloc, 0, &kCFTypeArrayCallBacks); do { @@ -291,7 +270,15 @@ __private_extern__ CFMutableArrayRef _CFContentsOfDirectory(CFAllocatorRef alloc #elif defined(__svr4__) || defined(__hpux__) || defined(__LINUX__) || defined(__FREEBSD__) /* Solaris and HPUX Implementation */ - + /* 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; + dirp = opendir(dirPath); if (!dirp) { if (extension) { @@ -340,22 +327,10 @@ __private_extern__ CFMutableArrayRef _CFContentsOfDirectory(CFAllocatorRef alloc } #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 */ + int fd, numread; + long basep; + char dirge[8192]; - 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) { diff --git a/Base.subproj/CFInternal.h b/Base.subproj/CFInternal.h index 5dd95b9..5cc43ae 100644 --- a/Base.subproj/CFInternal.h +++ b/Base.subproj/CFInternal.h @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -23,7 +21,7 @@ * @APPLE_LICENSE_HEADER_END@ */ /* CFInternal.h - Copyright (c) 1998-2003, Apple, Inc. All rights reserved. + Copyright (c) 1998-2005, Apple, Inc. All rights reserved. */ /* @@ -42,16 +40,34 @@ #include #include #include -#include "ForFoundationOnly.h" +#include #include "CFRuntime.h" #if defined(__MACH__) +#include #include +#include #endif #if defined(__MACH__) || defined(__LINUX__) || defined(__FREEBSD__) #include +#include #endif #include #include +#include "auto_stubs.h" +#include + +#if defined(__MACH__) +#if defined(__ppc__) +// This hack is in here because B&I kernel does not set up comm page with Tiger additions yet. +#define AtomicCompareAndSwap(mem, old, new) ({bool result; if (((void **)OSMemoryBarrier)[1] == 0x0) {result = ((*(mem) == (old)) ? (*(mem) = (new), 1) : 0);} else {result = OSAtomicCompareAndSwap32(old, new, (int32_t *)mem); OSMemoryBarrier();} result;}) +#define AtomicAdd32(mem, val) ({if (((void **)OSMemoryBarrier)[1] == 0x0) {*(mem) += (val);} else {OSAtomicAdd32(val, mem); OSMemoryBarrier();} 0;}) +#else +#define AtomicCompareAndSwap(mem, old, new) ({bool result; result = OSAtomicCompareAndSwap32(old, new, (int32_t *)mem); OSMemoryBarrier(); result;}) +#define AtomicAdd32(mem, val) ({OSAtomicAdd32(val, mem); OSMemoryBarrier(); 0;}) +#endif +#endif + +#include "ForFoundationOnly.h" #if !defined(__MACH__) #define __private_extern__ @@ -74,11 +90,16 @@ CF_EXPORT void __CFSetNastyFile(CFTypeRef cf); CF_EXPORT void _CFMachPortInstallNotifyPort(CFRunLoopRef rl, CFStringRef mode); - -#if defined(__ppc__) -#define HALT asm __volatile__("trap") +#if defined(__ppc__) || defined(__ppc64__) + #define HALT asm __volatile__("trap") #elif defined(__i386__) -#define HALT asm __volatile__("int3") + #if defined(__GNUC__) + #define HALT asm __volatile__("int3") + #elif defined(_MSC_VER) || defined(__MWERKS__) + #define HALT __asm int 3; + #else + #error Compiler not supported + #endif #endif #if defined(DEBUG) @@ -118,6 +139,7 @@ extern void __CFGenericValidateType_(CFTypeRef cf, CFTypeID type, const char *fu /* Bit manipulation macros */ /* Bits are numbered from 31 on left to 0 on right */ /* May or may not work if you use them on bitfields in types other than UInt32, bitfields the full width of a UInt32, or anything else for which they were not designed. */ +/* In the following, N1 and N2 specify an inclusive range N2..N1 with N1 >= N2 */ #define __CFBitfieldMask(N1, N2) ((((UInt32)~0UL) << (31UL - (N1) + (N2))) >> (31UL - N1)) #define __CFBitfieldGetValue(V, N1, N2) (((V) & __CFBitfieldMask(N1, N2)) >> (N2)) #define __CFBitfieldSetValue(V, N1, N2, X) ((V) = ((V) & ~__CFBitfieldMask(N1, N2)) | (((X) << (N2)) & __CFBitfieldMask(N1, N2))) @@ -136,6 +158,7 @@ typedef struct ___CFThreadSpecificData { } __CFThreadSpecificData; extern __CFThreadSpecificData *__CFGetThreadSpecificData(void); +__private_extern__ void __CFFinalizeThreadData(void *arg); #if defined(__MACH__) || defined(__LINUX__) || defined(__FREEBSD__) extern pthread_key_t __CFTSDKey; @@ -175,7 +198,7 @@ extern CFTypeID __CFGenericTypeID(const void *cf); // 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)) { + if (__builtin_expect(__CFBitfieldGetValue(((const CFRuntimeBase *)cf)->_info, 7, 7), 1)) { return kCFAllocatorSystemDefault; } return *(CFAllocatorRef *)((char *)cf - sizeof(CFAllocatorRef)); @@ -219,8 +242,6 @@ 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); @@ -231,8 +252,31 @@ extern Boolean __CFStringScanDouble(CFStringInlineBuffer *buf, CFDictionaryRef l extern Boolean __CFStringScanHex(CFStringInlineBuffer *buf, SInt32 *indexPtr, unsigned *result); +#ifdef __CONSTANT_CFSTRINGS__ #define CONST_STRING_DECL(S, V) const CFStringRef S = __builtin___CFStringMakeConstantString(V); +#else +struct CF_CONST_STRING { + CFRuntimeBase _base; + uint8_t *_ptr; + uint32_t _length; +}; + +extern int __CFConstantStringClassReference[]; + +/* CFNetwork also has a copy of the CONST_STRING_DECL macro (for use on platforms without constant string support in cc); please warn cfnetwork-core@group.apple.com of any necessary changes to this macro. -- REW, 1/28/2002 */ +#if defined(__ppc__) || defined(__ppc64__) +#define CONST_STRING_DECL(S, V) \ +static struct CF_CONST_STRING __ ## S ## __ = {{&__CFConstantStringClassReference, 0x0000, 0x07c8}, V, sizeof(V) - 1}; \ +const CFStringRef S = (CFStringRef) & __ ## S ## __; +#elif defined(__i386__) +#define CONST_STRING_DECL(S, V) \ +static struct CF_CONST_STRING __ ## S ## __ = {{&__CFConstantStringClassReference, 0x07c8, 0x0000}, V, sizeof(V) - 1}; \ +const CFStringRef S = (CFStringRef) & __ ## S ## __; +#else +#error undefined architecture +#endif +#endif #if defined(__MACH__) #define __kCFCharacterSetDir "/System/Library/CoreServices" @@ -277,24 +321,40 @@ CF_EXPORT CFStringRef __CFStringCreateImmutableFunnel3(CFAllocatorRef alloc, con 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; +typedef OSSpinLock CFSpinLock_t; + +#define CFSpinLockInit OS_SPINLOCK_INIT + +#if defined(__i386__) 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. +#define OSSpinLockLock(p) _spin_lock(p) +#define OSSpinLockUnlock(p) _spin_unlock(p) +#endif CF_INLINE void __CFSpinLock(CFSpinLock_t *lockp) { - if (__is_threaded) _spin_lock(lockp); + OSSpinLockLock(lockp); } CF_INLINE void __CFSpinUnlock(CFSpinLock_t *lockp) { - if (__is_threaded) _spin_unlock(lockp); + OSSpinLockUnlock(lockp); +} + +#elif defined(__WIN32__) + +typedef LONG CFSpinLock_t; + +CF_INLINE void __CFSpinLock(CFSpinLock_t *slock) { + while (InterlockedExchange(slock, 1) != 0) { + Sleep(1); // 1ms + } +} + +CF_INLINE void __CFSpinUnlock(CFSpinLock_t *lock) { + *lock = 0; } #else @@ -305,9 +365,8 @@ CF_INLINE void __CFSpinUnlock(CFSpinLock_t *lockp) { #endif -#if defined(__svr4__) || defined(__hpux__) +#if defined(__svr4__) || defined(__hpux__) || defined(__WIN32__) #include -#elif defined(__WIN32__) #elif defined(__MACH__) || defined(__LINUX__) || defined(__FREEBSD__) #include #endif @@ -367,11 +426,20 @@ CF_EXPORT CFIndex _CFLengthAfterDeletingPathExtension(UniChar *unichars, CFIndex #define CF_IS_OBJC(typeID, obj) (false) +#define CF_OBJC_VOIDCALL0(obj, sel) +#define CF_OBJC_VOIDCALL1(obj, sel, a1) +#define CF_OBJC_VOIDCALL2(obj, sel, a1, a2) + +#define CF_OBJC_CALL0(rettype, retvar, obj, sel) +#define CF_OBJC_CALL1(rettype, retvar, obj, sel, a1) +#define CF_OBJC_CALL2(rettype, retvar, obj, sel, a1, a2) + #define CF_OBJC_FUNCDISPATCH0(typeID, rettype, obj, sel) #define CF_OBJC_FUNCDISPATCH1(typeID, rettype, obj, sel, a1) #define CF_OBJC_FUNCDISPATCH2(typeID, rettype, obj, sel, a1, a2) #define CF_OBJC_FUNCDISPATCH3(typeID, rettype, obj, sel, a1, a2, a3) #define CF_OBJC_FUNCDISPATCH4(typeID, rettype, obj, sel, a1, a2, a3, a4) +#define CF_OBJC_FUNCDISPATCH5(typeID, rettype, obj, sel, a1, a2, a3, a4, a5) #endif @@ -379,57 +447,95 @@ CF_EXPORT CFIndex _CFLengthAfterDeletingPathExtension(UniChar *unichars, CFIndex #define __CFISAForTypeID(x) (NULL) #endif -#if defined(__MACH__) +#define __CFMaxRuntimeTypes 256 -struct objc_class { // nasty, nasty - long __fields0__[2]; - const char *name; - long __fields1__[7]; -}; +#if defined(__MACH__) -#define __CFMaxRuntimeTypes 256 +#include extern struct objc_class *__CFRuntimeObjCClassTable[]; CF_INLINE void *__CFISAForTypeID(CFTypeID typeID) { return (void *)(__CFRuntimeObjCClassTable[typeID]); } -typedef void *SEL; - -extern SEL (*__CFGetObjCSelector)(const char *); +#if defined(__ppc__) +#define __CFSendObjCMsg 0xfffeff00 +#else extern void * (*__CFSendObjCMsg)(const void *, SEL, ...); +#endif +#if 0 // 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); } +#else +#define CF_IS_OBJC(typeID, obj) (false) +#endif +// Invoke an ObjC method that returns void. +// Assumes CF_IS_OBJC has already been checked. +#define CF_OBJC_VOIDCALL0(obj, sel) \ + {void (*func)(const void *, SEL) = (void *)__CFSendObjCMsg; \ + static SEL s = NULL; if (!s) s = sel_registerName(sel); \ + func((const void *)obj, s);} +#define CF_OBJC_VOIDCALL1(obj, sel, a1) \ + {void (*func)(const void *, SEL, ...) = (void *)__CFSendObjCMsg; \ + static SEL s = NULL; if (!s) s = sel_registerName(sel); \ + func((const void *)obj, s, (a1));} +#define CF_OBJC_VOIDCALL2(obj, sel, a1, a2) \ + {void (*func)(const void *, SEL, ...) = (void *)__CFSendObjCMsg; \ + static SEL s = NULL; if (!s) s = sel_registerName(sel); \ + func((const void *)obj, s, (a1), (a2));} + + +// Invoke an ObjC method, leaving the result in "retvar". +// Assumes CF_IS_OBJC has already been checked. +#define CF_OBJC_CALL0(rettype, retvar, obj, sel) \ + {rettype (*func)(const void *, SEL) = (void *)__CFSendObjCMsg; \ + static SEL s = NULL; if (!s) s = sel_registerName(sel); \ + retvar = func((const void *)obj, s);} +#define CF_OBJC_CALL1(rettype, retvar, obj, sel, a1) \ + {rettype (*func)(const void *, SEL, ...) = (void *)__CFSendObjCMsg; \ + static SEL s = NULL; if (!s) s = sel_registerName(sel); \ + retvar = func((const void *)obj, s, (a1));} +#define CF_OBJC_CALL2(rettype, retvar, obj, sel, a1, a2) \ + {rettype (*func)(const void *, SEL, ...) = (void *)__CFSendObjCMsg; \ + static SEL s = NULL; if (!s) s = sel_registerName(sel); \ + retvar = func((const void *)obj, s, (a1), (a2));} + +// Invoke an ObjC method, return the result #define CF_OBJC_FUNCDISPATCH0(typeID, rettype, obj, sel) \ - if (CF_IS_OBJC(typeID, obj)) \ + if (__builtin_expect(CF_IS_OBJC(typeID, obj), 0)) \ {rettype (*func)(const void *, SEL) = (void *)__CFSendObjCMsg; \ - static SEL s = NULL; if (!s) s = __CFGetObjCSelector(sel); \ + static SEL s = NULL; if (!s) s = sel_registerName(sel); \ return func((const void *)obj, s);} #define CF_OBJC_FUNCDISPATCH1(typeID, rettype, obj, sel, a1) \ - if (CF_IS_OBJC(typeID, obj)) \ + if (__builtin_expect(CF_IS_OBJC(typeID, obj), 0)) \ {rettype (*func)(const void *, SEL, ...) = (void *)__CFSendObjCMsg; \ - static SEL s = NULL; if (!s) s = __CFGetObjCSelector(sel); \ + static SEL s = NULL; if (!s) s = sel_registerName(sel); \ return func((const void *)obj, s, (a1));} #define CF_OBJC_FUNCDISPATCH2(typeID, rettype, obj, sel, a1, a2) \ - if (CF_IS_OBJC(typeID, obj)) \ + if (__builtin_expect(CF_IS_OBJC(typeID, obj), 0)) \ {rettype (*func)(const void *, SEL, ...) = (void *)__CFSendObjCMsg; \ - static SEL s = NULL; if (!s) s = __CFGetObjCSelector(sel); \ + static SEL s = NULL; if (!s) s = sel_registerName(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)) \ + if (__builtin_expect(CF_IS_OBJC(typeID, obj), 0)) \ {rettype (*func)(const void *, SEL, ...) = (void *)__CFSendObjCMsg; \ - static SEL s = NULL; if (!s) s = __CFGetObjCSelector(sel); \ + static SEL s = NULL; if (!s) s = sel_registerName(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)) \ + if (__builtin_expect(CF_IS_OBJC(typeID, obj), 0)) \ {rettype (*func)(const void *, SEL, ...) = (void *)__CFSendObjCMsg; \ - static SEL s = NULL; if (!s) s = __CFGetObjCSelector(sel); \ + static SEL s = NULL; if (!s) s = sel_registerName(sel); \ return func((const void *)obj, s, (a1), (a2), (a3), (a4));} +#define CF_OBJC_FUNCDISPATCH5(typeID, rettype, obj, sel, a1, a2, a3, a4, a5) \ + if (__builtin_expect(CF_IS_OBJC(typeID, obj), 0)) \ + {rettype (*func)(const void *, SEL, ...) = (void *)__CFSendObjCMsg; \ + static SEL s = NULL; if (!s) s = sel_registerName(sel); \ + return func((const void *)obj, s, (a1), (a2), (a3), (a4), (a5));} #endif @@ -440,11 +546,11 @@ extern void __CF_FAULT_CALLBACK(void **ptr); extern void *__CF_INVOKE_CALLBACK(void *, ...); #define FAULT_CALLBACK(V) __CF_FAULT_CALLBACK(V) -#define INVOKE_CALLBACK1(P, A) __CF_INVOKE_CALLBACK(P, A) -#define INVOKE_CALLBACK2(P, A, B) __CF_INVOKE_CALLBACK(P, A, B) -#define INVOKE_CALLBACK3(P, A, B, C) __CF_INVOKE_CALLBACK(P, A, B, C) -#define INVOKE_CALLBACK4(P, A, B, C, D) __CF_INVOKE_CALLBACK(P, A, B, C, D) -#define INVOKE_CALLBACK5(P, A, B, C, D, E) __CF_INVOKE_CALLBACK(P, A, B, C, D, E) +#define INVOKE_CALLBACK1(P, A) (((uintptr_t)(P) & 0x2) ? __CF_INVOKE_CALLBACK(P, A) : (P)(A)) +#define INVOKE_CALLBACK2(P, A, B) (((uintptr_t)(P) & 0x2) ? __CF_INVOKE_CALLBACK(P, A, B) : (P)(A, B)) +#define INVOKE_CALLBACK3(P, A, B, C) (((uintptr_t)(P) & 0x2) ? __CF_INVOKE_CALLBACK(P, A, B, C) : (P)(A, B, C)) +#define INVOKE_CALLBACK4(P, A, B, C, D) (((uintptr_t)(P) & 0x2) ? __CF_INVOKE_CALLBACK(P, A, B, C, D) : (P)(A, B, C, D)) +#define INVOKE_CALLBACK5(P, A, B, C, D, E) (((uintptr_t)(P) & 0x2) ? __CF_INVOKE_CALLBACK(P, A, B, C, D, E) : (P)(A, B, C, D, E)) #else #define FAULT_CALLBACK(V) #define INVOKE_CALLBACK1(P, A) (P)(A) @@ -454,8 +560,67 @@ extern void *__CF_INVOKE_CALLBACK(void *, ...); #define INVOKE_CALLBACK5(P, A, B, C, D, E) (P)(A, B, C, D, E) #endif +#if defined(__MACH__) + +/* For the support of functionality which needs CarbonCore or other frameworks */ +extern void *__CFLookupCarbonCoreFunction(const char *name); +extern void *__CFLookupCFNetworkFunction(const char *name); + +// These macros define an upcall or weak "symbol-lookup" wrapper function. +// The parameters are: +// R : the return type of the function +// N : the name of the function (in the other library) +// P : the parenthesized parameter list of the function +// A : the parenthesized actual argument list to be passed +// opt: a fifth optional argument can be passed in which is the +// return value of the wrapper when the function cannot be +// found; should be of type R, & can be a function call +// The name of the resulting wrapper function is: +// __CFCarbonCore_N (where N is the second parameter) +// __CFNetwork_N (where N is the second parameter) +// +// Example: +// DEFINE_WEAK_CARBONCORE_FUNC(void, DisposeHandle, (Handle h), (h)) +// + +#define DEFINE_WEAK_CARBONCORE_FUNC(R, N, P, A, ...) \ +static R __CFCarbonCore_ ## N P { \ + static R (*dyfunc) P = (void *)(~(uintptr_t)0); \ + if ((void *)(~(uintptr_t)0) == dyfunc) { \ + dyfunc = __CFLookupCarbonCoreFunction(#N); } \ + return dyfunc ? dyfunc A : (R)(0 , ## __VA_ARGS__); \ +} + +#define DEFINE_WEAK_CFNETWORK_FUNC(R, N, P, A, ...) \ +static R __CFNetwork_ ## N P { \ + static R (*dyfunc) P = (void *)(~(uintptr_t)0); \ + if ((void *)(~(uintptr_t)0) == dyfunc) { \ + dyfunc = __CFLookupCFNetworkFunction(#N); } \ + return dyfunc ? dyfunc A : (R)(0 , ## __VA_ARGS__); \ +} + +#else + +#define DEFINE_WEAK_CARBONCORE_FUNC(R, N, P, A, ...) +#define DEFINE_WEAK_CFNETWORK_FUNC(R, N, P, A, ...) + +#endif + __private_extern__ CFArrayRef _CFBundleCopyUserLanguages(Boolean useBackstops); -#endif /* ! __COREFOUNDATION_CFINTERNAL__ */ +/* GC related internal SPIs. */ +extern malloc_zone_t *__CFCollectableZone; +__private_extern__ void _CFStorageSetWeak(struct __CFStorage *storage); +#if defined(__WIN32__) +__private_extern__ const char *_CFDLLPath(void); +__private_extern__ void __CFStringCleanup(void); +__private_extern__ void __CFSocketCleanup(void); +__private_extern__ void __CFUniCharCleanup(void); +__private_extern__ void __CFStreamCleanup(void); +// We should export this as SPI or API to clients - 3514284 +CF_EXPORT CFAbsoluteTime _CFAbsoluteTimeFromFileTime(const FILETIME *ft); +#endif + +#endif /* ! __COREFOUNDATION_CFINTERNAL__ */ diff --git a/Base.subproj/CFPlatform.c b/Base.subproj/CFPlatform.c index 03d980b..7564e45 100644 --- a/Base.subproj/CFPlatform.c +++ b/Base.subproj/CFPlatform.c @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -30,8 +28,6 @@ #include "CFInternal.h" #include "CFPriv.h" #if defined(__WIN32__) - #include - #include #include #include #else @@ -41,7 +37,6 @@ #include #endif #if defined(__MACH__) - #include #include #endif @@ -53,21 +48,15 @@ extern char *getenv(const char *name); #define kCFPlatformInterfaceStringEncoding CFStringGetSystemEncoding() #endif -char **_CFArgv(void) { #if defined(__MACH__) +char **_CFArgv(void) { return *_NSGetArgv(); -#else - return NULL; -#endif } int _CFArgc(void) { -#if defined(__MACH__) return *_NSGetArgc(); -#else - return 0; -#endif } +#endif __private_extern__ Boolean _CFGetCurrentDirectory(char *path, int maxlen) { @@ -86,13 +75,15 @@ __private_extern__ Boolean _CFIsCFM(void) { return __CFIsCFM; } - #if defined(__WIN32__) -#define PATH_LIST_SEP ';' +#define PATH_SEP '\\' #else -#define PATH_LIST_SEP ':' +#define PATH_SEP '/' #endif +#if !defined(__WIN32__) +#define PATH_LIST_SEP ':' + static char *_CFSearchForNameInPath(CFAllocatorRef alloc, const char *name, char *path) { struct stat statbuf; char *nname = CFAllocatorAllocate(alloc, strlen(name) + strlen(path) + 2, 0); @@ -124,19 +115,61 @@ static char *_CFSearchForNameInPath(CFAllocatorRef alloc, const char *name, char return NULL; } +#endif +#if defined(__WIN32__) +// Returns the path to the CF DLL, which we can then use to find resources like char sets + +__private_extern__ const char *_CFDLLPath(void) { + static TCHAR cachedPath[MAX_PATH+1] = ""; + + if ('\0' == cachedPath[0]) { +#if defined(DEBUG) + char *DLLFileName = "CoreFoundation_debug"; +#elif defined(PROFILE) + char *DLLFileName = "CoreFoundation_profile"; +#else + char *DLLFileName = "CoreFoundation"; +#endif + HMODULE ourModule = GetModuleHandle(DLLFileName); + CFAssert(ourModule, __kCFLogAssertion, "GetModuleHandle failed"); + + DWORD wResult = GetModuleFileName(ourModule, cachedPath, MAX_PATH+1); + CFAssert1(wResult > 0, __kCFLogAssertion, "GetModuleFileName failed: %d", GetLastError()); + CFAssert1(wResult < MAX_PATH+1, __kCFLogAssertion, "GetModuleFileName result truncated: %s", cachedPath); + + // strip off last component, the DLL name + CFIndex idx; + for (idx = wResult - 1; idx; idx--) { + if ('\\' == cachedPath[idx]) { + cachedPath[idx] = '\0'; + break; + } + } + } + return cachedPath; +} +#endif + static const char *__CFProcessPath = NULL; -static const char *__CFprogname = ""; +static const char *__CFprogname = NULL; -const char **_CFGetProgname(void) { // This is a hack around the broken _NSGetPrognam(), for now; will be removed +const char **_CFGetProgname(void) { + if (!__CFprogname) + _CFProcessPath(); // sets up __CFprogname as a side-effect return &__CFprogname; } +const char **_CFGetProcessPath(void) { + if (!__CFProcessPath) + _CFProcessPath(); // sets up __CFProcessPath as a side-effect + return &__CFProcessPath; +} + const char *_CFProcessPath(void) { CFAllocatorRef alloc = NULL; char *thePath = NULL; - int execIndex = 0; if (__CFProcessPath) return __CFProcessPath; if (!__CFProcessPath) { @@ -153,9 +186,10 @@ const char *_CFProcessPath(void) { } #if defined(__MACH__) + int execIndex = 0; { struct stat exec, lcfm; - unsigned long size = CFMaxPathSize; + uint32_t size = CFMaxPathSize; char buffer[CFMaxPathSize]; if (0 == _NSGetExecutablePath(buffer, &size) && strcasestr(buffer, "LaunchCFMApp") != NULL && @@ -170,13 +204,14 @@ const char *_CFProcessPath(void) { } #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; + if (!__CFProcessPath) { + char buf[CFMaxPathSize] = {0}; + DWORD rlen = GetModuleFileName(NULL, buf, 1028); thePath = rlen ? buf : NULL; #else + if (!__CFProcessPath && NULL != (*_NSGetArgv())[execIndex]) { + char buf[CFMaxPathSize] = {0}; struct stat statbuf; const char *arg0 = (*_NSGetArgv())[execIndex]; if (arg0[0] == '/') { @@ -218,23 +253,20 @@ const char *_CFProcessPath(void) { } if (thePath) { - // We are going to process the buffer replacing all "/./" with "/" + // We are going to process the buffer replacing all "/./" and "//" with "/" CFIndex srcIndex = 0, dstIndex = 0; CFIndex len = strlen(thePath); for (srcIndex=0; srcIndexpw_dir) { @@ -455,7 +501,6 @@ 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 index f51ff10..1df483f 100644 --- a/Base.subproj/CFPriv.h +++ b/Base.subproj/CFPriv.h @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -23,7 +21,7 @@ * @APPLE_LICENSE_HEADER_END@ */ /* CFPriv.h - Copyright (c) 1998-2003, Apple, Inc. All rights reserved. + Copyright (c) 1998-2005, Apple, Inc. All rights reserved. */ /* @@ -38,7 +36,10 @@ #include #include #include -#if defined(__MACH__) +#include + + +#if defined(__MACH__) || defined(__WIN32__) #include #include #include @@ -50,9 +51,11 @@ extern "C" { CF_EXPORT intptr_t _CFDoOperation(intptr_t code, intptr_t subcode1, intptr_t subcode2); -CF_EXPORT void _CFRuntimeSetCFMPresent(int a); +CF_EXPORT void _CFRuntimeSetCFMPresent(void *a); CF_EXPORT const char *_CFProcessPath(void); +CF_EXPORT const char **_CFGetProcessPath(void); +CF_EXPORT const char **_CFGetProgname(void); #if defined(__MACH__) @@ -72,6 +75,10 @@ CF_EXPORT CFPropertyListRef _CFURLCopyPropertyListRepresentation(CFURLRef url); CF_EXPORT CFURLRef _CFURLCreateFromPropertyListRepresentation(CFAllocatorRef alloc, CFPropertyListRef pListRepresentation); #endif /* __MACH__ */ +CF_EXPORT void CFPreferencesFlushCaches(void); + +CF_EXPORT CFTimeInterval _CFTimeZoneGetDSTOffset(CFTimeZoneRef tz, CFAbsoluteTime at); +CF_EXPORT CFAbsoluteTime _CFTimeZoneGetNextDSTSwitch(CFTimeZoneRef tz, CFAbsoluteTime at); #if !defined(__WIN32__) struct FSSpec; @@ -194,7 +201,7 @@ 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 */ +/* System Version file access */ CF_EXPORT CFStringRef CFCopySystemVersionString(void); // Human-readable string containing both marketing and build version, should be API'd CF_EXPORT CFDictionaryRef _CFCopySystemVersionDictionary(void); CF_EXPORT CFDictionaryRef _CFCopyServerVersionDictionary(void); @@ -216,6 +223,10 @@ typedef enum { CF_EXPORT CFRange CFStringGetRangeOfCharacterClusterAtIndex(CFStringRef string, CFIndex charIndex, CFStringCharacterClusterType type); +enum { + kCFCompareDiacriticsInsensitive = (1 << 28), + kCFCompareWidthInsensitive = (1 << 29), +}; /* CFStringEncoding SPI */ /* When set, CF encoding conversion engine keeps ASCII compatibility. (i.e. ASCII backslash <-> Unicode backslash in MacJapanese */ diff --git a/Base.subproj/CFRuntime.c b/Base.subproj/CFRuntime.c index 55a9676..5f4491b 100644 --- a/Base.subproj/CFRuntime.c +++ b/Base.subproj/CFRuntime.c @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -27,27 +25,34 @@ Responsibility: Christopher Kane */ +#define ENABLE_ZOMBIES 1 + #include "CFRuntime.h" #include "CFInternal.h" #include #include +#include #if defined(__MACH__) +#include #include -#include #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 +#if defined(__MACH__) +extern BOOL objc_isAuto(id object); +extern void* objc_assign_ivar_address_CF(void *value, void *base, void **slot); +extern void* objc_assign_strongCast_CF(void* value, void **slot); +#endif + enum { // retain/release recording constants -- must match values // used by OA for now; probably will change in the future @@ -63,6 +68,8 @@ __kCFReleaseEvent = 29 CF_INLINE size_t malloc_size(void *memblock) { return _msize(memblock); } +#else +#include #endif #if defined(__MACH__) @@ -73,13 +80,11 @@ 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")); + dyfunc = dlsym(RTLD_DEFAULT, "_OAInitialize"); } if (NULL != dyfunc) { - dyfunc(); - __CFOASafe = true; + dyfunc(); + __CFOASafe = true; } } @@ -87,12 +92,10 @@ void __CFRecordAllocationEvent(int eventnum, void *ptr, int size, int data, cons 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")); + dyfunc = dlsym(RTLD_DEFAULT, "_OARecordAllocationEvent"); } if (NULL != dyfunc) { - dyfunc(eventnum, ptr, size, data, classname); + dyfunc(eventnum, ptr, size, data, classname); } } @@ -100,12 +103,10 @@ 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")); + dyfunc = dlsym(RTLD_DEFAULT, "_OASetLastAllocationEventName"); } if (NULL != dyfunc) { - dyfunc(ptr, classname); + dyfunc(ptr, classname); } } #endif @@ -148,10 +149,31 @@ static int32_t __CFRuntimeClassTableCount = 0; #if defined(__MACH__) -__private_extern__ SEL (*__CFGetObjCSelector)(const char *) = NULL; +#if !defined(__ppc__) __private_extern__ void * (*__CFSendObjCMsg)(const void *, SEL, ...) = NULL; +#endif + +__private_extern__ malloc_zone_t *__CFCollectableZone = NULL; + +static bool objc_isCollectable_nope(void* obj) { return false; } +bool (*__CFObjCIsCollectable)(void *) = NULL; -__private_extern__ struct objc_class *__CFRuntimeObjCClassTable[__CFMaxRuntimeTypes] = {NULL}; +static const void* objc_AssignIvar_none(const void *value, void *base, const void **slot) { return (*slot = value); } +const void* (*__CFObjCAssignIvar)(const void *value, const void *base, const void **slot) = objc_AssignIvar_none; + +static const void* objc_StrongAssign_none(const void *value, const void **slot) { return (*slot = value); } +const void* (*__CFObjCStrongAssign)(const void *value, const void **slot) = objc_StrongAssign_none; + +void* (*__CFObjCMemmoveCollectable)(void *dst, const void *, unsigned) = memmove; + +// GC: to be moved to objc if necessary. +static void objc_WriteBarrierRange_none(void *ptr, unsigned size) {} +static void objc_WriteBarrierRange_auto(void *ptr, unsigned size) { auto_zone_write_barrier_range(__CFCollectableZone, ptr, size); } +void (*__CFObjCWriteBarrierRange)(void *, unsigned) = objc_WriteBarrierRange_none; + +// Temporarily disabled __private_extern__ +#warning Ali, be sure to reexamine this +struct objc_class *__CFRuntimeObjCClassTable[__CFMaxRuntimeTypes] = {NULL}; #endif @@ -160,8 +182,6 @@ 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; @@ -177,6 +197,17 @@ CFTypeID _CFRuntimeRegisterClass(const CFRuntimeClass * const cls) { return __CFRuntimeClassTableCount - 1; } +void _CFRuntimeInitializeClassForBridging(CFTypeID typeID) { + __CFRuntimeObjCClassTable[typeID] = (struct objc_class *)calloc(sizeof(struct objc_class), 1); +} + +Boolean _CFRuntimeSetupBridging(CFTypeID typeID, struct objc_class *mainClass, struct objc_class *subClass) { + void *isa = __CFISAForTypeID(typeID); + memmove(isa, subClass, sizeof(struct objc_class)); + class_poseAs(isa, mainClass); + return true; +} + const CFRuntimeClass * _CFRuntimeGetClassWithTypeID(CFTypeID typeID) { return __CFRuntimeClassTable[typeID]; } @@ -186,7 +217,7 @@ void _CFRuntimeUnregisterClassWithTypeID(CFTypeID typeID) { } -#if defined(DEBUG) +#if defined(DEBUG) || defined(ENABLE_ZOMBIES) /* CFZombieLevel levels: * bit 0: scribble deallocated CF object memory @@ -229,11 +260,20 @@ static void __CFZombifyDeallocatedMemory(void *cf) { #endif /* DEBUG */ +// XXX_PCB: use the class version field as a bitmask, to allow classes to opt-in for GC scanning. + +CF_INLINE CFOptionFlags CF_GET_COLLECTABLE_MEMORY_TYPE(const CFRuntimeClass *cls) +{ + return (cls->version & _kCFRuntimeScannedObject) ? AUTO_OBJECT_SCANNED : AUTO_OBJECT_UNSCANNED; +} + CFTypeRef _CFRuntimeCreateInstance(CFAllocatorRef allocator, CFTypeID typeID, uint32_t extraBytes, unsigned char *category) { CFRuntimeBase *memory; Boolean usesSystemDefaultAllocator; int32_t size; + CFAssert1(typeID != _kCFRuntimeNotATypeID, __kCFLogAssertion, "%s(): Uninitialized type id", __PRETTY_FUNCTION__); + if (NULL == __CFRuntimeClassTable[typeID]) { return NULL; } @@ -241,19 +281,23 @@ CFTypeRef _CFRuntimeCreateInstance(CFAllocatorRef allocator, CFTypeID typeID, ui usesSystemDefaultAllocator = (allocator == kCFAllocatorSystemDefault); extraBytes = (extraBytes + (sizeof(void *) - 1)) & ~(sizeof(void *) - 1); size = sizeof(CFRuntimeBase) + extraBytes + (usesSystemDefaultAllocator ? 0 : sizeof(CFAllocatorRef)); - memory = CFAllocatorAllocate(allocator, size, 0); + // CFType version 0 objects are unscanned by default since they don't have write-barriers and hard retain their innards + // CFType version 1 objects are scanned and use hand coded write-barriers to store collectable storage within + memory = CFAllocatorAllocate(allocator, size, CF_GET_COLLECTABLE_MEMORY_TYPE(__CFRuntimeClassTable[typeID])); if (NULL == memory) { return NULL; } -#if defined(DEBUG) +#if defined(DEBUG) || defined(ENABLE_ZOMBIES) __CFZombifyAllocatedMemory((void *)memory); #endif if (__CFOASafe && category) { - __CFSetLastAllocationEventName(memory, category); + __CFSetLastAllocationEventName(memory, category); } else if (__CFOASafe) { - __CFSetLastAllocationEventName(memory, __CFRuntimeClassTable[typeID]->className); + __CFSetLastAllocationEventName(memory, __CFRuntimeClassTable[typeID]->className); } if (!usesSystemDefaultAllocator) { + // add space to hold allocator ref for non-standard allocators. + // (this screws up 8 byte alignment but seems to work) *(CFAllocatorRef *)((char *)memory) = CFRetain(allocator); memory = (CFRuntimeBase *)((char *)memory + sizeof(CFAllocatorRef)); } @@ -307,19 +351,19 @@ __private_extern__ void __CFGenericValidateType_(CFTypeRef cf, CFTypeID type, co CF_INLINE int CFTYPE_IS_OBJC(const void *obj) { CFTypeID typeID = __CFGenericTypeID_inline(obj); - return CF_IS_OBJC(typeID, obj) && __CFSendObjCMsg; + return CF_IS_OBJC(typeID, obj); } #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);} + {rettype (*func)(void *, SEL) = (void *)__CFSendObjCMsg; \ + static SEL s = NULL; if (!s) s = sel_registerName(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));} + {rettype (*func)(void *, SEL, ...) = (void *)__CFSendObjCMsg; \ + static SEL s = NULL; if (!s) s = sel_registerName(sel); \ + return func((void *)obj, s, (a1));} #endif @@ -348,108 +392,34 @@ extern int _CFDictionaryDecrementValue(CFMutableDictionaryRef dict, const void * // Bit 31 (highest bit) in second word of cf instance indicates external ref count +extern void _CFRelease(CFTypeRef cf); +extern CFTypeRef _CFRetain(CFTypeRef cf); +extern CFHashCode _CFHash(CFTypeRef cf); + CFTypeRef CFRetain(CFTypeRef cf) { - CFIndex lowBits = 0; - bool is_threaded = __is_threaded; -#if defined(DEBUG) - if (NULL == cf) HALT; -#endif + // always honor CFRetain's with a hard reference + if (CF_IS_COLLECTABLE(cf)) { + auto_zone_retain(__CFCollectableZone, (void*)cf); + return cf; + } + // XXX_PCB some Objc objects aren't really reference counted, perhaps they should be able to make that distinction? 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; + return _CFRetain(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 + // make sure we get rid of the hard reference if called + if (CF_IS_COLLECTABLE(cf)) { + auto_zone_release(__CFCollectableZone, (void*)cf); + return; + } + // XXX_PCB some objects aren't really reference counted. 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); - } - } + _CFRelease(cf); } static uint64_t __CFGetFullRetainCount(CFTypeRef cf) { @@ -457,21 +427,46 @@ static uint64_t __CFGetFullRetainCount(CFTypeRef cf) { uint64_t highBits = 0, compositeRC; lowBits = ((CFRuntimeBase *)cf)->_rc; if (0 == lowBits) { - return (uint64_t)0x00FFFFFFFFFFFFFFULL; + return (uint64_t)0x00ffffffffffffffULL; } if ((lowBits & 0x08000) != 0) { - highBits = (uint64_t)(uintptr_t)CFDictionaryGetValue(__CFRuntimeExternRefCountTable, DISGUISE(cf)); + highBits = (uint64_t)(uintptr_t)CFDictionaryGetValue(__CFRuntimeExternRefCountTable, DISGUISE(cf)); } compositeRC = (lowBits & 0x7fff) + (highBits << 15); return compositeRC; } +CFTypeRef _CFRetainGC(CFTypeRef cf) +{ +#if defined(DEBUG) + if (CF_USING_COLLECTABLE_MEMORY && !CF_IS_COLLECTABLE(cf)) { + fprintf(stderr, "non-auto object %p passed to _CFRetainGC.\n", cf); + HALT; + } +#endif + return CF_USING_COLLECTABLE_MEMORY ? cf : CFRetain(cf); +} + +void _CFReleaseGC(CFTypeRef cf) +{ +#if defined(DEBUG) + if (CF_USING_COLLECTABLE_MEMORY && !CF_IS_COLLECTABLE(cf)) { + fprintf(stderr, "non-auto object %p passed to _CFReleaseGC.\n", cf); + HALT; + } +#endif + if (!CF_USING_COLLECTABLE_MEMORY) CFRelease(cf); +} + CFIndex CFGetRetainCount(CFTypeRef cf) { uint64_t rc; CFIndex result; #if defined(DEBUG) if (NULL == cf) HALT; #endif + if (CF_IS_COLLECTABLE(cf)) { + return auto_zone_retain_count(__CFCollectableZone, cf); + } CFTYPE_OBJC_FUNCDISPATCH0(CFIndex, cf, "retainCount"); __CFGenericAssertIsCF(cf); rc = __CFGetFullRetainCount(cf); @@ -479,6 +474,26 @@ CFIndex CFGetRetainCount(CFTypeRef cf) { return result; } +CFTypeRef CFMakeCollectable(CFTypeRef cf) +{ + if (!cf) return NULL; + if (CF_USING_COLLECTABLE_MEMORY) { +#if defined(DEBUG) + CFAllocatorRef allocator = CFGetAllocator(cf); + if (!CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { + CFLog(0, CFSTR("object %p with non-GC allocator %p passed to CFMakeCollected."), cf, allocator); + HALT; + } +#endif + if (CFGetRetainCount(cf) == 0) { + CFLog(0, CFSTR("object %p with 0 retain-count passed to CFMakeCollected."), cf); + return cf; + } + CFRelease(cf); + } + return cf; +} + Boolean CFEqual(CFTypeRef cf1, CFTypeRef cf2) { #if defined(DEBUG) if (NULL == cf1) HALT; @@ -497,15 +512,9 @@ Boolean CFEqual(CFTypeRef cf1, CFTypeRef cf2) { } 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; + return _CFHash(cf); } // definition: produces a normally non-NULL debugging description of the object @@ -513,7 +522,15 @@ CFStringRef CFCopyDescription(CFTypeRef cf) { #if defined(DEBUG) if (NULL == cf) HALT; #endif - CFTYPE_OBJC_FUNCDISPATCH0(CFStringRef, cf, "_copyDescription"); + if (CFTYPE_IS_OBJC(cf)) { + static SEL s = NULL; + CFStringRef (*func)(void *, SEL, ...) = (void *)__CFSendObjCMsg; + if (!s) s = sel_registerName("_copyDescription"); + CFStringRef result = func((void *)cf, s); + if (result && CF_USING_COLLECTABLE_MEMORY) CFRetain(result); // needs hard retain + return result; + } + // CFTYPE_OBJC_FUNCDISPATCH0(CFStringRef, cf, "_copyDescription"); // XXX returns 0 refcounted item under GC __CFGenericAssertIsCF(cf); if (NULL != __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)]->copyDebugDesc) { CFStringRef result; @@ -532,8 +549,8 @@ __private_extern__ CFStringRef __CFCopyFormattingDescription(CFTypeRef cf, CFDic 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) s = sel_registerName("_copyFormattingDescription:"); + if (!r) r = sel_registerName("respondsToSelector:"); if (s && func((void *)cf, r, s)) return func((void *)cf, s, formatOptions); return NULL; } @@ -579,6 +596,10 @@ extern void __CFTreeInitialize(void); extern void __CFURLInitialize(void); extern void __CFXMLNodeInitialize(void); extern void __CFXMLParserInitialize(void); +extern void __CFLocaleInitialize(void); +extern void __CFCalendarInitialize(void); +extern void __CFNumberFormatterInitialize(void); +extern void __CFDateFormatterInitialize(void); #if defined(__MACH__) extern void __CFMessagePortInitialize(void); extern void __CFMachPortInitialize(void); @@ -596,15 +617,24 @@ extern void __CFPlugInInstanceInitialize(void); extern void __CFUUIDInitialize(void); extern void __CFBinaryHeapInitialize(void); extern void __CFBitVectorInitialize(void); +#if defined(__WIN32__) +extern void __CFWindowsMessageQueueInitialize(void); +extern void __CFBaseCleanup(void); +#endif +extern void __CFStreamInitialize(void); +#if defined(__MACH__) +extern void __CFPreferencesDomainInitialize(void); +extern void __CFUserNotificationInitialize(void); +#endif #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) +#define SYSCALL_TRACE(N) do ptrace(N, 0, 0, 0); while (0) #else -#define SYSCALL_TRACE(N) do {} while (0) +#define SYSCALL_TRACE(N) do {} while (0) #endif #if defined(__MACH__) && defined(PROFILE) @@ -613,10 +643,6 @@ static void _CF_mcleanup(void) { } #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; @@ -631,14 +657,22 @@ CF_EXPORT void __CFInitialize(void) { static int __done = 0; if (sizeof(int) != sizeof(long) || 4 != sizeof(long)) __HALT(); - if (!__done) { - CFIndex idx, cnt; + if (!__done) { __done = 1; SYSCALL_TRACE(0xC000); - - __setonlyClocaleconv(1); -#if defined(DEBUG) + { + kCFUseCollectableAllocator = objc_collecting_enabled(); + if (kCFUseCollectableAllocator) { + __CFCollectableZone = auto_zone(); + __CFObjCIsCollectable = objc_isAuto; + __CFObjCAssignIvar = objc_assign_ivar_address_CF; + __CFObjCStrongAssign = objc_assign_strongCast_CF; + __CFObjCMemmoveCollectable = objc_memmove_collectable; + __CFObjCWriteBarrierRange = objc_WriteBarrierRange_auto; + } + } +#if defined(DEBUG) || defined(ENABLE_ZOMBIES) { const char *value = getenv("CFZombieLevel"); if (NULL != value) { @@ -665,9 +699,12 @@ void __CFInitialize(void) { __CFBaseInitialize(); #if defined(__MACH__) - for (idx = 0; idx < __CFMaxRuntimeTypes; idx++) { - __CFRuntimeObjCClassTable[idx] = &__CFNSTypeClass; - } + { + CFIndex idx; + for (idx = 0; idx < __CFMaxRuntimeTypes; idx++) { + __CFRuntimeObjCClassTable[idx] = &__CFNSTypeClass; + } + } #endif /* Here so that two runtime classes get indices 0, 1. */ @@ -685,6 +722,7 @@ void __CFInitialize(void) { #if defined(__MACH__) { + CFIndex idx, cnt; char **args = *_NSGetArgv(); cnt = *_NSGetArgc(); for (idx = 1; idx < cnt - 1; idx++) { @@ -698,27 +736,13 @@ void __CFInitialize(void) { } #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 + + // 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 @@ -732,7 +756,7 @@ void __CFInitialize(void) { __CFStorageInitialize(); __CFTreeInitialize(); __CFURLInitialize(); - __CFXMLNodeInitialize(); + __CFXMLNodeInitialize(); __CFXMLParserInitialize(); __CFBundleInitialize(); __CFPlugInInitialize(); @@ -749,19 +773,17 @@ void __CFInitialize(void) { __CFRunLoopTimerInitialize(); __CFSocketInitialize(); #endif - + __CFStreamInitialize(); #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 + __CFPreferencesDomainInitialize(); +#endif // __MACH__ + SYSCALL_TRACE(0xC001); #if defined(__MACH__) { + CFIndex idx, cnt; char **args = *_NSGetArgv(); CFIndex count; cnt = *_NSGetArgc(); @@ -788,10 +810,12 @@ void __CFInitialize(void) { _CFProcessPath(); // cache this early #if defined(__MACH__) - __CFOAInitialize(); + __CFOAInitialize(); SYSCALL_TRACE(0xC003); #endif + if (__CFRuntimeClassTableCount < 100) __CFRuntimeClassTableCount = 100; + #if defined(DEBUG) && !defined(__WIN32__) // Don't log on MacOS 8 as this will create a log file unnecessarily CFLog (0, CFSTR("Assertions enabled")); @@ -807,8 +831,15 @@ void __CFInitialize(void) { */ WINBOOL WINAPI DllMain( HINSTANCE hInstance, DWORD dwReason, LPVOID pReserved ) { if (dwReason == DLL_PROCESS_ATTACH) { - __CFInitialize(); + __CFInitialize(); } else if (dwReason == DLL_PROCESS_DETACH) { + __CFStringCleanup(); + __CFSocketCleanup(); + __CFUniCharCleanup(); + __CFStreamCleanup(); + __CFBaseCleanup(); + } else if (dwReason == DLL_THREAD_DETACH) { + __CFFinalizeThreadData(NULL); } return TRUE; } @@ -827,9 +858,9 @@ Boolean _CFEqual(CFTypeRef cf1, CFTypeRef cf2) { 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); + 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; } @@ -843,6 +874,9 @@ CFIndex _CFGetRetainCount(CFTypeRef cf) { } CFHashCode _CFHash(CFTypeRef cf) { +#if defined(DEBUG) + if (NULL == cf) HALT; +#endif if (NULL != __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)]->hash) { return __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)]->hash(cf); } @@ -851,25 +885,24 @@ CFHashCode _CFHash(CFTypeRef 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); + __CFSpinLock(&__CFGlobalRetainLock); lowBits = ((CFRuntimeBase *)cf)->_rc; - if (0 == lowBits) { // Constant CFTypeRef - if (is_threaded) __CFSpinUnlock(&__CFGlobalRetainLock); + if (__builtin_expect(0 == lowBits, 0)) { // Constant CFTypeRef + __CFSpinUnlock(&__CFGlobalRetainLock); return cf; } lowBits++; - if ((lowBits & 0x07fff) == 0) { + if (__builtin_expect((lowBits & 0x07fff) == 0, 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) { + __CFSpinUnlock(&__CFGlobalRetainLock); + if (__builtin_expect(__CFOASafe, 0)) { uint64_t compositeRC; compositeRC = (lowBits & 0x7fff) + ((uint64_t)(uintptr_t)CFDictionaryGetValue(__CFRuntimeExternRefCountTable, DISGUISE(cf)) << 15); if (compositeRC > (uint64_t)0x7fffffff) compositeRC = (uint64_t)0x7fffffff; @@ -880,21 +913,20 @@ CF_EXPORT CFTypeRef _CFRetain(CFTypeRef 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); + __CFSpinLock(&__CFGlobalRetainLock); lowBits = ((CFRuntimeBase *)cf)->_rc; - if (0 == lowBits) { // Constant CFTypeRef - if (is_threaded) __CFSpinUnlock(&__CFGlobalRetainLock); + if (__builtin_expect(0 == lowBits, 0)) { // Constant CFTypeRef + __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) + if (__builtin_expect(1 == lowBits, 0)) { + __CFSpinUnlock(&__CFGlobalRetainLock); + if (__builtin_expect(__CFOASafe, 0)) __CFRecordAllocationEvent(__kCFReleaseEvent, (void *)cf, 0, 0, NULL); + if (__builtin_expect(__kCFAllocatorTypeID_CONST == __CFGenericTypeID_inline(cf), 0)) { +#if defined(DEBUG) || defined(ENABLE_ZOMBIES) __CFZombifyDeallocatedMemory((void *)cf); if (!(__CFZombieLevel & (1 << 4))) { __CFAllocatorDeallocate((void *)cf); @@ -904,16 +936,17 @@ CF_EXPORT void _CFRelease(CFTypeRef cf) { #endif } else { CFAllocatorRef allocator; - if (NULL != __CFRuntimeClassTable[__CFGenericTypeID(cf)]->finalize) { - __CFRuntimeClassTable[__CFGenericTypeID(cf)]->finalize(cf); +// ((CFRuntimeBase *)cf)->_rc = 0; + if (NULL != __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)]->finalize) { + __CFRuntimeClassTable[__CFGenericTypeID_inline(cf)]->finalize(cf); } - if (__CFBitfieldGetValue(((const CFRuntimeBase *)cf)->_info, 7, 7)) { + if (__builtin_expect(__CFBitfieldGetValue(((const CFRuntimeBase *)cf)->_info, 7, 7), 1)) { allocator = kCFAllocatorSystemDefault; } else { allocator = CFGetAllocator(cf); (intptr_t)cf -= sizeof(CFAllocatorRef); } -#if defined(DEBUG) +#if defined(DEBUG) || defined(ENABLE_ZOMBIES) __CFZombifyDeallocatedMemory((void *)cf); if (!(__CFZombieLevel & (1 << 4))) { CFAllocatorDeallocate(allocator, (void *)cf); @@ -926,7 +959,7 @@ CF_EXPORT void _CFRelease(CFTypeRef cf) { } } } else { - if (0x8000 == lowBits) { + if (__builtin_expect(0x8000 == lowBits, 0)) { // Time to remove a bit from the external ref count if (0 == _CFDictionaryDecrementValue(__CFRuntimeExternRefCountTable, DISGUISE(cf))) { lowBits = 0x07fff; @@ -937,8 +970,8 @@ CF_EXPORT void _CFRelease(CFTypeRef cf) { lowBits--; } ((CFRuntimeBase *)cf)->_rc = lowBits; - if (is_threaded) __CFSpinUnlock(&__CFGlobalRetainLock); - if (__CFOASafe) { + __CFSpinUnlock(&__CFGlobalRetainLock); + if (__builtin_expect(__CFOASafe, 0)) { uint64_t compositeRC; compositeRC = (lowBits & 0x7fff) + ((uint64_t)(uintptr_t)CFDictionaryGetValue(__CFRuntimeExternRefCountTable, DISGUISE(cf)) << 15); if (compositeRC > (uint64_t)0x7fffffff) compositeRC = (uint64_t)0x7fffffff; diff --git a/Base.subproj/CFRuntime.h b/Base.subproj/CFRuntime.h index b25340b..c763910 100644 --- a/Base.subproj/CFRuntime.h +++ b/Base.subproj/CFRuntime.h @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -23,7 +21,7 @@ * @APPLE_LICENSE_HEADER_END@ */ /* CFRuntime.h - Copyright (c) 1999-2003, Apple, Inc. All rights reserved. + Copyright (c) 1999-2005, Apple, Inc. All rights reserved. */ #if !defined(__COREFOUNDATION_CFRUNTIME__) @@ -36,8 +34,58 @@ extern "C" { #endif +// GC: until we link against ObjC must use indirect functions. Overridden in CFSetupFoundationBridging +extern bool kCFUseCollectableAllocator; +extern bool (*__CFObjCIsCollectable)(void *); +extern const void* (*__CFObjCAssignIvar)(const void *value, const void *base, const void **slot); +extern const void* (*__CFObjCStrongAssign)(const void *value, const void **slot); +extern void* (*__CFObjCMemmoveCollectable)(void *dest, const void *src, unsigned); +extern void (*__CFObjCWriteBarrierRange)(void *, unsigned); + +// GC: primitives. +// is GC on? +#define CF_USING_COLLECTABLE_MEMORY (kCFUseCollectableAllocator) +// is GC on and is this the GC allocator? +#define CF_IS_COLLECTABLE_ALLOCATOR(allocator) (CF_USING_COLLECTABLE_MEMORY && (NULL == (allocator) || kCFAllocatorSystemDefault == (allocator))) +// is this allocated by the collector? +#define CF_IS_COLLECTABLE(obj) (__CFObjCIsCollectable ? __CFObjCIsCollectable((void*)obj) : false) + +// XXX_PCB for generational GC support. + +CF_INLINE const void* __CFAssignIvar(CFAllocatorRef allocator, const void *rvalue, const void *base, const void **lvalue) { + if (rvalue && CF_IS_COLLECTABLE_ALLOCATOR(allocator)) + return __CFObjCAssignIvar(rvalue, base, lvalue); + else + return (*lvalue = rvalue); +} + +CF_INLINE const void* __CFStrongAssign(CFAllocatorRef allocator, const void *rvalue, const void **lvalue) { + if (rvalue && CF_IS_COLLECTABLE_ALLOCATOR(allocator)) + return __CFObjCStrongAssign(rvalue, lvalue); + else + return (*lvalue = rvalue); +} + +// Use this form when the base pointer to the object is known. +#define CF_WRITE_BARRIER_BASE_ASSIGN(allocator, base, lvalue, rvalue) __CFAssignIvar(allocator, (const void*)rvalue, (const void*)base, (const void**)&(lvalue)) + +// Use this form when the base pointer to the object isn't known. +#define CF_WRITE_BARRIER_ASSIGN(allocator, lvalue, rvalue) __CFStrongAssign(allocator, (const void*)rvalue, (const void**)&(lvalue)) + +// Write-barrier memory move. +#define CF_WRITE_BARRIER_MEMMOVE(dst, src, size) __CFObjCMemmoveCollectable(dst, src, size) + +// Used by frameworks to assert they "KNOW WHAT THEY'RE DOING under GC." +CF_EXPORT CFAllocatorRef _CFAllocatorCreateGC(CFAllocatorRef allocator, CFAllocatorContext *context); + +// Zero-retain count CFAllocator functions, i.e. memory that will be collected, no dealloc necessary +CF_EXPORT void *_CFAllocatorAllocateGC(CFAllocatorRef allocator, CFIndex size, CFOptionFlags hint); +CF_EXPORT void *_CFAllocatorReallocateGC(CFAllocatorRef allocator, void *ptr, CFIndex newsize, CFOptionFlags hint); +CF_EXPORT void _CFAllocatorDeallocateGC(CFAllocatorRef allocator, void *ptr); + enum { - _kCFRuntimeNotATypeID = 0 + _kCFRuntimeNotATypeID = 0, + _kCFRuntimeScannedObject = (1 << 0) }; typedef struct __CFRuntimeClass { // Version 0 struct @@ -157,7 +205,7 @@ CF_EXPORT void _CFRuntimeUnregisterClassWithTypeID(CFTypeID typeID); */ typedef struct __CFRuntimeBase { void *_isa; -#if defined(__ppc__) +#if defined(__ppc__) || defined(__ppc64__) uint16_t _rc; uint16_t _info; #elif defined(__i386__) @@ -168,11 +216,11 @@ typedef struct __CFRuntimeBase { #endif } CFRuntimeBase; -#if defined(__ppc__) +#if defined(__ppc__) || defined(__ppc64__) #define INIT_CFRUNTIME_BASE(isa, info, rc) { isa, info, rc } #elif defined(__i386__) #define INIT_CFRUNTIME_BASE(isa, info, rc) { isa, rc, info } -#elif +#else #error unknown architecture #endif @@ -294,7 +342,7 @@ CFTypeID EXRangeGetTypeID(void) { 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); + newrange = (struct __EXRange *)_CFRuntimeCreateInstance(allocator, _kEXRangeID, extra, NULL); if (NULL == newrange) { return NULL; } diff --git a/Base.subproj/CFSortFunctions.c b/Base.subproj/CFSortFunctions.c index 47eb122..5596ac1 100644 --- a/Base.subproj/CFSortFunctions.c +++ b/Base.subproj/CFSortFunctions.c @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 diff --git a/Base.subproj/CFSystemDirectories.c b/Base.subproj/CFSystemDirectories.c new file mode 100644 index 0000000..9c2ff9e --- /dev/null +++ b/Base.subproj/CFSystemDirectories.c @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFSystemDirectories.c + Copyright 1997-2002, Apple, Inc. All rights reserved. + Responsibility: Ali Ozer +*/ + +/* + This file defines CFCopySearchPathForDirectoriesInDomains(). + On MacOS 8, this function returns empty array. + On Mach, it calls the System.framework enumeration functions. + On Windows, it calls the enumeration functions defined here. +*/ + +#include +#include "CFInternal.h" + +#if defined(__MACH__) + +/* We use the System framework implementation on Mach. +*/ +#include +#include +#include +#include + +CFSearchPathEnumerationState __CFStartSearchPathEnumeration(CFSearchPathDirectory dir, CFSearchPathDomainMask domainMask) { + return NSStartSearchPathEnumeration(dir, domainMask); +} + +CFSearchPathEnumerationState __CFGetNextSearchPathEnumeration(CFSearchPathEnumerationState state, uint8_t *path, CFIndex pathSize) { + CFSearchPathEnumerationState result; + // NSGetNextSearchPathEnumeration requires a MAX_PATH size + if (pathSize < PATH_MAX) { + uint8_t tempPath[PATH_MAX]; + result = NSGetNextSearchPathEnumeration(state, tempPath); + strlcpy(path, tempPath, pathSize); + } else { + result = NSGetNextSearchPathEnumeration(state, path); + } + return result; +} + +#endif + + +#if defined(__MACH__) || defined(__WIN32__) + +CFArrayRef CFCopySearchPathForDirectoriesInDomains(CFSearchPathDirectory directory, CFSearchPathDomainMask domainMask, Boolean expandTilde) { + CFMutableArrayRef array; + CFSearchPathEnumerationState state; + CFIndex homeLen = -1; + char cPath[CFMaxPathSize], home[CFMaxPathSize]; + + array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + state = __CFStartSearchPathEnumeration(directory, domainMask); + while ((state = __CFGetNextSearchPathEnumeration(state, cPath, sizeof(cPath)))) { + CFURLRef url = NULL; + if (expandTilde && (cPath[0] == '~')) { + if (homeLen < 0) { + CFURLRef homeURL = CFCopyHomeDirectoryURLForUser(NULL); + if (homeURL) { + CFURLGetFileSystemRepresentation(homeURL, true, home, CFMaxPathSize); + homeLen = strlen(home); + CFRelease(homeURL); + } + } + if (homeLen + strlen(cPath) < CFMaxPathSize) { + home[homeLen] = '\0'; + strcat(home, &cPath[1]); + url = CFURLCreateFromFileSystemRepresentation(NULL, home, strlen(home), true); + } + } else { + url = CFURLCreateFromFileSystemRepresentation(NULL, cPath, strlen(cPath), true); + } + if (url) { + CFArrayAppendValue(array, url); + CFRelease(url); + } + } + return array; +} + +#endif + +#undef numDirs +#undef numApplicationDirs +#undef numLibraryDirs +#undef numDomains +#undef invalidDomains +#undef invalidDomains + diff --git a/Base.subproj/CFUUID.c b/Base.subproj/CFUUID.c index 405fed1..aca6d47 100644 --- a/Base.subproj/CFUUID.c +++ b/Base.subproj/CFUUID.c @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -29,37 +27,8 @@ #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); +extern uint32_t _CFGenerateUUID(uint8_t *uuid_bytes); static CFMutableDictionaryRef _uniquedUUIDs = NULL; static CFSpinLock_t CFUUIDGlobalDataLock = 0; @@ -87,7 +56,8 @@ static void __CFUUIDAddUniqueUUID(CFUUIDRef uuid) { __CFSpinLock(&CFUUIDGlobalDataLock); if (_uniquedUUIDs == NULL) { /* Allocate table from default allocator */ - _uniquedUUIDs = CFDictionaryCreateMutable(NULL, 0, &__CFUUIDBytesDictionaryKeyCallBacks, &__CFnonRetainedUUIDDictionaryValueCallBacks); + // XXX_PCB these need to weakly hold the UUIDs, otherwise, they will never be collected. + _uniquedUUIDs = CFDictionaryCreateMutable(kCFAllocatorMallocZone, 0, &__CFUUIDBytesDictionaryKeyCallBacks, &__CFnonRetainedUUIDDictionaryValueCallBacks); } CFDictionarySetValue(_uniquedUUIDs, &(uuid->_bytes), uuid); __CFSpinUnlock(&CFUUIDGlobalDataLock); @@ -172,10 +142,10 @@ static CFUUIDRef __CFUUIDCreateWithBytesPrimitive(CFAllocatorRef allocator, CFUU CFUUIDRef CFUUIDCreate(CFAllocatorRef alloc) { /* Create a new bytes struct and then call the primitive. */ CFUUIDBytes bytes; - unsigned long retval = 0; + uint32_t retval = 0; __CFSpinLock(&CFUUIDGlobalDataLock); - retval = _CFGenerateUUID((uuid_t *)&bytes); + retval = _CFGenerateUUID((uint8_t *)&bytes); __CFSpinUnlock(&CFUUIDGlobalDataLock); return (retval == 0) ? __CFUUIDCreateWithBytesPrimitive(alloc, bytes, false) : NULL; diff --git a/Base.subproj/CFUUID.h b/Base.subproj/CFUUID.h index 6310f8b..13f265a 100644 --- a/Base.subproj/CFUUID.h +++ b/Base.subproj/CFUUID.h @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -23,7 +21,7 @@ * @APPLE_LICENSE_HEADER_END@ */ /* CFUUID.h - Copyright (c) 1999-2003, Apple, Inc. All rights reserved. + Copyright (c) 1999-2005, Apple, Inc. All rights reserved. */ #if !defined(__COREFOUNDATION_CFUUID__) diff --git a/Base.subproj/CFUtilities.c b/Base.subproj/CFUtilities.c index 4a937ab..fed064f 100644 --- a/Base.subproj/CFUtilities.c +++ b/Base.subproj/CFUtilities.c @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -27,8 +25,9 @@ Responsibility: Christopher Kane */ -#include "CFUtilities.h" +#include "CFUtilitiesPriv.h" #include "CFInternal.h" +#include "CFPriv.h" #include #include #include @@ -36,13 +35,16 @@ #include #include #include +#include #if defined(__MACH__) #include #include - #include #include + #include #include + #include #include + #include #endif #if defined(__WIN32__) #include @@ -51,11 +53,8 @@ #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) @@ -262,8 +261,6 @@ static CFStringRef _CFCopyLocalizedVersionKey(CFBundleRef *bundlePtr, CFStringRe 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; @@ -317,24 +314,22 @@ CFStringRef CFCopySystemVersionString(void) { return versionString; } -// These two functions cache the dictionaries to avoid calling _CFCopyVersionDictionary() more than once per dict desired +// Obsolete: These two functions cache the dictionaries to avoid calling _CFCopyVersionDictionary() more than once per dict desired +// In fact, they do not cache any more, because the file can change after +// apps are running in some situations, and apps need the new info. +// Proper caching and testing to see if the file has changed, without race +// conditions, would require semi-convoluted use of fstat(). CFDictionaryRef _CFCopySystemVersionDictionary(void) { - static CFPropertyListRef plist = NULL; // Set to -1 for failed lookup - if (!plist) { + CFPropertyListRef plist = NULL; plist = _CFCopyVersionDictionary(CFSTR("/System/Library/CoreServices/SystemVersion.plist")); - if (!plist) plist = (CFPropertyListRef)(-1); - } - return (plist == (CFPropertyListRef)(-1)) ? NULL : CFRetain(plist); + return plist; } CFDictionaryRef _CFCopyServerVersionDictionary(void) { - static CFPropertyListRef plist = NULL; // Set to -1 for failed lookup - if (!plist) { + CFPropertyListRef plist = NULL; plist = _CFCopyVersionDictionary(CFSTR("/System/Library/CoreServices/ServerVersion.plist")); - if (!plist) plist = (CFPropertyListRef)(-1); - } - return (plist == (CFPropertyListRef)(-1)) ? NULL : CFRetain(plist); + return plist; } CONST_STRING_DECL(_kCFSystemVersionProductNameKey, "ProductName") @@ -347,57 +342,35 @@ CONST_STRING_DECL(_kCFSystemVersionProductVersionStringKey, "Version") CONST_STRING_DECL(_kCFSystemVersionBuildStringKey, "Build") #if defined(__MACH__) + +typedef struct { + uint16_t primaryVersion; + uint8_t secondaryVersion; + uint8_t tertiaryVersion; +} CFLibraryVersion; + 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); + int32_t version = NSVersionOfLinkTimeLibrary(library); + if (-1 != version) { + ret.primaryVersion = version >> 16; + ret.secondaryVersion = (version >> 8) & 0xff; + ret.tertiaryVersion = version & 0xff; } 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; - } - } + int32_t version = NSVersionOfRunTimeLibrary(library); + if (-1 != version) { + ret.primaryVersion = version >> 16; + ret.secondaryVersion = (version >> 8) & 0xff; + ret.tertiaryVersion = version & 0xff; } return ret; } @@ -416,7 +389,7 @@ Else Continue checking (the next library) */ #define checkLibrary(LIBNAME, VERSIONFIELD) \ - {int vers = CFGetExecutableLinkedLibraryVersion(LIBNAME).primaryVersion; \ + {uint16_t vers = (NSVersionOfLinkTimeLibrary(LIBNAME) >> 16); \ 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) { @@ -442,9 +415,10 @@ CF_EXPORT Boolean _CFExecutableLinkedOnOrAfter(CFSystemVersion version) { {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 */ + {73, 10, 750, 505, 305, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF}, /* CFSystemVersionTiger */ + {0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF}, /* CFSystemVersionChablis */ }; - static char results[CFSystemVersionMax] = {-2, -2, -2, -2, -2}; /* We cache the results per-release; there are only a few of these... */ + static char results[CFSystemVersionMax] = {-2, -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]; @@ -453,25 +427,53 @@ CF_EXPORT Boolean _CFExecutableLinkedOnOrAfter(CFSystemVersion version) { 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); + checkLibrary("System", libSystemVersion); // Pretty much everyone links with this + checkLibrary("Cocoa", cocoaVersion); + checkLibrary("AppKit", appkitVersion); + checkLibrary("Foundation", fouVersion); + checkLibrary("CoreFoundation", cfVersion); + checkLibrary("Carbon", carbonVersion); + checkLibrary("ApplicationServices", applicationServicesVersion); + checkLibrary("CoreServices", coreServicesVersion); + checkLibrary("IOKit", iokitVersion); /* If not found, then simply return NO to indicate earlier --- compatibility by default, unfortunately */ return false; } +#else +CF_EXPORT Boolean _CFExecutableLinkedOnOrAfter(CFSystemVersion version) { + return true; +} #endif +__private_extern__ void *__CFLookupCarbonCoreFunction(const char *name) { + static void *image = NULL; + if (NULL == image) { + image = dlopen("/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/CarbonCore.framework/Versions/A/CarbonCore", RTLD_LAZY | RTLD_LOCAL); + } + void *dyfunc = NULL; + if (image) { + dyfunc = dlsym(image, name); + } + return dyfunc; +} + +__private_extern__ void *__CFLookupCFNetworkFunction(const char *name) { + static void *image = NULL; + if (NULL == image) { + image = dlopen("/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/CFNetwork.framework/Versions/A/CFNetwork", RTLD_LAZY | RTLD_LOCAL); + } + void *dyfunc = NULL; + if (image) { + dyfunc = dlsym(image, name); + } + return dyfunc; +} -void CFShow(const void *obj) { // ??? supposed to use stderr for Logging ? + +static void _CFShowToFile(FILE *file, Boolean flush, const void *obj) { CFStringRef str; CFIndex idx, cnt; CFStringInlineBuffer buffer; @@ -490,21 +492,38 @@ void CFShow(const void *obj) { // ??? supposed to use stderr for Logging ? } cnt = CFStringGetLength(str); + // iTunes used OutputDebugStringW(theString); + CFStringInitInlineBuffer(str, &buffer, CFRangeMake(0, cnt)); for (idx = 0; idx < cnt; idx++) { UniChar ch = __CFStringGetCharacterFromInlineBufferQuick(&buffer, idx); if (ch < 128) { - fprintf (stderr, "%c", ch); + fprintf(file, "%c", ch); lastNL = (ch == '\n'); } else { - fprintf (stderr, "\\u%04x", ch); + fprintf(file, "\\u%04x", ch); } } - if (!lastNL) fprintf(stderr, "\n"); + if (!lastNL) { + fprintf(file, "\n"); + if (flush) fflush(file); + } if (str) CFRelease(str); } +void CFShow(const void *obj) { + _CFShowToFile(stderr, true, obj); +} + +static CFGregorianDate gregorianDate(void) { + CFTimeZoneRef tz = CFTimeZoneCopySystem(); // specifically choose system time zone for logs + CFGregorianDate gdate = CFAbsoluteTimeGetGregorianDate(CFAbsoluteTimeGetCurrent(), tz); + CFRelease(tz); + gdate.second = gdate.second + 0.0005; + return gdate; +} + void CFLog(int p, CFStringRef format, ...) { CFStringRef result; va_list argList; @@ -516,14 +535,11 @@ void CFLog(int p, CFStringRef format, ...) { __CFSpinLock(&lock); #if defined(__WIN32__) - printf("*** CFLog (%d): %s[%d] ", p, __argv[0], GetCurrentProcessId()); + fprintf(stderr, "*** %s[%ld] CFLog(%d): ", *_CFGetProgname(), GetCurrentProcessId(), p); #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); + CFGregorianDate gdate = gregorianDate(); + // Date format: YYYY '-' MM '-' DD ' ' hh ':' mm ':' ss.fff + fprintf_l(stderr, NULL, "%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); @@ -531,3 +547,4 @@ void CFLog(int p, CFStringRef format, ...) { CFRelease(result); } + diff --git a/Base.subproj/CFUtilities.h b/Base.subproj/CFUtilities.h index 7d002f8..81130d8 100644 --- a/Base.subproj/CFUtilities.h +++ b/Base.subproj/CFUtilities.h @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -23,7 +21,7 @@ * @APPLE_LICENSE_HEADER_END@ */ /* CFUtilities.h - Copyright (c) 1998-2003, Apple, Inc. All rights reserved. + Copyright (c) 1998-2005, Apple, Inc. All rights reserved. */ #if !defined(__COREFOUNDATION_CFUTILITIES__) @@ -35,37 +33,50 @@ extern "C" { #endif +/* +Only hits in Panther sources on this file: +./DisplayServices/O3Manager/O3Master.m:#import +./HIServices/CoreDrag.subproj/DragManager.c:#include +./HIToolbox/Menus/Source/MenuEvents.cp:#include +./WebBrowser/BugReportController.m:#import + + +lore% grep '\(CFLog2\|CFMergeSortArray\|CFQSortArray\|CFLibraryVersion\|primaryVersion\|secondaryVersion\|tertiaryVersion\|CFGetExecutableLinkedLibraryVersion\|CFGetExecutingLibraryVersion\|CFSystemVersion\|_CFExecutableLinkedOnOrAfter\)' ./DisplayServices/O3Manager/O3Master.m ./HIServices/CoreDrag.subproj/DragManager.c ./HIToolbox/Menus/Source/MenuEvents.cp ./WebBrowser/BugReportController.m + +./DisplayServices/O3Manager/O3Master.m: if (!_CFExecutableLinkedOnOrAfter(CFSystemVersionJaguar)) +./HIServices/CoreDrag.subproj/DragManager.c: if( _CFExecutableLinkedOnOrAfter( CFSystemVersionJaguar ) && (info->remoteAllowableActions == kCoreDragActionNothing) ) +./HIToolbox/Menus/Source/MenuEvents.cp: if ( _CFExecutableLinkedOnOrAfter( CFSystemVersionPanther ) ) + + +... Other than CF, Foundation, and AppKit of course. + +*/ + + + 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. + + Note that for non-MACH this function always returns true. */ typedef enum { CFSystemVersionCheetah = 0, /* 10.0 */ CFSystemVersionPuma = 1, /* 10.1 */ - CFSystemVersionJaguar = 2, /* 10.2? TBD */ - CFSystemVersionPanther = 3, /* Post-Jaguar */ + CFSystemVersionJaguar = 2, /* 10.2 */ + CFSystemVersionPanther = 3, /* 10.3 */ CFSystemVersionPinot = 3, /* Deprecated name for Panther */ - CFSystemVersionMerlot = 4, /* Post-Panther */ + CFSystemVersionTiger = 4, /* 10.4 */ + CFSystemVersionMerlot = 4, /* Deprecated name for Tiger */ + CFSystemVersionChablis = 5, /* Post-Tiger */ CFSystemVersionMax /* This should bump up when new entries are added */ } CFSystemVersion; CF_EXPORT Boolean _CFExecutableLinkedOnOrAfter(CFSystemVersion version); - -#endif /* __MACH__ */ #if defined(__cplusplus) diff --git a/Base.subproj/CFUtilitiesPriv.h b/Base.subproj/CFUtilitiesPriv.h new file mode 100644 index 0000000..5553d91 --- /dev/null +++ b/Base.subproj/CFUtilitiesPriv.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFUtilitiesPriv.h + Copyright (c) 1998-2005, 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); + +/* _CFExecutableLinkedOnOrAfter(releaseVersionName) will return YES if the current executable seems to be linked on or after the specified release. Example: If you specify CFSystemVersionPuma (10.1), you will get back true for executables linked on Puma or Jaguar(10.2), but false for those linked on Cheetah (10.0) or any of its software updates (10.0.x). You will also get back false for any app whose version info could not be figured out. + This function caches its results, so no need to cache at call sites. + + Note that for non-MACH this function always returns true. +*/ +typedef enum { + CFSystemVersionCheetah = 0, /* 10.0 */ + CFSystemVersionPuma = 1, /* 10.1 */ + CFSystemVersionJaguar = 2, /* 10.2 */ + CFSystemVersionPanther = 3, /* 10.3 */ + CFSystemVersionPinot = 3, /* Deprecated name for Panther */ + CFSystemVersionTiger = 4, /* 10.4 */ + CFSystemVersionMerlot = 4, /* Deprecated name for Tiger */ + CFSystemVersionChablis = 5, /* Post-Tiger */ + CFSystemVersionMax /* This should bump up when new entries are added */ +} CFSystemVersion; + +CF_EXPORT Boolean _CFExecutableLinkedOnOrAfter(CFSystemVersion version); + + + +#if defined(__cplusplus) +} +#endif + +#endif /* ! __COREFOUNDATION_CFUTILITIES__ */ + diff --git a/Base.subproj/CoreFoundation.h b/Base.subproj/CoreFoundation.h index b8bee17..1baa647 100644 --- a/Base.subproj/CoreFoundation.h +++ b/Base.subproj/CoreFoundation.h @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -23,29 +21,14 @@ * @APPLE_LICENSE_HEADER_END@ */ /* CoreFoundation.h - Copyright (c) 1998-2003, Apple, Inc. All rights reserved. + Copyright (c) 1998-2005, Apple, Inc. All rights reserved. */ #if !defined(__COREFOUNDATION_COREFOUNDATION__) #define __COREFOUNDATION_COREFOUNDATION__ 1 +#define __COREFOUNDATION__ 1 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - +#include #include #include #include @@ -65,30 +48,56 @@ #if defined(__STDC_VERSION__) && (199901L <= __STDC_VERSION__) #include -//#include #include #include #endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(__MACH__) || defined(__WIN32__) +#include +#include +#include +#include +#endif + #include #include #include #include #include +#include #include #include +#include + +#include + #if defined(__MACH__) + + #include -#endif -#if defined(__MACH__) || defined(__WIN32__) -#include -#include -#include -#include -#endif + +#endif // __MACH__ #endif /* ! __COREFOUNDATION_COREFOUNDATION__ */ diff --git a/Base.subproj/ForFoundationOnly.h b/Base.subproj/ForFoundationOnly.h index e7a6e50..1c25d45 100644 --- a/Base.subproj/ForFoundationOnly.h +++ b/Base.subproj/ForFoundationOnly.h @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -23,7 +21,7 @@ * @APPLE_LICENSE_HEADER_END@ */ /* ForFoundationOnly.h - Copyright (c) 1998-2003, Apple, Inc. All rights reserved. + Copyright (c) 1998-2005, Apple, Inc. All rights reserved. */ #if !CF_BUILDING_CF && !NSBUILDINGFOUNDATION @@ -39,13 +37,28 @@ #include #include #include +#include // NOTE: miscellaneous declarations are at the end +// ---- CFRuntime material ---------------------------------------- + +#if defined(__cplusplus) +extern "C" { +#endif + +#include + +CF_EXPORT +void __CFSetupFoundationBridging(void *, void *, void *, void *); + +#if defined(__cplusplus) +} +#endif // ---- CFBundle material ---------------------------------------- -#include "CFBundlePriv.h" +#include #if defined(__cplusplus) extern "C" { @@ -58,6 +71,9 @@ CF_EXPORT const CFStringRef _kCFBundleResourcesFileMappedKey; CF_EXPORT const CFStringRef _kCFBundleCFMLoadAsBundleKey; CF_EXPORT const CFStringRef _kCFBundleAllowMixedLocalizationsKey; +CF_EXPORT CFArrayRef _CFFindBundleResources(CFBundleRef bundle, CFURLRef bundleURL, CFStringRef subDirName, CFArrayRef searchLanguages, CFStringRef resName, CFArrayRef resTypes, CFIndex limit, UInt8 version); + +CF_EXPORT UInt8 _CFBundleLayoutVersion(CFBundleRef bundle); CF_EXPORT CFArrayRef _CFBundleCopyLanguageSearchListInDirectory(CFAllocatorRef alloc, CFURLRef url, UInt8 *version); CF_EXPORT CFArrayRef _CFBundleGetLanguageSearchList(CFBundleRef bundle); @@ -67,8 +83,105 @@ CF_EXPORT CFArrayRef _CFBundleGetLanguageSearchList(CFBundleRef bundle); #endif +#if defined(__MACH__) +// ---- CFPreferences material ---------------------------------------- + +#define DEBUG_PREFERENCES_MEMORY 0 + +#if DEBUG_PREFERENCES_MEMORY +#include "../Tests/CFCountingAllocator.h" +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +extern void _CFPreferencesPurgeDomainCache(void); + +typedef struct { + void * (*createDomain)(CFAllocatorRef allocator, CFTypeRef context); + void (*freeDomain)(CFAllocatorRef allocator, CFTypeRef context, void *domain); + CFTypeRef (*fetchValue)(CFTypeRef context, void *domain, CFStringRef key); // Caller releases + void (*writeValue)(CFTypeRef context, void *domain, CFStringRef key, CFTypeRef value); + Boolean (*synchronize)(CFTypeRef context, void *domain); + void (*getKeysAndValues)(CFAllocatorRef alloc, CFTypeRef context, void *domain, void **buf[], CFIndex *numKeyValuePairs); + CFDictionaryRef (*copyDomainDictionary)(CFTypeRef context, void *domain); + /* HACK - see comment on _CFPreferencesDomainSetIsWorldReadable(), below */ + void (*setIsWorldReadable)(CFTypeRef context, void *domain, Boolean isWorldReadable); +} _CFPreferencesDomainCallBacks; + +CF_EXPORT CFAllocatorRef __CFPreferencesAllocator(void); +CF_EXPORT const _CFPreferencesDomainCallBacks __kCFVolatileDomainCallBacks; + +#if defined(__WIN32__) +CF_EXPORT const _CFPreferencesDomainCallBacks __kCFWindowsRegistryDomainCallBacks; +#else +CF_EXPORT const _CFPreferencesDomainCallBacks __kCFXMLPropertyListDomainCallBacks; +#endif + +typedef struct __CFPreferencesDomain * CFPreferencesDomainRef; + +CF_EXPORT CFPreferencesDomainRef _CFPreferencesDomainCreate(CFTypeRef context, const _CFPreferencesDomainCallBacks *callBacks); +CF_EXPORT CFPreferencesDomainRef _CFPreferencesStandardDomain(CFStringRef domainName, CFStringRef userName, CFStringRef hostName); + +CF_EXPORT CFTypeRef _CFPreferencesDomainCreateValueForKey(CFPreferencesDomainRef domain, CFStringRef key); +CF_EXPORT void _CFPreferencesDomainSet(CFPreferencesDomainRef domain, CFStringRef key, CFTypeRef value); +CF_EXPORT Boolean _CFPreferencesDomainSynchronize(CFPreferencesDomainRef domain); + +/* If buf is NULL, no allocation occurs. *numKeyValuePairs should be set to the current size of buf; alloc should be allocator to allocate/reallocate buf, or kCFAllocatorNull if buf is not to be (re)allocated. No matter what happens, *numKeyValuePairs is always set to the number of pairs in the domain */ +CF_EXPORT void _CFPreferencesDomainGetKeysAndValues(CFAllocatorRef alloc, CFPreferencesDomainRef domain, void **buf[], CFIndex *numKeyValuePairs); + +CF_EXPORT CFArrayRef _CFPreferencesCreateDomainList(CFStringRef userName, CFStringRef hostName); +CF_EXPORT Boolean _CFSynchronizeDomainCache(void); + +CF_EXPORT void _CFPreferencesDomainSetDictionary(CFPreferencesDomainRef domain, CFDictionaryRef dict); +CF_EXPORT CFDictionaryRef _CFPreferencesDomainCopyDictionary(CFPreferencesDomainRef domain); +CF_EXPORT CFDictionaryRef _CFPreferencesDomainDeepCopyDictionary(CFPreferencesDomainRef domain); +CF_EXPORT Boolean _CFPreferencesDomainExists(CFStringRef domainName, CFStringRef userName, CFStringRef hostName); + +/* HACK - this is to work around the fact that individual domains lose the information about their user/host/app triplet at creation time. We should find a better way to propogate this information. REW, 1/13/00 */ +CF_EXPORT void _CFPreferencesDomainSetIsWorldReadable(CFPreferencesDomainRef domain, Boolean isWorldReadable); + +typedef struct { + CFMutableArrayRef _search; // the search list; an array of _CFPreferencesDomains + CFMutableDictionaryRef _dictRep; // Mutable; a collapsed view of the search list, expressed as a single dictionary + CFStringRef _appName; +} _CFApplicationPreferences; + +CF_EXPORT _CFApplicationPreferences *_CFStandardApplicationPreferences(CFStringRef appName); +CF_EXPORT _CFApplicationPreferences *_CFApplicationPreferencesCreateWithUser(CFStringRef userName, CFStringRef appName); +CF_EXPORT void _CFDeallocateApplicationPreferences(_CFApplicationPreferences *self); +CF_EXPORT CFTypeRef _CFApplicationPreferencesCreateValueForKey(_CFApplicationPreferences *prefs, CFStringRef key); +CF_EXPORT void _CFApplicationPreferencesSet(_CFApplicationPreferences *self, CFStringRef defaultName, CFTypeRef value); +CF_EXPORT void _CFApplicationPreferencesRemove(_CFApplicationPreferences *self, CFStringRef defaultName); +CF_EXPORT Boolean _CFApplicationPreferencesSynchronize(_CFApplicationPreferences *self); +CF_EXPORT void _CFApplicationPreferencesUpdate(_CFApplicationPreferences *self); // same as updateDictRep +CF_EXPORT CFDictionaryRef _CFApplicationPreferencesCopyRepresentation3(_CFApplicationPreferences *self, CFDictionaryRef hint, CFDictionaryRef insertion, CFPreferencesDomainRef afterDomain); +CF_EXPORT CFDictionaryRef _CFApplicationPreferencesCopyRepresentationWithHint(_CFApplicationPreferences *self, CFDictionaryRef hint); // same as dictRep +CF_EXPORT void _CFApplicationPreferencesSetStandardSearchList(_CFApplicationPreferences *appPreferences); +CF_EXPORT void _CFApplicationPreferencesSetSearchList(_CFApplicationPreferences *self, CFArrayRef newSearchList); +CF_EXPORT void _CFApplicationPreferencesSetCacheForApp(_CFApplicationPreferences *appPrefs, CFStringRef appName); +CF_EXPORT void _CFApplicationPreferencesAddSuitePreferences(_CFApplicationPreferences *appPrefs, CFStringRef suiteName); +CF_EXPORT void _CFApplicationPreferencesRemoveSuitePreferences(_CFApplicationPreferences *appPrefs, CFStringRef suiteName); + +CF_EXPORT void _CFApplicationPreferencesAddDomain(_CFApplicationPreferences *self, CFPreferencesDomainRef domain, Boolean addAtTop); +CF_EXPORT Boolean _CFApplicationPreferencesContainsDomain(_CFApplicationPreferences *self, CFPreferencesDomainRef domain); +CF_EXPORT void _CFApplicationPreferencesRemoveDomain(_CFApplicationPreferences *self, CFPreferencesDomainRef domain); + +CF_EXPORT CFTypeRef _CFApplicationPreferencesSearchDownToDomain(_CFApplicationPreferences *self, CFPreferencesDomainRef stopper, CFStringRef key); + + +#if defined(__cplusplus) +} +#endif + +#endif // __MACH__ + + + // ---- CFString material ---------------------------------------- + #if defined(__cplusplus) extern "C" { #endif @@ -87,17 +200,15 @@ extern "C" { */ CF_EXPORT CFIndex __CFStringEncodeByteStream(CFStringRef string, CFIndex rangeLoc, CFIndex rangeLen, Boolean generatingExternalFile, CFStringEncoding encoding, char lossByte, UInt8 *buffer, CFIndex max, CFIndex *usedBufLen); +CF_EXPORT CFStringRef __CFStringCreateImmutableFunnel2(CFAllocatorRef alloc, const void *bytes, CFIndex numBytes, CFStringEncoding encoding, Boolean possiblyExternalFormat, Boolean tryToReduceUnicode, Boolean hasLengthByte, Boolean hasNullByte, Boolean noCopy, CFAllocatorRef contentsDeallocator); + 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; + if (encoding != kCFStringEncodingUTF8) return false; return true; - case 0x200: // ISO range - return true; case 0x600: // National standards range if (encoding != kCFStringEncodingASCII) return false; @@ -106,8 +217,6 @@ CF_INLINE Boolean __CFStringEncodingIsSupersetOfASCII(CFStringEncoding encoding) 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; @@ -121,6 +230,7 @@ CF_INLINE Boolean __CFStringEncodingIsSupersetOfASCII(CFStringEncoding encoding) } } + /* Desperately using extern here */ CF_EXPORT CFStringEncoding __CFDefaultEightBitStringEncoding; CF_EXPORT CFStringEncoding __CFStringComputeEightBitStringEncoding(void); @@ -205,10 +315,28 @@ CF_INLINE UniChar __CFStringGetCharacterFromInlineBufferQuick(CFStringInlineBuff 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); +/* For NSString (and NSAttributedString) usage, mutate with isMutable check +*/ enum {_CFStringErrNone = 0, _CFStringErrNotMutable = 1, _CFStringErrNilArg = 2, _CFStringErrBounds = 3}; - +CF_EXPORT int __CFStringCheckAndReplace(CFMutableStringRef str, CFRange range, CFStringRef replacement); CF_EXPORT Boolean __CFStringNoteErrors(void); // Should string errors raise? +/* For NSString usage, guarantees that the contents can be extracted as 8-bit bytes in the __CFStringGetEightBitStringEncoding(). +*/ +CF_EXPORT Boolean __CFStringIsEightBit(CFStringRef str); + +/* For NSCFString usage, these do range check (where applicable) but don't check for ObjC dispatch +*/ +CF_EXPORT int _CFStringCheckAndGetCharacterAtIndex(CFStringRef str, CFIndex idx, UniChar *ch); +CF_EXPORT int _CFStringCheckAndGetCharacters(CFStringRef str, CFRange range, UniChar *buffer); +CF_EXPORT CFIndex _CFStringGetLength2(CFStringRef str); +CF_EXPORT CFHashCode __CFStringHash(CFTypeRef cf); +CF_EXPORT CFHashCode CFStringHashISOLatin1CString(const uint8_t *bytes, CFIndex len); +CF_EXPORT CFHashCode CFStringHashCString(const uint8_t *bytes, CFIndex len); +CF_EXPORT CFHashCode CFStringHashCharacters(const UniChar *characters, CFIndex len); +CF_EXPORT CFHashCode CFStringHashNSString(CFStringRef str); + + #if defined(__cplusplus) } #endif @@ -252,6 +380,17 @@ typedef struct { uint64_t _offsetTableOffset; } CFBinaryPlistTrailer; +extern bool __CFBinaryPlistGetTopLevelInfo(const uint8_t *databytes, uint64_t datalen, uint8_t *marker, uint64_t *offset, CFBinaryPlistTrailer *trailer); +extern bool __CFBinaryPlistGetOffsetForValueFromArray(const uint8_t *databytes, uint64_t datalen, uint64_t startOffset, const CFBinaryPlistTrailer *trailer, CFIndex idx, uint64_t *offset); +extern bool __CFBinaryPlistGetOffsetForValueFromDictionary(const uint8_t *databytes, uint64_t datalen, uint64_t startOffset, const CFBinaryPlistTrailer *trailer, CFTypeRef key, uint64_t *koffset, uint64_t *voffset); +extern bool __CFBinaryPlistCreateObject(const uint8_t *databytes, uint64_t datalen, uint64_t startOffset, const CFBinaryPlistTrailer *trailer, CFAllocatorRef allocator, CFOptionFlags mutabilityOption, CFMutableDictionaryRef objects, CFPropertyListRef *plist); +extern CFIndex __CFBinaryPlistWriteToStream(CFPropertyListRef plist, CFTypeRef stream); + + +// ---- Used by property list parsing in Foundation + +extern CFTypeRef _CFPropertyListCreateFromXMLData(CFAllocatorRef allocator, CFDataRef xmlData, CFOptionFlags option, CFStringRef *errorString, Boolean allowNewTypes, CFPropertyListFormat *format); + // ---- Miscellaneous material ---------------------------------------- @@ -265,14 +404,32 @@ extern "C" { CF_EXPORT CFTypeID CFTypeGetTypeID(void); +CF_EXPORT CFTypeRef _CFRetainGC(CFTypeRef cf); +CF_EXPORT void _CFReleaseGC(CFTypeRef cf); + 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 CFCharacterSetCompact(CFMutableCharacterSetRef theSet); +CF_EXPORT void CFCharacterSetFast(CFMutableCharacterSetRef theSet); + +CF_EXPORT const void *_CFArrayCheckAndGetValueAtIndex(CFArrayRef array, CFIndex idx); CF_EXPORT void _CFArrayReplaceValues(CFMutableArrayRef array, CFRange range, const void **newValues, CFIndex newCount); +/* Enumeration + Call CFStartSearchPathEnumeration() once, then call + CFGetNextSearchPathEnumeration() one or more times with the returned state. + The return value of CFGetNextSearchPathEnumeration() should be used as + the state next time around. + When CFGetNextSearchPathEnumeration() returns 0, you're done. +*/ +typedef CFIndex CFSearchPathEnumerationState; +CF_EXPORT CFSearchPathEnumerationState __CFStartSearchPathEnumeration(CFSearchPathDirectory dir, CFSearchPathDomainMask domainMask); +CF_EXPORT CFSearchPathEnumerationState __CFGetNextSearchPathEnumeration(CFSearchPathEnumerationState state, UInt8 *path, CFIndex pathSize); + /* For use by NSNumber and CFNumber. Hashing algorithm for CFNumber: M = Max CFHashCode (assumed to be unsigned) @@ -291,14 +448,27 @@ CF_INLINE CFHashCode _CFHashDouble(double d) { return (CFHashCode)(fmod(dInt, (double)0xFFFFFFFF) + ((d - dInt) * 0xFFFFFFFF)); } +CF_EXPORT CFURLRef _CFURLAlloc(CFAllocatorRef allocator); +CF_EXPORT void _CFURLInitWithString(CFURLRef url, CFStringRef string, CFURLRef baseURL); +CF_EXPORT void _CFURLInitFSPath(CFURLRef url, CFStringRef path); +CF_EXPORT Boolean _CFStringIsLegalURLString(CFStringRef string); +CF_EXPORT void *__CFURLReservedPtr(CFURLRef url); +CF_EXPORT void __CFURLSetReservedPtr(CFURLRef url, void *ptr); typedef void (*CFRunLoopPerformCallBack)(void *info); - +CF_EXPORT void _CFRunLoopPerformEnqueue(CFRunLoopRef rl, CFStringRef mode, CFRunLoopPerformCallBack callout, void *info); +CF_EXPORT Boolean _CFRunLoopFinished(CFRunLoopRef rl, CFStringRef mode); #if defined(__MACH__) -#include + #if !defined(__CFReadTSR) + #include + #define __CFReadTSR() mach_absolute_time() + #endif +#elif defined(__WIN32__) CF_INLINE UInt64 __CFReadTSR(void) { - return mach_absolute_time(); + LARGE_INTEGER freq; + QueryPerformanceCounter(&freq); + return freq.QuadPart; } #else CF_INLINE UInt64 __CFReadTSR(void) { @@ -313,7 +483,7 @@ CF_INLINE UInt64 __CFReadTSR(void) { __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__) +#elif defined(__ppc__) || defined(__ppc64__) /* 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. */ @@ -324,7 +494,7 @@ CF_INLINE UInt64 __CFReadTSR(void) { __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 +#error Do not know how to read a time stamp register on this architecture now.time64 = (uint64_t)0; #endif return now.time64; diff --git a/Base.subproj/auto_stubs.h b/Base.subproj/auto_stubs.h new file mode 100644 index 0000000..5eed384 --- /dev/null +++ b/Base.subproj/auto_stubs.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* auto_stubs.h + Copyright 2005, Apple, Inc. All rights reserved. +*/ + + +#include +#include + +/* Stubs for functions in libauto used by CoreFoundation. */ + +typedef malloc_zone_t auto_zone_t; +typedef enum { AUTO_OBJECT_SCANNED, AUTO_OBJECT_UNSCANNED, AUTO_MEMORY_SCANNED, AUTO_MEMORY_UNSCANNED } auto_memory_type_t; + +CF_INLINE auto_zone_t *auto_zone(void) { return NULL; } +CF_INLINE void* auto_zone_allocate_object(auto_zone_t *zone, size_t size, auto_memory_type_t type, boolean_t rc, boolean_t clear) { return NULL; } +CF_INLINE const void *auto_zone_base_pointer(auto_zone_t *zone, const void *ptr) { return NULL; } +CF_INLINE void auto_zone_retain(auto_zone_t *zone, void *ptr) {} +CF_INLINE unsigned int auto_zone_release(auto_zone_t *zone, void *ptr) { return 0; } +CF_INLINE unsigned int auto_zone_retain_count(auto_zone_t *zone, const void *ptr) { return 0; } +CF_INLINE void auto_zone_set_layout_type(auto_zone_t *zone, void *ptr, auto_memory_type_t type) {} +CF_INLINE void auto_zone_write_barrier_range(auto_zone_t *zone, void *address, size_t size) {} + diff --git a/Base.subproj/uuid.c b/Base.subproj/uuid.c index 490d2c7..739396f 100644 --- a/Base.subproj/uuid.c +++ b/Base.subproj/uuid.c @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -27,6 +25,127 @@ Responsibility: Doug Davidson */ +#include +#include +#include "CFInternal.h" +#include "CFUtilitiesPriv.h" +#include + +typedef struct +{ + unsigned char eaddr[6]; /* 6 bytes of ethernet hardware address */ +} uuid_address_t; + +#if defined(__WIN32__) + +static OSErr GetEthernetAddr(uuid_address_t *addr) { + return -1; +} + +#else + +#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) { + snprintf(p, 2, "%d", c-'0'); // at least 3 bytes available here, we hope! + } + + 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); +} + +#undef IFR_NEXT + +#endif // __WIN32__ + +__private_extern__ CFStringRef __CFCopyRegularEthernetAddrString(void) { + uuid_address_t addr; + static CFStringRef string = NULL; + static Boolean lookedUpAddr = false; + + if (!lookedUpAddr) { + 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) { + 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); +} + #if defined(__WIN32__) /* _CFGenerateUUID function just calls the COM library's UUID generator * (Aleksey Dukhnyakov) @@ -35,13 +154,13 @@ #include #include -unsigned long _CFGenerateUUID(uuid_t *uuid) { +LONG _CFGenerateUUID(uint8_t *uuid_bytes) { RPC_STATUS rStatus; /* call GetScode() function to get RPC_STATUS, because * CoCreateGuid(uuid) function return HRESULT type */ - rStatus = GetScode(CoCreateGuid(uuid)); + rStatus = GetScode(CoCreateGuid((uuid_t *)uuid_bytes)); /* We accept only following results RPC_S_OK, RPC_S_UUID_LOCAL_ONLY */ @@ -98,21 +217,6 @@ unsigned long _CFGenerateUUID(uuid_t *uuid) { ** */ -#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 * @@ -120,39 +224,28 @@ unsigned long _CFGenerateUUID(uuid_t *uuid) { * structure regardless what platform it is on. */ -struct uuid_t { - unsigned long time_low; - unsigned short time_mid; - unsigned short time_hi_and_version; +struct uuid_v1_t { + uint32_t time_low; + uint16_t time_mid; + uint16_t 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 +typedef struct uuid_v1_t uuid_v1_t; 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; + uint32_t lo; + uint32_t hi; } uuid_time_t; static OSErr GenRandomEthernet(uuid_address_t *addr); -static OSErr GetEthernetAddr(uuid_address_t *addr); - static OSErr ReadPrefData(void); /* @@ -161,8 +254,8 @@ static OSErr ReadPrefData(void); 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; +static uint16_t GTimeAdjust = 0; +static uint16_t GClockSeq = 0; /* @@ -333,8 +426,8 @@ static const long uuid_c_version = 1; ****************************************************************************/ typedef struct { - unsigned long lo; - unsigned long hi; + uint32_t lo; + uint32_t hi; } unsigned64_t; /* @@ -343,17 +436,17 @@ typedef struct { 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*/ +//static uint16_t time_adjust; /* 'adjustment' to ensure uniqness */ +//static uint16_t 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 */ +static uint32_t rand_m = 0; /* multiplier */ +static uint32_t rand_ia = 0; /* adder #1 */ +static uint32_t rand_ib = 0; /* adder #2 */ +static uint32_t rand_irand = 0; /* random value */ typedef enum { @@ -386,7 +479,7 @@ static void true_random_init (void); /* * T R U E _ R A N D O M */ -static unsigned short true_random (void); +static uint16_t true_random (void); /* @@ -398,7 +491,7 @@ static unsigned short true_random (void); * 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*/); +static void new_clock_seq ( uint16_t * /*clock_seq*/); /* @@ -426,6 +519,13 @@ static uuid_address_t saved_addr = {{0, 0, 0, 0, 0, 0}}; static int got_address = false; static int last_addr_result = false; +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; +} /* **++ @@ -501,44 +601,6 @@ static int uuid_get_address(uuid_address_t *addr) 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 @@ -719,11 +781,11 @@ static void uuid__get_os_time (uuid_time_t * uuid_time) double utc_at = at / utc_conversion_factor; /* Convert 'at' in double seconds to 100ns units in utc */ - utc.hi = (unsigned long)utc_at; + utc.hi = (uint32_t)utc_at; utc_at -= (double)utc.hi; utc_at *= utc_conversion_factor; utc_at *= 10000000.0; - utc.lo = (unsigned long)utc_at; + utc.lo = (uint32_t)utc_at; /* * Offset between DTSS formatted times and Unix formatted times. @@ -795,60 +857,9 @@ static OSErr init() 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) +static uint32_t _CFGenerateV1UUID(uint8_t *uuid_bytes) { + uuid_v1_t *uuid = (uuid_v1_t *)uuid_bytes; OSErr err; uuid_address_t eaddr; int got_no_time = false; @@ -935,6 +946,37 @@ __private_extern__ unsigned long _CFGenerateUUID(uuid_t *uuid) return 0; } +#if defined(__MACH__) + +#include + +__private_extern__ uint32_t _CFGenerateUUID(uuid_t *uuid_bytes) { + static Boolean useV1UUIDs = false, checked = false; + uuid_t uuid; + if (!checked) { + const char *value = getenv("CFUUIDVersionNumber"); + if (value) { + if (1 == strtoul(value, NULL, 0)) useV1UUIDs = true; + } else { + if (!_CFExecutableLinkedOnOrAfter(CFSystemVersionTiger)) useV1UUIDs = true; + } + checked = true; + } + if (useV1UUIDs) return _CFGenerateV1UUID(uuid_bytes); + uuid_generate_random(uuid); + memcpy(uuid_bytes, uuid, sizeof(uuid)); + return 0; +} + +#else + +__private_extern__ uint32_t _CFGenerateUUID(uuid_t *uuid_bytes) { + return _CFGenerateV1UUID(uuid_bytes); +} + +#endif // __MACH__ + + /***************************************************************************** * * LOCAL MATH PROCEDURES - math procedures used internally by the UUID module @@ -1020,16 +1062,16 @@ static uuid_compval_t time_cmp(uuid_time_t *time1,uuid_time_t *time2) static void true_random_init (void) { uuid_time_t t; - unsigned short *seedp, seed=0; + uint16_t *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; + static uint32_t rand_m_init = 971; + static uint32_t rand_ia_init = 11113; + static uint32_t rand_ib_init = 104322; + static uint32_t rand_irand_init = 4181; rand_m = rand_m_init; rand_ia = rand_ia_init; @@ -1048,7 +1090,7 @@ static void true_random_init (void) * are multiple processes creating UUID's on a system, we add in the PID. */ uuid__get_os_time(&t); - seedp = (unsigned short *)(&t); + seedp = (uint16_t *)(&t); seed ^= *seedp++; seed ^= *seedp++; seed ^= *seedp++; @@ -1063,7 +1105,7 @@ static void true_random_init (void) ** using this routine should modify the return value accordingly. **/ -static unsigned short true_random (void) +static uint16_t true_random (void) { rand_m += 7; rand_ia += 1907; @@ -1097,11 +1139,11 @@ static unsigned short true_random (void) static void new_clock_seq #ifdef _DCE_PROTO_ ( - unsigned short *clkseq + uint16_t *clkseq ) #else (clkseq) -unsigned short *clkseq; +uint16_t *clkseq; #endif { /* @@ -1152,6 +1194,7 @@ static OSErr ReadPrefData(void) GTimeAdjust = 0; GClockSeq = 0; + return 0; } @@ -1175,93 +1218,7 @@ 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 +#endif // 0 #undef HI_WORD #undef RAND_MASK @@ -1291,7 +1248,5 @@ static OSErr GetEthernetAddr(uuid_address_t *addr) { #undef UUID_C_100NS_PER_USEC #undef UADD_UVLW_2_UVLW #undef UADD_UW_2_UVLW -#undef IFR_NEXT - -#endif +#endif // __WIN32__ diff --git a/CharacterSets/CFCharacterSetBitmaps.bitmap b/CharacterSets/CFCharacterSetBitmaps.bitmap index fc7f13aaf33f304ac8525adf537ea5fcf8868e92..e57ddcd21c5521c5f655a4cdf6b6d37f19796954 100644 GIT binary patch delta 79 zcmX?hQ1sYAQDzeZ!;Q>R%o8v4Pwr!6o9wEnHu(;tz%*5snC%x9F#5>=Mdg||D70@- hU<6_&AZFgaL4if|2;-0C$DXh_F#gznT!r<&H2{)|9&i8v delta 90 zcmX?hQ1sYAQDzeZhKD+czk%h#q15u>9B)76--;+mEZT{c zNpt>*36hf=4MZkyG4KO%Cm*m`$;x&ilc!7Q0lCK{rU_3>5Zs)r!_6}JnTydR eSFXuh4Ez{bCI;$mHZXk1%gn{awOPcTg$)22+!XNu diff --git a/CharacterSets/CFUnicodeData-B.mapping b/CharacterSets/CFUnicodeData-B.mapping index 919f01c8b870a3e7f8358008da4aa2be805396d6..99f30662f04e10a37dd43e1b3adfb93da45b76e7 100644 GIT binary patch delta 17 YcmZ4Tn`Oyw7G@Iz!$#(<%!~_d0Y2FU`Tzg` delta 17 YcmZ4Tn`Oyw7G@IzhDPSC%!~_d0X #include "CFStorage.h" -#include "CFUtilities.h" +#include "CFUtilitiesPriv.h" #include "CFInternal.h" #include @@ -52,7 +50,7 @@ struct __CFArrayFixedMutable { }; enum { - __CF_MAX_BUCKETS_PER_DEQUE = 65534 + __CF_MAX_BUCKETS_PER_DEQUE = 262140 }; CF_INLINE CFIndex __CFArrayDequeRoundUpCapacity(CFIndex capacity) { @@ -61,8 +59,9 @@ CF_INLINE CFIndex __CFArrayDequeRoundUpCapacity(CFIndex capacity) { } struct __CFArrayDeque { - uint16_t _leftIdx; - uint16_t _capacity; + uint32_t _leftIdx; + uint32_t _capacity; + int32_t _bias; /* struct __CFArrayBucket buckets follow here */ }; @@ -72,6 +71,16 @@ struct __CFArrayMutable { void *_store; /* can be NULL when MutableDeque */ }; +/* convenience for debugging. */ +struct __CFArray { + CFRuntimeBase _base; + CFIndex _count; /* number of objects */ + union { + CFIndex _capacity; /* maximum number of objects */ + void *_store; /* can be NULL when MutableDeque */ + }; +}; + /* Flag bits */ enum { /* Bits 0-1 */ __kCFArrayImmutable = 0, @@ -86,6 +95,17 @@ enum { /* Bits 2-3 */ __kCFArrayHasCustomCallBacks = 3 /* callbacks are at end of header */ }; +/* Bits 4-5 are used by GC */ + +static bool isStrongMemory(CFTypeRef collection) { + return ! __CFBitfieldGetValue(((const CFRuntimeBase *)collection)->_info, 4, 4); +} + +static bool needsRestore(CFTypeRef collection) { + return __CFBitfieldGetValue(((const CFRuntimeBase *)collection)->_info, 5, 5); +} + + CF_INLINE CFIndex __CFArrayGetType(CFArrayRef array) { return __CFBitfieldGetValue(((const CFRuntimeBase *)array)->_info, 1, 0); } @@ -119,7 +139,7 @@ CF_INLINE void __CFArraySetCount(CFArrayRef array, CFIndex 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. */ + * Returns the bucket holding the left-most real value in the latter case. */ CF_INLINE struct __CFArrayBucket *__CFArrayGetBucketsPtr(CFArrayRef array) { switch (__CFArrayGetType(array)) { case __kCFArrayImmutable: @@ -197,6 +217,7 @@ struct _releaseContext { static void __CFArrayStorageRelease(const void *itemptr, void *context) { struct _releaseContext *rc = (struct _releaseContext *)context; INVOKE_CALLBACK2(rc->release, rc->allocator, *(const void **)itemptr); + *(const void **)itemptr = NULL; // GC: clear item to break strong reference. } static void __CFArrayReleaseValues(CFArrayRef array, CFRange range, bool releaseStorageIfPossible) { @@ -211,21 +232,30 @@ static void __CFArrayReleaseValues(CFArrayRef array, CFRange range, bool release allocator = __CFGetAllocator(array); for (idx = 0; idx < range.length; idx++) { INVOKE_CALLBACK2(cb->release, allocator, buckets[idx + range.location]._item); + buckets[idx + range.location]._item = NULL; // GC: break strong reference. } } break; case __kCFArrayMutableDeque: { struct __CFArrayDeque *deque = ((struct __CFArrayMutable *)array)->_store; - if (NULL != cb->release && 0 < range.length && NULL != deque) { + if (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 (NULL != cb->release) { + allocator = __CFGetAllocator(array); + for (idx = 0; idx < range.length; idx++) { + INVOKE_CALLBACK2(cb->release, allocator, buckets[idx + range.location]._item); + buckets[idx + range.location]._item = NULL; // GC: break strong reference. + } + } else { + for (idx = 0; idx < range.length; idx++) { + buckets[idx + range.location]._item = NULL; // GC: break strong reference. + } } } if (releaseStorageIfPossible && 0 == range.location && __CFArrayGetCount(array) == range.length) { allocator = __CFGetAllocator(array); - if (NULL != deque) CFAllocatorDeallocate(allocator, deque); + if (NULL != deque) _CFAllocatorDeallocateGC(allocator, deque); + ((struct __CFArrayMutable *)array)->_count = 0; // GC: _count == 0 ==> _store == NULL. ((struct __CFArrayMutable *)array)->_store = NULL; } break; @@ -241,6 +271,7 @@ static void __CFArrayReleaseValues(CFArrayRef array, CFRange range, bool release } if (releaseStorageIfPossible && 0 == range.location && __CFArrayGetCount(array) == range.length) { CFRelease(store); + ((struct __CFArrayMutable *)array)->_count = 0; // GC: _count == 0 ==> _store == NULL. ((struct __CFArrayMutable *)array)->_store = NULL; __CFBitfieldSetValue(((CFRuntimeBase *)array)->_info, 1, 0, __kCFArrayMutableDeque); } @@ -325,13 +356,21 @@ static CFStringRef __CFArrayCopyDescription(CFTypeRef cf) { static void __CFArrayDeallocate(CFTypeRef cf) { CFArrayRef array = (CFArrayRef)cf; + // Under GC, keep contents alive when we know we can, either standard callbacks or NULL + // if (__CFBitfieldGetValue(cf->info, 5, 4)) return; // bits only ever set under GC + CFAllocatorRef allocator = __CFGetAllocator(array); + if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { + const CFArrayCallBacks *cb = __CFArrayGetCallBacks(array); + if (cb->retain == NULL && cb->release == NULL) + return; // XXX_PCB keep array intact during finalization. + } __CFArrayReleaseValues(array, CFRangeMake(0, __CFArrayGetCount(array)), true); } static CFTypeID __kCFArrayTypeID = _kCFRuntimeNotATypeID; static const CFRuntimeClass __CFArrayClass = { - 0, + _kCFRuntimeScannedObject, "CFArray", NULL, // init NULL, // copy @@ -353,7 +392,22 @@ CFTypeID CFArrayGetTypeID(void) { static CFArrayRef __CFArrayInit(CFAllocatorRef allocator, UInt32 flags, CFIndex capacity, const CFArrayCallBacks *callBacks) { struct __CFArrayImmutable *memory; UInt32 size; + CFArrayCallBacks nonRetainingCallbacks; __CFBitfieldSetValue(flags, 31, 2, 0); + if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { + if (!callBacks || (callBacks->retain == NULL && callBacks->release == NULL)) { + __CFBitfieldSetValue(flags, 4, 4, 1); // setWeak + } + else { + if (callBacks->retain == __CFTypeCollectionRetain && callBacks->release == __CFTypeCollectionRelease) { + nonRetainingCallbacks = *callBacks; + nonRetainingCallbacks.retain = NULL; + nonRetainingCallbacks.release = NULL; + callBacks = &nonRetainingCallbacks; + __CFBitfieldSetValue(flags, 5, 5, 1); // setNeedsRestore + } + } + } if (__CFArrayCallBacksMatchNull(callBacks)) { __CFBitfieldSetValue(flags, 3, 2, __kCFArrayHasNullCallBacks); } else if (__CFArrayCallBacksMatchCFType(callBacks)) { @@ -379,9 +433,15 @@ static CFArrayRef __CFArrayInit(CFAllocatorRef allocator, UInt32 flags, CFIndex __CFArraySetCount((CFArrayRef)memory, 0); switch (__CFBitfieldGetValue(flags, 1, 0)) { case __kCFArrayImmutable: + if (!isStrongMemory(memory)) { // if weak, don't scan + auto_zone_set_layout_type(__CFCollectableZone, memory, AUTO_OBJECT_UNSCANNED); + } if (__CFOASafe) __CFSetLastAllocationEventName(memory, "CFArray (immutable)"); break; case __kCFArrayFixedMutable: + if (!isStrongMemory(memory)) { // if weak, don't scan + auto_zone_set_layout_type(__CFCollectableZone, memory, AUTO_OBJECT_UNSCANNED); + } if (__CFOASafe) __CFSetLastAllocationEventName(memory, "CFArray (mutable-fixed)"); ((struct __CFArrayFixedMutable *)memory)->_capacity = capacity; break; @@ -392,8 +452,8 @@ static CFArrayRef __CFArrayInit(CFAllocatorRef allocator, UInt32 flags, CFIndex break; } if (__kCFArrayHasCustomCallBacks == __CFBitfieldGetValue(flags, 3, 2)) { - const CFArrayCallBacks *cb = __CFArrayGetCallBacks((CFArrayRef)memory); - *(CFArrayCallBacks *)cb = *callBacks; + CFArrayCallBacks *cb = (CFArrayCallBacks *)__CFArrayGetCallBacks((CFArrayRef)memory); + *cb = *callBacks; FAULT_CALLBACK((void **)&(cb->retain)); FAULT_CALLBACK((void **)&(cb->release)); FAULT_CALLBACK((void **)&(cb->copyDescription)); @@ -406,16 +466,20 @@ CFArrayRef CFArrayCreate(CFAllocatorRef allocator, const void **values, CFIndex CFArrayRef result; const CFArrayCallBacks *cb; struct __CFArrayBucket *buckets; + CFAllocatorRef bucketsAllocator; + void* bucketsBase; CFIndex idx; CFAssert2(0 <= numValues, __kCFLogAssertion, "%s(): numValues (%d) cannot be less than zero", __PRETTY_FUNCTION__, numValues); result = __CFArrayInit(allocator, __kCFArrayImmutable, numValues, callBacks); cb = __CFArrayGetCallBacks(result); buckets = __CFArrayGetBucketsPtr(result); + bucketsAllocator = isStrongMemory(result) ? allocator : kCFAllocatorNull; + bucketsBase = CF_IS_COLLECTABLE_ALLOCATOR(bucketsAllocator) ? auto_zone_base_pointer(__CFCollectableZone, buckets) : NULL; for (idx = 0; idx < numValues; idx++) { if (NULL != cb->retain) { - buckets->_item = (void *)INVOKE_CALLBACK2(cb->retain, allocator, *values); + CF_WRITE_BARRIER_BASE_ASSIGN(bucketsAllocator, bucketsBase, buckets->_item, (void *)INVOKE_CALLBACK2(cb->retain, allocator, *values)); } else { - buckets->_item = *values; + CF_WRITE_BARRIER_BASE_ASSIGN(bucketsAllocator, bucketsBase, buckets->_item, *values); } values++; buckets++; @@ -429,17 +493,20 @@ CFMutableArrayRef CFArrayCreateMutable(CFAllocatorRef allocator, CFIndex capacit return (CFMutableArrayRef)__CFArrayInit(allocator, (0 == capacity) ? __kCFArrayMutableDeque : __kCFArrayFixedMutable, capacity, callBacks); } +// This creates an array which is for CFTypes or NSObjects, with an ownership transfer -- +// the array does not take a retain, and the caller does not need to release the inserted objects. +// The incoming objects must also be collectable if allocated out of a collectable allocator. CFArrayRef _CFArrayCreate_ex(CFAllocatorRef allocator, 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)); + CF_WRITE_BARRIER_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; + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, result, ((struct __CFArrayMutable *)result)->_store, store); CFStorageInsertValues(store, CFRangeMake(0, numValues)); CFStorageReplaceValues(store, CFRangeMake(0, numValues), values); __CFBitfieldSetValue(((CFRuntimeBase *)result)->_info, 1, 0, __kCFArrayMutableStore); @@ -448,13 +515,14 @@ CFArrayRef _CFArrayCreate_ex(CFAllocatorRef allocator, bool mutable, const void struct __CFArrayBucket *raw_buckets; CFIndex capacity = __CFArrayDequeRoundUpCapacity(numValues); CFIndex size = sizeof(struct __CFArrayDeque) + capacity * sizeof(struct __CFArrayBucket); - deque = CFAllocatorAllocate(allocator, size, 0); + deque = _CFAllocatorAllocateGC(allocator, size, isStrongMemory(result) ? AUTO_MEMORY_SCANNED : AUTO_MEMORY_UNSCANNED); if (__CFOASafe) __CFSetLastAllocationEventName(deque, "CFArray (store-deque)"); deque->_leftIdx = (capacity - numValues) / 2; deque->_capacity = capacity; - ((struct __CFArrayMutable *)result)->_store = deque; + deque->_bias = 0; + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, result, ((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)); + CF_WRITE_BARRIER_MEMMOVE(raw_buckets + deque->_leftIdx + 0, values, numValues * sizeof(struct __CFArrayBucket)); __CFBitfieldSetValue(((CFRuntimeBase *)result)->_info, 1, 0, __kCFArrayMutableDeque); } } @@ -465,18 +533,36 @@ CFArrayRef _CFArrayCreate_ex(CFAllocatorRef allocator, bool mutable, const void CFArrayRef CFArrayCreateCopy(CFAllocatorRef allocator, CFArrayRef array) { CFArrayRef result; const CFArrayCallBacks *cb; + CFArrayCallBacks patchedCB; struct __CFArrayBucket *buckets; + CFAllocatorRef bucketsAllocator; + void* bucketsBase; CFIndex numValues = CFArrayGetCount(array); CFIndex idx; - cb = CF_IS_OBJC(__kCFArrayTypeID, array) ? &kCFTypeArrayCallBacks : __CFArrayGetCallBacks(array); + if (CF_IS_OBJC(__kCFArrayTypeID, array)) { + cb = &kCFTypeArrayCallBacks; + } else { + cb = __CFArrayGetCallBacks(array); + if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { + if (needsRestore(array)) { + patchedCB = *cb; // copy + cb = &patchedCB; // reset to copy + patchedCB.retain = __CFTypeCollectionRetain; + patchedCB.release = __CFTypeCollectionRelease; + } + } + } result = __CFArrayInit(allocator, __kCFArrayImmutable, numValues, cb); + cb = __CFArrayGetCallBacks(result); // GC: use the new array's callbacks so we don't leak. buckets = __CFArrayGetBucketsPtr(result); + bucketsAllocator = isStrongMemory(result) ? allocator : kCFAllocatorNull; + bucketsBase = CF_IS_COLLECTABLE_ALLOCATOR(bucketsAllocator) ? auto_zone_base_pointer(__CFCollectableZone, buckets) : NULL; for (idx = 0; idx < numValues; idx++) { const void *value = CFArrayGetValueAtIndex(array, idx); if (NULL != cb->retain) { value = (void *)INVOKE_CALLBACK2(cb->retain, allocator, value); } - buckets->_item = value; + CF_WRITE_BARRIER_BASE_ASSIGN(bucketsAllocator, bucketsBase, buckets->_item, value); buckets++; } __CFArraySetCount(result, numValues); @@ -488,8 +574,22 @@ CFMutableArrayRef CFArrayCreateMutableCopy(CFAllocatorRef allocator, CFIndex cap const CFArrayCallBacks *cb; CFIndex idx, numValues = CFArrayGetCount(array); UInt32 flags; + CFArrayCallBacks patchedCB; 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); + if (CF_IS_OBJC(__kCFArrayTypeID, array)) { + cb = &kCFTypeArrayCallBacks; + } + else { + cb = __CFArrayGetCallBacks(array); + if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { + if (needsRestore(array)) { + patchedCB = *cb; // copy + cb = &patchedCB; // reset to copy + patchedCB.retain = __CFTypeCollectionRetain; + patchedCB.release = __CFTypeCollectionRelease; + } + } + } flags = (0 == capacity) ? __kCFArrayMutableDeque : __kCFArrayFixedMutable; result = (CFMutableArrayRef)__CFArrayInit(allocator, flags, capacity, cb); if (0 == capacity) _CFArraySetCapacity(result, numValues); @@ -526,20 +626,27 @@ CFIndex CFArrayGetCountOfValue(CFArrayRef array, CFRange range, const void *valu } 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))) { + if (value == item) { return true; } } + const CFArrayCallBacks *cb = __CFArrayGetCallBacks(array); + if (cb->equal) { + for (idx = 0; idx < range.length; idx++) { + const void *item = __CFArrayGetBucketAtIndex(array, range.location + idx)->_item; + if (INVOKE_CALLBACK2(cb->equal, value, item)) { + return true; + } + } + } return false; } @@ -569,7 +676,7 @@ void CFArrayGetValues(CFArrayRef array, CFRange range, const void **values) { case __kCFArrayImmutable: case __kCFArrayFixedMutable: case __kCFArrayMutableDeque: - memmove(values, __CFArrayGetBucketsPtr(array) + range.location, range.length * sizeof(struct __CFArrayBucket)); + CF_WRITE_BARRIER_MEMMOVE(values, __CFArrayGetBucketsPtr(array) + range.location, range.length * sizeof(struct __CFArrayBucket)); break; case __kCFArrayMutableStore: { CFStorageRef store = ((struct __CFArrayMutable *)array)->_store; @@ -647,11 +754,13 @@ void CFArraySetValueAtIndex(CFMutableArrayRef array, CFIndex idx, const void *va const void *old_value; const CFArrayCallBacks *cb = __CFArrayGetCallBacks(array); CFAllocatorRef allocator = __CFGetAllocator(array); + CFAllocatorRef bucketsAllocator = isStrongMemory(array) ? allocator : kCFAllocatorNull; + struct __CFArrayBucket *bucket = __CFArrayGetBucketAtIndex(array, idx); if (NULL != cb->retain) { value = (void *)INVOKE_CALLBACK2(cb->retain, allocator, value); } - old_value = __CFArrayGetBucketAtIndex(array, idx)->_item; - __CFArrayGetBucketAtIndex(array, idx)->_item = value; + old_value = bucket->_item; + CF_WRITE_BARRIER_ASSIGN(bucketsAllocator, bucket->_item, value); // GC: handles deque/CFStorage cases. if (NULL != cb->release) { INVOKE_CALLBACK2(cb->release, allocator, old_value); } @@ -668,14 +777,19 @@ void CFArrayInsertValueAtIndex(CFMutableArrayRef array, CFIndex idx, const void void CFArrayExchangeValuesAtIndices(CFMutableArrayRef array, CFIndex idx1, CFIndex idx2) { const void *tmp; + struct __CFArrayBucket *bucket1, *bucket2; + CFAllocatorRef bucketsAllocator; CF_OBJC_FUNCDISPATCH2(__kCFArrayTypeID, void, array, "exchange::", idx1, idx2); __CFGenericValidateType(array, __kCFArrayTypeID); CFAssert2(0 <= idx1 && idx1 < __CFArrayGetCount(array), __kCFLogAssertion, "%s(): index #1 (%d) out of bounds", __PRETTY_FUNCTION__, idx1); CFAssert2(0 <= idx2 && idx2 < __CFArrayGetCount(array), __kCFLogAssertion, "%s(): index #2 (%d) out of bounds", __PRETTY_FUNCTION__, idx2); CFAssert1(__CFArrayGetType(array) != __kCFArrayImmutable, __kCFLogAssertion, "%s(): array is immutable", __PRETTY_FUNCTION__); - tmp = __CFArrayGetBucketAtIndex(array, idx1)->_item; - __CFArrayGetBucketAtIndex(array, idx1)->_item = __CFArrayGetBucketAtIndex(array, idx2)->_item; - __CFArrayGetBucketAtIndex(array, idx2)->_item = tmp; + bucket1 = __CFArrayGetBucketAtIndex(array, idx1); + bucket2 = __CFArrayGetBucketAtIndex(array, idx2); + tmp = bucket1->_item; + bucketsAllocator = isStrongMemory(array) ? __CFGetAllocator(array) : kCFAllocatorNull; + CF_WRITE_BARRIER_ASSIGN(bucketsAllocator, bucket1->_item, bucket2->_item); + CF_WRITE_BARRIER_ASSIGN(bucketsAllocator, bucket2->_item, tmp); } void CFArrayRemoveValueAtIndex(CFMutableArrayRef array, CFIndex idx) { @@ -699,12 +813,13 @@ static void __CFArrayConvertDequeToStore(CFMutableArrayRef array) { 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 *)); + CFAllocatorRef allocator = __CFGetAllocator(array); + store = CFStorageCreate(allocator, sizeof(const void *)); if (__CFOASafe) __CFSetLastAllocationEventName(store, "CFArray (store-storage)"); - ((struct __CFArrayMutable *)array)->_store = store; + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, array, ((struct __CFArrayMutable *)array)->_store, store); CFStorageInsertValues(store, CFRangeMake(0, count)); CFStorageReplaceValues(store, CFRangeMake(0, count), raw_buckets + deque->_leftIdx); - CFAllocatorDeallocate(__CFGetAllocator(array), deque); + _CFAllocatorDeallocateGC(__CFGetAllocator(array), deque); __CFBitfieldSetValue(((CFRuntimeBase *)array)->_info, 1, 0, __kCFArrayMutableStore); } @@ -716,11 +831,13 @@ static void __CFArrayConvertStoreToDeque(CFMutableArrayRef array) { // 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); + CFAllocatorRef allocator = __CFGetAllocator(array); + deque = _CFAllocatorAllocateGC(allocator, size, isStrongMemory(array) ? AUTO_MEMORY_SCANNED : AUTO_MEMORY_UNSCANNED); if (__CFOASafe) __CFSetLastAllocationEventName(deque, "CFArray (store-deque)"); deque->_leftIdx = (capacity - count) / 2; deque->_capacity = capacity; - ((struct __CFArrayMutable *)array)->_store = deque; + deque->_bias = 0; + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, array, ((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); @@ -746,12 +863,14 @@ static void __CFArrayRepositionDequeRegions(CFMutableArrayRef array, CFRange ran 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 wiggle = deque->_capacity >> 17; + if (wiggle < 4) wiggle = 4; + if (deque->_capacity < futureCnt || (cnt < futureCnt && L + R < wiggle)) { + // must be inserting or space is tight, reallocate and re-center everything + CFIndex capacity = __CFArrayDequeRoundUpCapacity(futureCnt + wiggle); CFIndex size = sizeof(struct __CFArrayDeque) + capacity * sizeof(struct __CFArrayBucket); CFAllocatorRef allocator = __CFGetAllocator(array); - struct __CFArrayDeque *newDeque = CFAllocatorAllocate(allocator, size, 0); + struct __CFArrayDeque *newDeque = _CFAllocatorAllocateGC(allocator, size, isStrongMemory(array) ? AUTO_MEMORY_SCANNED : AUTO_MEMORY_UNSCANNED); if (__CFOASafe) __CFSetLastAllocationEventName(newDeque, "CFArray (store-deque)"); struct __CFArrayBucket *newBuckets = (struct __CFArrayBucket *)((uint8_t *)newDeque + sizeof(struct __CFArrayDeque)); CFIndex oldL = L; @@ -760,10 +879,10 @@ static void __CFArrayRepositionDequeRegions(CFMutableArrayRef array, CFRange ran 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; + if (0 < A) CF_WRITE_BARRIER_MEMMOVE(newBuckets + newL, buckets + oldL, A * sizeof(struct __CFArrayBucket)); + if (0 < C) CF_WRITE_BARRIER_MEMMOVE(newBuckets + newC0, buckets + oldC0, C * sizeof(struct __CFArrayBucket)); + if (deque) _CFAllocatorDeallocateGC(allocator, deque); + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, array, ((struct __CFArrayMutable *)array)->_store, newDeque); return; } @@ -772,14 +891,18 @@ static void __CFArrayRepositionDequeRegions(CFMutableArrayRef array, CFRange ran // 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)); + if (0 < C) CF_WRITE_BARRIER_MEMMOVE(buckets + newC0, buckets + oldC0, C * sizeof(struct __CFArrayBucket)); + // GrP GC: zero-out newly exposed space on the right, if any + if (oldC0 > newC0) bzero(buckets + newC0 + C, (oldC0 - newC0) * sizeof(struct __CFArrayBucket)); } else if ((numNewElems < 0) || (numNewElems <= L && A <= C)) { // move A // deleting: A is smaller or equal (covers remaining delete cases) // inserting: A is smaller and L has room CFIndex oldL = L; CFIndex newL = L - numNewElems; deque->_leftIdx = newL; - if (0 < A) memmove(buckets + newL, buckets + oldL, A * sizeof(struct __CFArrayBucket)); + if (0 < A) CF_WRITE_BARRIER_MEMMOVE(buckets + newL, buckets + oldL, A * sizeof(struct __CFArrayBucket)); + // GrP GC: zero-out newly exposed space on the left, if any + if (newL > oldL) bzero(buckets + oldL, (newL - oldL) * sizeof(struct __CFArrayBucket)); } else { // now, must be inserting, and either: // A<=C, but L doesn't have room (R might have, but don't care) @@ -787,15 +910,26 @@ static void __CFArrayRepositionDequeRegions(CFMutableArrayRef array, CFRange ran // re-center everything CFIndex oldL = L; CFIndex newL = (L + R - numNewElems) / 2; + CFIndex oldBias = deque->_bias; + deque->_bias = (newL < oldL) ? -1 : 1; + if (oldBias < 0) { + newL = newL - newL / 2; + } else if (0 < oldBias) { + newL = newL + newL / 2; + } CFIndex oldC0 = oldL + A + B; CFIndex newC0 = newL + A + newCount; deque->_leftIdx = newL; if (newL < oldL) { - if (0 < A) memmove(buckets + newL, buckets + oldL, A * sizeof(struct __CFArrayBucket)); - if (0 < C) memmove(buckets + newC0, buckets + oldC0, C * sizeof(struct __CFArrayBucket)); + if (0 < A) CF_WRITE_BARRIER_MEMMOVE(buckets + newL, buckets + oldL, A * sizeof(struct __CFArrayBucket)); + if (0 < C) CF_WRITE_BARRIER_MEMMOVE(buckets + newC0, buckets + oldC0, C * sizeof(struct __CFArrayBucket)); + // GrP GC: zero-out newly exposed space on the right, if any + if (oldC0 > newC0) bzero(buckets + newC0 + C, (oldC0 - newC0) * 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)); + if (0 < C) CF_WRITE_BARRIER_MEMMOVE(buckets + newC0, buckets + oldC0, C * sizeof(struct __CFArrayBucket)); + if (0 < A) CF_WRITE_BARRIER_MEMMOVE(buckets + newL, buckets + oldL, A * sizeof(struct __CFArrayBucket)); + // GrP GC: zero-out newly exposed space on the left, if any + if (newL > oldL) bzero(buckets + oldL, (newL - oldL) * sizeof(struct __CFArrayBucket)); } } } @@ -816,23 +950,21 @@ void _CFArraySetCapacity(CFMutableArrayRef array, CFIndex cap) { struct __CFArrayDeque *deque = ((struct __CFArrayMutable *)array)->_store; CFIndex capacity = __CFArrayDequeRoundUpCapacity(cap); CFIndex size = sizeof(struct __CFArrayDeque) + capacity * sizeof(struct __CFArrayBucket); + CFAllocatorRef allocator = __CFGetAllocator(array); if (NULL == deque) { - deque = CFAllocatorAllocate(__CFGetAllocator(array), size, 0); + deque = _CFAllocatorAllocateGC(allocator, size, isStrongMemory(array) ? AUTO_MEMORY_SCANNED : AUTO_MEMORY_UNSCANNED); if (__CFOASafe) __CFSetLastAllocationEventName(deque, "CFArray (store-deque)"); deque->_leftIdx = capacity / 2; } else { - deque = CFAllocatorReallocate(__CFGetAllocator(array), deque, size, 0); + deque = _CFAllocatorReallocateGC(allocator, deque, size, isStrongMemory(array) ? AUTO_MEMORY_SCANNED : AUTO_MEMORY_UNSCANNED); if (__CFOASafe) __CFSetLastAllocationEventName(deque, "CFArray (store-deque)"); } deque->_capacity = capacity; - ((struct __CFArrayMutable *)array)->_store = deque; + deque->_bias = 0; + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, array, ((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); @@ -860,7 +992,7 @@ void _CFArrayReplaceValues(CFMutableArrayRef array, CFRange range, const void ** 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); + newv = (newCount <= 256) ? buffer : CFAllocatorAllocate(allocator, newCount * sizeof(void *), 0); // GC OK if (newv != buffer && __CFOASafe) __CFSetLastAllocationEventName(newv, "CFArray (temp)"); for (idx = 0; idx < newCount; idx++) { newv[idx] = (void *)INVOKE_CALLBACK2(cb->retain, allocator, (void *)newValues[idx]); @@ -886,13 +1018,13 @@ void _CFArrayReplaceValues(CFMutableArrayRef array, CFRange range, const void ** } 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)); + CF_WRITE_BARRIER_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 *)); + CF_WRITE_BARRIER_MEMMOVE(buckets + range.location, newv, newCount * sizeof(void *)); } __CFArraySetCount(array, futureCnt); - if (newv != buffer && newv != newValues) CFAllocatorDeallocate(allocator, newv); + if (newv != buffer && newv != newValues) CFAllocatorDeallocate(allocator, newv); // GC OK return; } if (0 < range.length) { @@ -900,32 +1032,34 @@ void _CFArrayReplaceValues(CFMutableArrayRef array, CFRange range, const void ** } // region B elements are now "dead" if (__kCFArrayMutableStore == __CFArrayGetType(array)) { - CFStorageRef store = ((struct __CFArrayMutable *)array)->_store; + 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 (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 (! isStrongMemory(array)) _CFStorageSetWeak(store); if (__CFOASafe) __CFSetLastAllocationEventName(store, "CFArray (store-storage)"); - ((struct __CFArrayMutable *)array)->_store = store; - CFStorageInsertValues(store, CFRangeMake(0, newCount)); + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, array, ((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); + deque = _CFAllocatorAllocateGC(allocator, size, isStrongMemory(array) ? AUTO_MEMORY_SCANNED : AUTO_MEMORY_UNSCANNED); if (__CFOASafe) __CFSetLastAllocationEventName(deque, "CFArray (store-deque)"); deque->_leftIdx = (capacity - newCount) / 2; deque->_capacity = capacity; - ((struct __CFArrayMutable *)array)->_store = deque; + deque->_bias = 0; + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, array, ((struct __CFArrayMutable *)array)->_store, deque); } } else { // Deque // reposition regions A and C for new region B elements in gap @@ -950,10 +1084,11 @@ void _CFArrayReplaceValues(CFMutableArrayRef array, CFRange range, const void ** } else { // Deque struct __CFArrayDeque *deque = ((struct __CFArrayMutable *)array)->_store; struct __CFArrayBucket *raw_buckets = (struct __CFArrayBucket *)((uint8_t *)deque + sizeof(struct __CFArrayDeque)); + CFAllocatorRef bucketsAllocator = isStrongMemory(array) ? allocator : kCFAllocatorNull; if (newCount == 1) - *((const void **)raw_buckets + deque->_leftIdx + range.location) = newv[0]; + CF_WRITE_BARRIER_ASSIGN(bucketsAllocator, *((const void **)raw_buckets + deque->_leftIdx + range.location), newv[0]); else - memmove(raw_buckets + deque->_leftIdx + range.location, newv, newCount * sizeof(struct __CFArrayBucket)); + CF_WRITE_BARRIER_MEMMOVE(raw_buckets + deque->_leftIdx + range.location, newv, newCount * sizeof(struct __CFArrayBucket)); } } __CFArraySetCount(array, futureCnt); @@ -982,23 +1117,26 @@ void CFArraySortValues(CFMutableArrayRef array, CFRange range, CFComparatorFunct CFAssert1(NULL != comparator, __kCFLogAssertion, "%s(): pointer to comparator function may not be NULL", __PRETTY_FUNCTION__); if (1 < range.length) { struct _acompareContext ctx; + struct __CFArrayBucket *bucket; 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); + bucket = __CFArrayGetBucketsPtr(array) + range.location; + if (CF_USING_COLLECTABLE_MEMORY && isStrongMemory(array)) __CFObjCWriteBarrierRange(bucket, range.length * sizeof(void *)); + CFQSortArray(bucket, 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); + values = (range.length <= 256) ? buffer : CFAllocatorAllocate(allocator, range.length * sizeof(void *), 0); // GC OK if (values != buffer && __CFOASafe) __CFSetLastAllocationEventName(values, "CFArray (temp)"); CFStorageGetValues(store, range, values); CFQSortArray(values, range.length, sizeof(void *), (CFComparatorFunction)__CFArrayCompareValues, &ctx); CFStorageReplaceValues(store, range, values); - if (values != buffer) CFAllocatorDeallocate(allocator, values); + if (values != buffer) CFAllocatorDeallocate(allocator, values); // GC OK break; } } diff --git a/Collections.subproj/CFArray.h b/Collections.subproj/CFArray.h index 9dcf528..7c507e7 100644 --- a/Collections.subproj/CFArray.h +++ b/Collections.subproj/CFArray.h @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -23,7 +21,7 @@ * @APPLE_LICENSE_HEADER_END@ */ /* CFArray.h - Copyright (c) 1998-2003, Apple, Inc. All rights reserved. + Copyright (c) 1998-2005, Apple, Inc. All rights reserved. */ /*! diff --git a/Collections.subproj/CFBag.c b/Collections.subproj/CFBag.c index 26ed5c0..9aaf404 100644 --- a/Collections.subproj/CFBag.c +++ b/Collections.subproj/CFBag.c @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -75,6 +73,12 @@ enum { /* Bits 3-2 */ __kCFBagHasCustomCallBacks = 3 /* callbacks are at end of header */ }; +enum { /* Bit 4 */ + __kCFCollectionIsWeak = 0, + __kCFCollectionIsStrong = 1, +}; + + struct __CFBagBucket { const void *_key; CFIndex _count; @@ -106,6 +110,16 @@ CF_INLINE bool __CFBagBucketIsOccupied(CFBagRef bag, const struct __CFBagBucket /* 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 */ +/* Bits 4-5 are used by GC */ + +static bool isStrongMemory(CFTypeRef collection) { + return ! __CFBitfieldGetValue(((const CFRuntimeBase *)collection)->_info, 4, 4); +} + +static bool needsRestore(CFTypeRef collection) { + return __CFBitfieldGetValue(((const CFRuntimeBase *)collection)->_info, 5, 5); +} + CF_INLINE CFIndex __CFBagGetType(CFBagRef bag) { return __CFBitfieldGetValue(((const CFRuntimeBase *)bag)->_info, 1, 0); @@ -166,7 +180,7 @@ static void __CFBagFindBuckets1(CFBagRef bag, const void *key, struct __CFBagBuc 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))) { + } else if (currentBucket->_key == key || (cb->equal && INVOKE_CALLBACK3((Boolean (*)(const void *, const void *, void *))cb->equal, currentBucket->_key, key, bag->_context))) { *match = currentBucket; return; } @@ -191,9 +205,9 @@ static void __CFBagFindBuckets2(CFBagRef bag, const void *key, struct __CFBagBuc 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)))) { + } else if (!*match && (currentBucket->_key == key || (cb->equal && INVOKE_CALLBACK3((Boolean (*)(const void *, const void *, void *))cb->equal, currentBucket->_key, key, bag->_context)))) { *match = currentBucket; - if (*nomatch) return; + return; } probe = (probe + probeskip) % bag->_bucketsNum; if (start == probe) return; @@ -313,19 +327,24 @@ static CFStringRef __CFBagCopyDescription(CFTypeRef cf) { static void __CFBagDeallocate(CFTypeRef cf) { CFMutableBagRef bag = (CFMutableBagRef)cf; CFAllocatorRef allocator = CFGetAllocator(bag); + if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { + const CFBagCallBacks *cb = __CFBagGetCallBacks(bag); + if (cb->retain == NULL && cb->release == NULL) + return; // XXX_PCB keep bag intact during finalization. + } if (__CFBagGetType(bag) == __kCFBagImmutable) { __CFBitfieldSetValue(((CFRuntimeBase *)bag)->_info, 1, 0, __kCFBagFixedMutable); } CFBagRemoveAllValues(bag); if (__CFBagGetType(bag) == __kCFBagMutable && bag->_buckets) { - CFAllocatorDeallocate(allocator, bag->_buckets); + _CFAllocatorDeallocateGC(allocator, bag->_buckets); } } static CFTypeID __kCFBagTypeID = _kCFRuntimeNotATypeID; static const CFRuntimeClass __CFBagClass = { - 0, + _kCFRuntimeScannedObject, "CFBag", NULL, // init NULL, // copy @@ -348,7 +367,22 @@ static CFBagRef __CFBagInit(CFAllocatorRef allocator, UInt32 flags, CFIndex capa struct __CFBag *memory; UInt32 size; CFIndex idx; + CFBagCallBacks nonRetainingCallbacks; __CFBitfieldSetValue(flags, 31, 2, 0); + if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { + if (!callBacks || (callBacks->retain == NULL && callBacks->release == NULL)) { + __CFBitfieldSetValue(flags, 4, 4, 1); // setWeak + } + else { + if (callBacks->retain == __CFTypeCollectionRetain && callBacks->release == __CFTypeCollectionRelease) { + nonRetainingCallbacks = *callBacks; + nonRetainingCallbacks.retain = NULL; + nonRetainingCallbacks.release = NULL; + callBacks = &nonRetainingCallbacks; + __CFBitfieldSetValue(flags, 5, 5, 1); // setNeedsRestore + } + } + } if (__CFBagCallBacksMatchNull(callBacks)) { __CFBitfieldSetValue(flags, 3, 2, __kCFBagHasNullCallBacks); } else if (__CFBagCallBacksMatchCFType(callBacks)) { @@ -377,6 +411,9 @@ static CFBagRef __CFBagInit(CFAllocatorRef allocator, UInt32 flags, CFIndex capa memory->_context = NULL; switch (__CFBitfieldGetValue(flags, 1, 0)) { case __kCFBagImmutable: + if (!isStrongMemory(memory)) { // if weak, don't scan + auto_zone_set_layout_type(__CFCollectableZone, memory, AUTO_OBJECT_UNSCANNED); + } if (__CFOASafe) __CFSetLastAllocationEventName(memory, "CFBag (immutable)"); memory->_capacity = capacity; /* Don't round up capacity */ memory->_bucketsNum = __CFBagNumBucketsForCapacity(memory->_capacity); @@ -386,6 +423,9 @@ static CFBagRef __CFBagInit(CFAllocatorRef allocator, UInt32 flags, CFIndex capa } break; case __kCFBagFixedMutable: + if (!isStrongMemory(memory)) { // if weak, don't scan + auto_zone_set_layout_type(__CFCollectableZone, memory, AUTO_OBJECT_UNSCANNED); + } if (__CFOASafe) __CFSetLastAllocationEventName(memory, "CFBag (mutable-fixed)"); memory->_capacity = capacity; /* Don't round up capacity */ memory->_bucketsNum = __CFBagNumBucketsForCapacity(memory->_capacity); @@ -440,12 +480,26 @@ CFBagRef CFBagCreateCopy(CFAllocatorRef allocator, CFBagRef bag) { const CFBagCallBacks *cb; CFIndex numValues = CFBagGetCount(bag); const void **list, *buffer[256]; - list = (numValues <= 256) ? buffer : CFAllocatorAllocate(allocator, numValues * sizeof(void *), 0); + list = (numValues <= 256) ? buffer : CFAllocatorAllocate(allocator, numValues * sizeof(void *), 0); // XXX_PCB GC OK if (list != buffer && __CFOASafe) __CFSetLastAllocationEventName(list, "CFBag (temp)"); CFBagGetValues(bag, list); - cb = CF_IS_OBJC(__kCFBagTypeID, bag) ? &kCFTypeBagCallBacks : __CFBagGetCallBacks(bag); + CFBagCallBacks patchedCB; + if (CF_IS_OBJC(__kCFBagTypeID, bag)) { + cb = &kCFTypeBagCallBacks; + } + else { + cb = __CFBagGetCallBacks(bag); + if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { + if (needsRestore(bag)) { + patchedCB = *cb; // copy + cb = &patchedCB; // reset to copy + patchedCB.retain = __CFTypeCollectionRetain; + patchedCB.release = __CFTypeCollectionRelease; + } + } + } result = CFBagCreate(allocator, list, numValues, cb); - if (list != buffer) CFAllocatorDeallocate(allocator, list); + if (list != buffer) CFAllocatorDeallocate(allocator, list); // XXX_PCB GC OK return result; } @@ -455,21 +509,37 @@ CFMutableBagRef CFBagCreateMutableCopy(CFAllocatorRef allocator, CFIndex capacit 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); + list = (numValues <= 256) ? buffer : CFAllocatorAllocate(allocator, numValues * sizeof(void *), 0); // XXX_PCB GC OK if (list != buffer && __CFOASafe) __CFSetLastAllocationEventName(list, "CFBag (temp)"); CFBagGetValues(bag, list); - cb = CF_IS_OBJC(__kCFBagTypeID, bag) ? &kCFTypeBagCallBacks : __CFBagGetCallBacks(bag); + CFBagCallBacks patchedCB; + if (CF_IS_OBJC(__kCFBagTypeID, bag)) { + cb = &kCFTypeBagCallBacks; + } + else { + cb = __CFBagGetCallBacks(bag); + if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { + if (needsRestore(bag)) { + patchedCB = *cb; // copy + cb = &patchedCB; // reset to copy + patchedCB.retain = __CFTypeCollectionRetain; + patchedCB.release = __CFTypeCollectionRelease; + } + } + } 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); + if (list != buffer) CFAllocatorDeallocate(allocator, list); // GC OK return result; } + void _CFBagSetContext(CFBagRef bag, void *context) { - ((struct __CFBag *)bag)->_context = context; + CFAllocatorRef allocator = CFGetAllocator(bag); + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, bag, bag->_context, context); } CFIndex CFBagGetCount(CFBagRef bag) { @@ -506,13 +576,17 @@ Boolean CFBagGetValueIfPresent(CFBagRef bag, const void *candidate, const void * __CFGenericValidateType(bag, __kCFBagTypeID); if (0 == bag->_count) return false; __CFBagFindBuckets1(bag, candidate, &match); - return (match ? ((value ? *value = match->_key : NULL), true) : false); + return (match ? ((value ? __CFObjCStrongAssign(match->_key, value) : NULL), true) : false); } void CFBagGetValues(CFBagRef bag, const void **values) { struct __CFBagBucket *buckets; CFIndex idx, cnt, nbuckets; __CFGenericValidateType(bag, __kCFBagTypeID); + if (CF_USING_COLLECTABLE_MEMORY) { + // GC: speculatively issue a write-barrier on the copied to buffers (3743553). + __CFObjCWriteBarrierRange(values, bag->_count * sizeof(void *)); + } buckets = bag->_buckets; nbuckets = bag->_bucketsNum; for (idx = 0; idx < nbuckets; idx++) { @@ -544,9 +618,11 @@ static void __CFBagGrow(CFMutableBagRef bag, CFIndex numNewValues) { struct __CFBagBucket *oldbuckets = bag->_buckets; CFIndex idx, oldnbuckets = bag->_bucketsNum; CFIndex oldCount = bag->_count; + CFAllocatorRef allocator = CFGetAllocator(bag); bag->_capacity = __CFBagRoundUpCapacity(oldCount + numNewValues); bag->_bucketsNum = __CFBagNumBucketsForCapacity(bag->_capacity); - bag->_buckets = CFAllocatorAllocate(CFGetAllocator(bag), bag->_bucketsNum * sizeof(struct __CFBagBucket), 0); + void *bucket = _CFAllocatorAllocateGC(allocator, bag->_bucketsNum * sizeof(struct __CFBagBucket), isStrongMemory(bag) ? AUTO_MEMORY_SCANNED : AUTO_MEMORY_UNSCANNED); + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, bag, bag->_buckets, bucket); if (__CFOASafe) __CFSetLastAllocationEventName(bag->_buckets, "CFBag (store)"); if (NULL == bag->_buckets) HALT; for (idx = bag->_bucketsNum; idx--;) { @@ -558,12 +634,14 @@ static void __CFBagGrow(CFMutableBagRef bag, CFIndex numNewValues) { 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; + if (nomatch) { + CF_WRITE_BARRIER_ASSIGN(allocator, 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); + _CFAllocatorDeallocateGC(CFGetAllocator(bag), oldbuckets); } // This function is for Foundation's benefit; no one else should use it. @@ -579,6 +657,7 @@ void _CFBagSetCapacity(CFMutableBagRef bag, CFIndex cap) { void CFBagAddValue(CFMutableBagRef bag, const void *value) { struct __CFBagBucket *match, *nomatch; + CFAllocatorRef allocator; const CFBagCallBacks *cb; const void *newValue; __CFGenericValidateType(bag, __kCFBagTypeID); @@ -599,9 +678,10 @@ void CFBagAddValue(CFMutableBagRef bag, const void *value) { if (match) { match->_count++; bag->_count++; } else { + allocator = CFGetAllocator(bag); cb = __CFBagGetCallBacks(bag); if (cb->retain) { - newValue = (void *)INVOKE_CALLBACK3(((const void *(*)(CFAllocatorRef, const void *, void *))cb->retain), CFGetAllocator(bag), value, bag->_context); + newValue = (void *)INVOKE_CALLBACK3(((const void *(*)(CFAllocatorRef, const void *, void *))cb->retain), allocator, value, bag->_context); } else { newValue = value; } @@ -611,7 +691,7 @@ void CFBagAddValue(CFMutableBagRef bag, const void *value) { if (bag->_deletedMarker == newValue) { __CFBagFindNewDeletedMarker(bag); } - nomatch->_key = newValue; + CF_WRITE_BARRIER_ASSIGN(allocator, nomatch->_key, newValue); nomatch->_count = 1; bag->_bucketsUsed++; bag->_count++; @@ -620,6 +700,7 @@ void CFBagAddValue(CFMutableBagRef bag, const void *value) { void CFBagReplaceValue(CFMutableBagRef bag, const void *value) { struct __CFBagBucket *match; + CFAllocatorRef allocator; const CFBagCallBacks *cb; const void *newValue; __CFGenericValidateType(bag, __kCFBagTypeID); @@ -634,14 +715,15 @@ void CFBagReplaceValue(CFMutableBagRef bag, const void *value) { if (0 == bag->_count) return; __CFBagFindBuckets1(bag, value, &match); if (!match) return; + allocator = CFGetAllocator(bag); cb = __CFBagGetCallBacks(bag); if (cb->retain) { - newValue = (void *)INVOKE_CALLBACK3(((const void *(*)(CFAllocatorRef, const void *, void *))cb->retain), CFGetAllocator(bag), value, bag->_context); + newValue = (void *)INVOKE_CALLBACK3(((const void *(*)(CFAllocatorRef, const void *, void *))cb->retain), allocator, value, bag->_context); } else { newValue = value; } if (cb->release) { - INVOKE_CALLBACK3(((void (*)(CFAllocatorRef, const void *, void *))cb->release), CFGetAllocator(bag), match->_key, bag->_context); + INVOKE_CALLBACK3(((void (*)(CFAllocatorRef, const void *, void *))cb->release), allocator, match->_key, bag->_context); match->_key = bag->_deletedMarker; } if (bag->_emptyMarker == newValue) { @@ -650,11 +732,12 @@ void CFBagReplaceValue(CFMutableBagRef bag, const void *value) { if (bag->_deletedMarker == newValue) { __CFBagFindNewDeletedMarker(bag); } - match->_key = newValue; + CF_WRITE_BARRIER_ASSIGN(allocator, match->_key, newValue); } void CFBagSetValue(CFMutableBagRef bag, const void *value) { struct __CFBagBucket *match, *nomatch; + CFAllocatorRef allocator; const CFBagCallBacks *cb; const void *newValue; __CFGenericValidateType(bag, __kCFBagTypeID); @@ -671,15 +754,16 @@ void CFBagSetValue(CFMutableBagRef bag, const void *value) { break; } __CFBagFindBuckets2(bag, value, &match, &nomatch); + allocator = CFGetAllocator(bag); cb = __CFBagGetCallBacks(bag); if (cb->retain) { - newValue = (void *)INVOKE_CALLBACK3(((const void *(*)(CFAllocatorRef, const void *, void *))cb->retain), CFGetAllocator(bag), value, bag->_context); + newValue = (void *)INVOKE_CALLBACK3(((const void *(*)(CFAllocatorRef, const void *, void *))cb->retain), allocator, 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); + INVOKE_CALLBACK3(((void (*)(CFAllocatorRef, const void *, void *))cb->release), allocator, match->_key, bag->_context); match->_key = bag->_deletedMarker; } if (bag->_emptyMarker == newValue) { @@ -688,7 +772,7 @@ void CFBagSetValue(CFMutableBagRef bag, const void *value) { if (bag->_deletedMarker == newValue) { __CFBagFindNewDeletedMarker(bag); } - match->_key = newValue; + CF_WRITE_BARRIER_ASSIGN(allocator, 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) { @@ -697,7 +781,7 @@ void CFBagSetValue(CFMutableBagRef bag, const void *value) { if (bag->_deletedMarker == newValue) { __CFBagFindNewDeletedMarker(bag); } - nomatch->_key = newValue; + CF_WRITE_BARRIER_ASSIGN(allocator, nomatch->_key, newValue); nomatch->_count = 1; bag->_bucketsUsed++; bag->_count++; diff --git a/Collections.subproj/CFBag.h b/Collections.subproj/CFBag.h index a95b9c9..97c89d9 100644 --- a/Collections.subproj/CFBag.h +++ b/Collections.subproj/CFBag.h @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -23,7 +21,7 @@ * @APPLE_LICENSE_HEADER_END@ */ /* CFBag.h - Copyright (c) 1998-2003, Apple, Inc. All rights reserved. + Copyright (c) 1998-2005, Apple, Inc. All rights reserved. */ #if !defined(__COREFOUNDATION_CFBAG__) diff --git a/Collections.subproj/CFBinaryHeap.c b/Collections.subproj/CFBinaryHeap.c index cf0fcaa..3f7e9eb 100644 --- a/Collections.subproj/CFBinaryHeap.c +++ b/Collections.subproj/CFBinaryHeap.c @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -28,7 +26,7 @@ */ #include -#include "CFUtilities.h" +#include "CFUtilitiesPriv.h" #include "CFInternal.h" const CFBinaryHeapCallBacks kCFStringBinaryHeapCallBacks = {0, __CFTypeCollectionRetain, __CFTypeCollectionRelease, CFCopyDescription, (CFComparisonResult (*)(const void *, const void *, void *))CFStringCompare}; @@ -87,12 +85,23 @@ CF_INLINE void __CFBinaryHeapSetNumBuckets(CFBinaryHeapRef heap, CFIndex v) { heap->_capacity = v; } -enum { - kCFBinaryHeapImmutable = 0x0, /* unchangable and fixed capacity; default */ +enum { /* bits 1-0 */ + kCFBinaryHeapImmutable = 0x0, /* unchangable and fixed capacity; default */ kCFBinaryHeapMutable = 0x1, /* changeable and variable capacity */ kCFBinaryHeapFixedMutable = 0x3 /* changeable and fixed capacity */ }; +/* Bits 4-5 are used by GC */ + +CF_INLINE bool isStrongMemory(CFTypeRef collection) { + return ! __CFBitfieldGetValue(((const CFRuntimeBase *)collection)->_info, 4, 4); +} + +CF_INLINE bool needsRestore(CFTypeRef collection) { + return __CFBitfieldGetValue(((const CFRuntimeBase *)collection)->_info, 5, 5); +} + + CF_INLINE UInt32 __CFBinaryHeapMutableVariety(const void *cf) { return __CFBitfieldGetValue(((const CFRuntimeBase *)cf)->_info, 3, 2); } @@ -117,7 +126,7 @@ static bool __CFBinaryHeapEqual(CFTypeRef cf1, CFTypeRef cf2) { 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); + list1 = (cnt <= 128) ? buffer : CFAllocatorAllocate(kCFAllocatorSystemDefault, 2 * cnt * sizeof(void *), 0); // GC OK if (__CFOASafe && list1 != buffer) __CFSetLastAllocationEventName(list1, "CFBinaryHeap (temp)"); list2 = (cnt <= 128) ? buffer + 128 : list1 + cnt; CFBinaryHeapGetValues(heap1, list1); @@ -129,7 +138,7 @@ static bool __CFBinaryHeapEqual(CFTypeRef cf1, CFTypeRef cf2) { // 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); + if (list1 != buffer) CFAllocatorDeallocate(CFGetAllocator(heap1), list1); // GC OK return true; } @@ -147,7 +156,7 @@ static CFStringRef __CFBinaryHeapCopyDescription(CFTypeRef cf) { 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); + list = (cnt <= 128) ? buffer : CFAllocatorAllocate(kCFAllocatorSystemDefault, cnt * sizeof(void *), 0); // GC OK if (__CFOASafe && list != buffer) __CFSetLastAllocationEventName(list, "CFBinaryHeap (temp)"); CFBinaryHeapGetValues(heap, list); for (idx = 0; idx < cnt; idx++) { @@ -164,25 +173,29 @@ static CFStringRef __CFBinaryHeapCopyDescription(CFTypeRef cf) { } } CFStringAppend(result, CFSTR(")}")); - if (list != buffer) CFAllocatorDeallocate(CFGetAllocator(heap), list); + if (list != buffer) CFAllocatorDeallocate(CFGetAllocator(heap), list); // GC OK return result; } static void __CFBinaryHeapDeallocate(CFTypeRef cf) { CFBinaryHeapRef heap = (CFBinaryHeapRef)cf; CFAllocatorRef allocator = CFGetAllocator(heap); + if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { + if (heap->_callbacks.retain == NULL && heap->_callbacks.release == NULL) + return; // GC: keep heap intact during finalization. + } // CF: should make the heap mutable here first, a la CFArrayDeallocate CFBinaryHeapRemoveAllValues(heap); // CF: does not release the context info if (__CFBinaryHeapMutableVariety(heap) == kCFBinaryHeapMutable) { - CFAllocatorDeallocate(allocator, heap->_buckets); + _CFAllocatorDeallocateGC(allocator, heap->_buckets); } } static CFTypeID __kCFBinaryHeapTypeID = _kCFRuntimeNotATypeID; static const CFRuntimeClass __CFBinaryHeapClass = { - 0, + _kCFRuntimeScannedObject, "CFBinaryHeap", NULL, // init NULL, // copy @@ -213,6 +226,22 @@ static CFBinaryHeapRef __CFBinaryHeapInit(CFAllocatorRef allocator, UInt32 flags size = sizeof(struct __CFBinaryHeap) - sizeof(CFRuntimeBase); if (__CFBinaryHeapMutableVarietyFromFlags(flags) != kCFBinaryHeapMutable) size += sizeof(struct __CFBinaryHeapBucket) * __CFBinaryHeapNumBucketsForCapacity(capacity); + CFBinaryHeapCallBacks nonRetainingCallbacks; + if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { + if (!callBacks || (callBacks->retain == NULL && callBacks->release == NULL)) { + __CFBitfieldSetValue(flags, 4, 4, 1); // setWeak + } + else { + if (callBacks->retain == __CFTypeCollectionRetain && callBacks->release == __CFTypeCollectionRelease) { + nonRetainingCallbacks = *callBacks; + nonRetainingCallbacks.retain = NULL; + nonRetainingCallbacks.release = NULL; + callBacks = &nonRetainingCallbacks; + __CFBitfieldSetValue(flags, 5, 5, 1); // setNeedsRestore + } + } + } + memory = (CFBinaryHeapRef)_CFRuntimeCreateInstance(allocator, __kCFBinaryHeapTypeID, size, NULL); if (NULL == memory) { return NULL; @@ -221,7 +250,8 @@ static CFBinaryHeapRef __CFBinaryHeapInit(CFAllocatorRef allocator, UInt32 flags case kCFBinaryHeapMutable: __CFBinaryHeapSetCapacity(memory, __CFBinaryHeapRoundUpCapacity(1)); __CFBinaryHeapSetNumBuckets(memory, __CFBinaryHeapNumBucketsForCapacity(__CFBinaryHeapRoundUpCapacity(1))); - memory->_buckets = CFAllocatorAllocate(allocator, __CFBinaryHeapNumBuckets(memory) * sizeof(struct __CFBinaryHeapBucket), 0); + void *buckets = _CFAllocatorAllocateGC(allocator, __CFBinaryHeapNumBuckets(memory) * sizeof(struct __CFBinaryHeapBucket), isStrongMemory(memory) ? AUTO_MEMORY_SCANNED : AUTO_MEMORY_UNSCANNED); + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, memory, memory->_buckets, buckets); if (__CFOASafe) __CFSetLastAllocationEventName(memory->_buckets, "CFBinaryHeap (store)"); if (NULL == memory->_buckets) { CFRelease(memory); @@ -230,6 +260,9 @@ static CFBinaryHeapRef __CFBinaryHeapInit(CFAllocatorRef allocator, UInt32 flags break; case kCFBinaryHeapFixedMutable: case kCFBinaryHeapImmutable: + if (!isStrongMemory(memory)) { // if weak, don't scan + auto_zone_set_layout_type(__CFCollectableZone, memory, AUTO_OBJECT_UNSCANNED); + } /* Don't round up capacity */ __CFBinaryHeapSetCapacity(memory, capacity); __CFBinaryHeapSetNumBuckets(memory, __CFBinaryHeapNumBucketsForCapacity(capacity)); @@ -316,7 +349,7 @@ const void *CFBinaryHeapGetMinimum(CFBinaryHeapRef heap) { Boolean CFBinaryHeapGetMinimumIfPresent(CFBinaryHeapRef heap, const void **value) { __CFGenericValidateType(heap, __kCFBinaryHeapTypeID); if (0 == __CFBinaryHeapCount(heap)) return false; - if (NULL != value) *value = heap->_buckets[0]._item; + if (NULL != value) __CFObjCStrongAssign(heap->_buckets[0]._item, value); return true; } @@ -328,6 +361,10 @@ void CFBinaryHeapGetValues(CFBinaryHeapRef heap, const void **values) { CFAssert1(NULL != values, __kCFLogAssertion, "%s(): pointer to values may not be NULL", __PRETTY_FUNCTION__); cnt = __CFBinaryHeapCount(heap); if (0 == cnt) return; + if (CF_USING_COLLECTABLE_MEMORY) { + // GC: speculatively issue a write-barrier on the copied to buffers (3743553). + __CFObjCWriteBarrierRange(values, cnt * sizeof(void *)); + } heapCopy = CFBinaryHeapCreateCopy(CFGetAllocator(heap), cnt, heap); idx = 0; while (0 < __CFBinaryHeapCount(heapCopy)) { @@ -357,9 +394,11 @@ void CFBinaryHeapApplyFunction(CFBinaryHeapRef heap, CFBinaryHeapApplierFunction static void __CFBinaryHeapGrow(CFBinaryHeapRef heap, CFIndex numNewValues) { CFIndex oldCount = __CFBinaryHeapCount(heap); CFIndex capacity = __CFBinaryHeapRoundUpCapacity(oldCount + numNewValues); + CFAllocatorRef allocator = CFGetAllocator(heap); __CFBinaryHeapSetCapacity(heap, capacity); __CFBinaryHeapSetNumBuckets(heap, __CFBinaryHeapNumBucketsForCapacity(capacity)); - heap->_buckets = CFAllocatorReallocate(CFGetAllocator(heap), heap->_buckets, __CFBinaryHeapNumBuckets(heap) * sizeof(struct __CFBinaryHeapBucket), 0); + void *buckets = _CFAllocatorReallocateGC(allocator, heap->_buckets, __CFBinaryHeapNumBuckets(heap) * sizeof(struct __CFBinaryHeapBucket), isStrongMemory(heap) ? AUTO_MEMORY_SCANNED : AUTO_MEMORY_UNSCANNED); + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, heap, heap->_buckets, buckets); if (__CFOASafe) __CFSetLastAllocationEventName(heap->_buckets, "CFBinaryHeap (store)"); if (NULL == heap->_buckets) HALT; } @@ -367,6 +406,7 @@ static void __CFBinaryHeapGrow(CFBinaryHeapRef heap, CFIndex numNewValues) { void CFBinaryHeapAddValue(CFBinaryHeapRef heap, const void *value) { CFIndex idx, pidx; CFIndex cnt; + CFAllocatorRef allocator = CFGetAllocator(heap); __CFGenericValidateType(heap, __kCFBinaryHeapTypeID); CFAssert1(__CFBinaryHeapMutableVariety(heap) == kCFBinaryHeapMutable || __CFBinaryHeapMutableVariety(heap) == kCFBinaryHeapFixedMutable, __kCFLogAssertion, "%s(): binary heap is immutable", __PRETTY_FUNCTION__); switch (__CFBinaryHeapMutableVariety(heap)) { @@ -386,14 +426,14 @@ void CFBinaryHeapAddValue(CFBinaryHeapRef heap, const void *value) { 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; + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, heap->_buckets, 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); + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, heap->_buckets, heap->_buckets[idx]._item, (void *)heap->_callbacks.retain(allocator, (void *)value)); } else { - heap->_buckets[idx]._item = (void *)value; + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, heap->_buckets, heap->_buckets[idx]._item, (void *)value); } } @@ -401,6 +441,7 @@ void CFBinaryHeapRemoveMinimumValue(CFBinaryHeapRef heap) { void *val; CFIndex idx, cidx; CFIndex cnt; + CFAllocatorRef allocator; __CFGenericValidateType(heap, __kCFBinaryHeapTypeID); CFAssert1(__CFBinaryHeapMutableVariety(heap) == kCFBinaryHeapMutable || __CFBinaryHeapMutableVariety(heap) == kCFBinaryHeapFixedMutable, __kCFLogAssertion, "%s(): binary heap is immutable", __PRETTY_FUNCTION__); cnt = __CFBinaryHeapCount(heap); @@ -408,8 +449,9 @@ void CFBinaryHeapRemoveMinimumValue(CFBinaryHeapRef heap) { idx = 0; __CFBinaryHeapSetNumBucketsUsed(heap, cnt - 1); __CFBinaryHeapSetCount(heap, cnt - 1); + allocator = CFGetAllocator(heap); if (heap->_callbacks.release) - heap->_callbacks.release(CFGetAllocator(heap), heap->_buckets[idx]._item); + heap->_callbacks.release(allocator, heap->_buckets[idx]._item); val = heap->_buckets[cnt - 1]._item; cidx = (idx << 1) + 1; while (cidx < __CFBinaryHeapCount(heap)) { @@ -422,11 +464,11 @@ void CFBinaryHeapRemoveMinimumValue(CFBinaryHeapRef heap) { } } if (kCFCompareGreaterThan == heap->_callbacks.compare(item, val, heap->_context.info)) break; - heap->_buckets[idx]._item = item; + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, heap->_buckets, heap->_buckets[idx]._item, item); idx = cidx; cidx = (idx << 1) + 1; } - heap->_buckets[idx]._item = val; + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, heap->_buckets, heap->_buckets[idx]._item, val); } void CFBinaryHeapRemoveAllValues(CFBinaryHeapRef heap) { diff --git a/Collections.subproj/CFBinaryHeap.h b/Collections.subproj/CFBinaryHeap.h index 2db26af..9528c4c 100644 --- a/Collections.subproj/CFBinaryHeap.h +++ b/Collections.subproj/CFBinaryHeap.h @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -23,7 +21,7 @@ * @APPLE_LICENSE_HEADER_END@ */ /* CFBinaryHeap.h - Copyright (c) 1998-2003, Apple, Inc. All rights reserved. + Copyright (c) 1998-2005, Apple, Inc. All rights reserved. */ /*! @header CFBinaryHeap diff --git a/Collections.subproj/CFBitVector.c b/Collections.subproj/CFBitVector.c index 7fe20d3..8b0590c 100644 --- a/Collections.subproj/CFBitVector.c +++ b/Collections.subproj/CFBitVector.c @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -206,14 +204,14 @@ static void __CFBitVectorDeallocate(CFTypeRef cf) { CFMutableBitVectorRef bv = (CFMutableBitVectorRef)cf; CFAllocatorRef allocator = CFGetAllocator(bv); if (__CFBitVectorMutableVariety(bv) == kCFBitVectorMutable) { - CFAllocatorDeallocate(allocator, bv->_buckets); + _CFAllocatorDeallocateGC(allocator, bv->_buckets); } } static CFTypeID __kCFBitVectorTypeID = _kCFRuntimeNotATypeID; static const CFRuntimeClass __CFBitVectorClass = { - 0, + _kCFRuntimeScannedObject, "CFBitVector", NULL, // init NULL, // copy @@ -249,7 +247,7 @@ static CFMutableBitVectorRef __CFBitVectorInit(CFAllocatorRef allocator, CFOptio case kCFBitVectorMutable: __CFBitVectorSetCapacity(memory, __CFBitVectorRoundUpCapacity(1)); __CFBitVectorSetNumBuckets(memory, __CFBitVectorNumBucketsForCapacity(__CFBitVectorRoundUpCapacity(1))); - memory->_buckets = CFAllocatorAllocate(allocator, __CFBitVectorNumBuckets(memory) * sizeof(__CFBitVectorBucket), 0); + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, memory, memory->_buckets, _CFAllocatorAllocateGC(allocator, __CFBitVectorNumBuckets(memory) * sizeof(__CFBitVectorBucket), 0)); if (__CFOASafe) __CFSetLastAllocationEventName(memory->_buckets, "CFBitVector (store)"); if (NULL == memory->_buckets) { CFRelease(memory); @@ -268,7 +266,7 @@ static CFMutableBitVectorRef __CFBitVectorInit(CFAllocatorRef allocator, CFOptio __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); + memmove(memory->_buckets, bytes, (numBits + __CF_BITS_PER_BYTE - 1) / __CF_BITS_PER_BYTE); } __CFBitVectorSetMutableVariety(memory, __CFBitVectorMutableVarietyFromFlags(flags)); return memory; @@ -385,7 +383,7 @@ static __CFBitVectorBucket __CFBitVectorGetBits(__CFBitVectorBucket bucketValue, __CFBitVectorBucket val; CFIndex nBits; val = bucketValue & bucketValueMask; - nBits = __CFMin(__CF_BITS_PER_BUCKET, context->totalBits); + nBits = __CFMin(__CF_BITS_PER_BUCKET - context->initBits, context->totalBits); /* First initBits bits go in *curByte ... */ if (0 < context->initBits) { if (!context->ignoreFirstInitBits) { @@ -394,7 +392,6 @@ static __CFBitVectorBucket __CFBitVectorGetBits(__CFBitVectorBucket bucketValue, context->totalBits -= context->initBits; context->ignoreFirstInitBits = false; } - nBits -= context->initBits; val <<= context->initBits; } /* ... then next groups of __CF_BITS_PER_BYTE go in *curByte ... */ @@ -452,9 +449,10 @@ CFIndex CFBitVectorGetLastIndexOfBit(CFBitVectorRef bv, CFRange range, CFBit val static void __CFBitVectorGrow(CFMutableBitVectorRef bv, CFIndex numNewValues) { CFIndex oldCount = __CFBitVectorCount(bv); CFIndex capacity = __CFBitVectorRoundUpCapacity(oldCount + numNewValues); + CFAllocatorRef allocator = CFGetAllocator(bv); __CFBitVectorSetCapacity(bv, capacity); __CFBitVectorSetNumBuckets(bv, __CFBitVectorNumBucketsForCapacity(capacity)); - bv->_buckets = CFAllocatorReallocate(CFGetAllocator(bv), bv->_buckets, __CFBitVectorNumBuckets(bv) * sizeof(__CFBitVectorBucket), 0); + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, bv, bv->_buckets, CFAllocatorReallocate(allocator, bv->_buckets, __CFBitVectorNumBuckets(bv) * sizeof(__CFBitVectorBucket), 0)); if (__CFOASafe) __CFSetLastAllocationEventName(bv->_buckets, "CFBitVector (store)"); if (NULL == bv->_buckets) HALT; } diff --git a/Collections.subproj/CFBitVector.h b/Collections.subproj/CFBitVector.h index 4cde214..36de4f1 100644 --- a/Collections.subproj/CFBitVector.h +++ b/Collections.subproj/CFBitVector.h @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -23,7 +21,7 @@ * @APPLE_LICENSE_HEADER_END@ */ /* CFBitVector.h - Copyright (c) 1998-2003, Apple, Inc. All rights reserved. + Copyright (c) 1998-2005, Apple, Inc. All rights reserved. */ #if !defined(__COREFOUNDATION_CFBITVECTOR__) diff --git a/Collections.subproj/CFData.c b/Collections.subproj/CFData.c index 3191091..449043c 100644 --- a/Collections.subproj/CFData.c +++ b/Collections.subproj/CFData.c @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -28,7 +26,7 @@ */ #include -#include "CFUtilities.h" +#include "CFUtilitiesPriv.h" #include "CFInternal.h" #include @@ -161,14 +159,21 @@ static void __CFDataDeallocate(CFTypeRef cf) { CFAllocatorRef allocator = CFGetAllocator(data); switch (__CFMutableVariety(data)) { case kCFMutable: - CFAllocatorDeallocate(allocator, data->_bytes); + _CFAllocatorDeallocateGC(allocator, data->_bytes); + data->_bytes = NULL; break; case kCFFixedMutable: break; case kCFImmutable: if (NULL != data->_bytesDeallocator) { - CFAllocatorDeallocate(data->_bytesDeallocator, data->_bytes); - CFRelease(data->_bytesDeallocator); + if (CF_IS_COLLECTABLE_ALLOCATOR(data->_bytesDeallocator)) { + // GC: for finalization safety, let collector reclaim the buffer in the next GC cycle. + auto_zone_release(__CFCollectableZone, data->_bytes); + } else { + CFAllocatorDeallocate(data->_bytesDeallocator, data->_bytes); + CFRelease(data->_bytesDeallocator); + data->_bytes = NULL; + } } break; } @@ -222,8 +227,10 @@ static CFMutableDataRef __CFDataInit(CFAllocatorRef allocator, CFOptionFlags fla case kCFMutable: __CFDataSetCapacity(memory, __CFDataRoundUpCapacity(1)); __CFDataSetNumBytes(memory, __CFDataNumBytesForCapacity(__CFDataRoundUpCapacity(1))); + // GC: if allocated in the collectable zone, mark the object as needing to be scanned. + if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) auto_zone_set_layout_type(__CFCollectableZone, memory, AUTO_MEMORY_SCANNED); // assume that allocators give 16-byte aligned memory back -- it is their responsibility - memory->_bytes = CFAllocatorAllocate(allocator, __CFDataNumBytes(memory) * sizeof(uint8_t), 0); + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, memory, memory->_bytes, _CFAllocatorAllocateGC(allocator, __CFDataNumBytes(memory) * sizeof(uint8_t), AUTO_MEMORY_UNSCANNED)); if (__CFOASafe) __CFSetLastAllocationEventName(memory->_bytes, "CFData (store)"); if (NULL == memory->_bytes) { CFRelease(memory); @@ -247,7 +254,7 @@ static CFMutableDataRef __CFDataInit(CFAllocatorRef allocator, CFOptionFlags fla __CFDataSetCapacity(memory, capacity); __CFDataSetNumBytes(memory, __CFDataNumBytesForCapacity(capacity)); if (bytesDeallocator != NULL) { - memory->_bytes = (uint8_t *)bytes; + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, memory, memory->_bytes, (uint8_t *)bytes); memory->_bytesDeallocator = CFRetain(bytesDeallocator); __CFDataSetNumBytesUsed(memory, length); __CFDataSetLength(memory, length); @@ -315,9 +322,10 @@ void CFDataGetBytes(CFDataRef data, CFRange range, uint8_t *buffer) { static void __CFDataGrow(CFMutableDataRef data, CFIndex numNewValues) { CFIndex oldLength = __CFDataLength(data); CFIndex capacity = __CFDataRoundUpCapacity(oldLength + numNewValues); + CFAllocatorRef allocator = CFGetAllocator(data); __CFDataSetCapacity(data, capacity); __CFDataSetNumBytes(data, __CFDataNumBytesForCapacity(capacity)); - data->_bytes = CFAllocatorReallocate(CFGetAllocator(data), data->_bytes, __CFDataNumBytes(data) * sizeof(uint8_t), 0); + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, data, data->_bytes, _CFAllocatorReallocateGC(allocator, data->_bytes, __CFDataNumBytes(data) * sizeof(uint8_t), AUTO_MEMORY_UNSCANNED)); if (__CFOASafe) __CFSetLastAllocationEventName(data->_bytes, "CFData (store)"); if (NULL == data->_bytes) HALT; } diff --git a/Collections.subproj/CFData.h b/Collections.subproj/CFData.h index 3b46f0d..beaa349 100644 --- a/Collections.subproj/CFData.h +++ b/Collections.subproj/CFData.h @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -23,7 +21,7 @@ * @APPLE_LICENSE_HEADER_END@ */ /* CFData.h - Copyright (c) 1998-2003, Apple, Inc. All rights reserved. + Copyright (c) 1998-2005, Apple, Inc. All rights reserved. */ #if !defined(__COREFOUNDATION_CFDATA__) diff --git a/Collections.subproj/CFDictionary.c b/Collections.subproj/CFDictionary.c index 7f11d22..706d3e9 100644 --- a/Collections.subproj/CFDictionary.c +++ b/Collections.subproj/CFDictionary.c @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -76,6 +74,21 @@ enum { /* Bits 5-4 (value), 3-2 (key) */ __kCFDictionaryHasCustomCallBacks = 3 /* callbacks are at end of header */ }; +// Under GC, we fudge the key/value memory in two ways +// First, if we had null callbacks or null for both retain/release, we use unscanned memory +// This means that if people were doing addValue:[xxx new] and never removing, well, that doesn't work +// +// Second, if we notice standard retain/release implementations we substitute scanned memory +// and zero out the retain/release callbacks. This is fine, but when copying we need to restore them + +enum { + __kCFDictionaryRestoreKeys = (1 << 0), + __kCFDictionaryRestoreValues = (1 << 1), + __kCFDictionaryRestoreStringKeys = (1 << 2), + __kCFDictionaryWeakKeys = (1 << 3), + __kCFDictionaryWeakValues = (1 << 4) +}; + struct __CFDictionary { CFRuntimeBase _base; CFIndex _count; /* number of values */ @@ -84,6 +97,7 @@ struct __CFDictionary { uintptr_t _marker; void *_context; /* private */ CFIndex _deletes; + CFOptionFlags _xflags; /* bits for GC */ const void **_keys; /* can be NULL if not allocated yet */ const void **_values; /* can be NULL if not allocated yet */ }; @@ -182,29 +196,14 @@ 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; +CF_INLINE bool _CFDictionaryIsSplit(CFDictionaryRef dict) { + return ((dict->_xflags & (__kCFDictionaryWeakKeys|__kCFDictionaryWeakValues)) != 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));} +#define CF_OBJC_KVO_WILLCHANGE(obj, sel) +#define CF_OBJC_KVO_DIDCHANGE(obj, sel) -#endif static CFIndex __CFDictionaryFindBuckets1a(CFDictionaryRef dict, const void *key) { @@ -250,7 +249,7 @@ static CFIndex __CFDictionaryFindBuckets1b(CFDictionaryRef dict, const void *key 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))) { + } else if (currKey == (uintptr_t)key || (cb->equal && INVOKE_CALLBACK3((Boolean (*)(const void *, const void *, void*))cb->equal, (void *)currKey, key, dict->_context))) { return probe; } probe = probe + probeskip; @@ -279,18 +278,16 @@ static void __CFDictionaryFindBuckets2(CFDictionaryRef dict, const void *key, CF for (;;) { uintptr_t currKey = (uintptr_t)keys[probe]; if (marker == currKey) { /* empty */ - if (kCFNotFound == *nomatch) *nomatch = probe; + if (nomatch) *nomatch = probe; return; } else if (~marker == currKey) { /* deleted */ - if (kCFNotFound == *nomatch) *nomatch = probe; - if (kCFNotFound != *match) { - return; + if (nomatch) { + *nomatch = probe; + nomatch = NULL; } - } else if (kCFNotFound == *match && (currKey == (uintptr_t)key || (cb->equal && INVOKE_CALLBACK3((Boolean (*)(void *, void *, void*))cb->equal, currKey, key, dict->_context)))) { + } else if (currKey == (uintptr_t)key || (cb->equal && INVOKE_CALLBACK3((Boolean (*)(const void *, const void *, void*))cb->equal, (void *)currKey, key, dict->_context))) { *match = probe; - if (kCFNotFound != *nomatch) { - return; - } + return; } probe = probe + probeskip; // This alternative to probe % buckets assumes that @@ -355,7 +352,7 @@ static bool __CFDictionaryEqual(CFTypeRef cf1, CFTypeRef cf2) { 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)) { + if (dict1->_values[idx] != value && vcb1->equal && !INVOKE_CALLBACK3((Boolean (*)(const void *, const void *, void*))vcb1->equal, dict1->_values[idx], value, dict1->_context)) { return false; } } @@ -424,19 +421,60 @@ static CFStringRef __CFDictionaryCopyDescription(CFTypeRef cf) { static void __CFDictionaryDeallocate(CFTypeRef cf) { CFMutableDictionaryRef dict = (CFMutableDictionaryRef)cf; CFAllocatorRef allocator = __CFGetAllocator(dict); + if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { + const CFDictionaryKeyCallBacks *kcb = __CFDictionaryGetKeyCallBacks(dict); + const CFDictionaryValueCallBacks *vcb = __CFDictionaryGetValueCallBacks(dict); + if (kcb->retain == NULL && kcb->release == NULL && vcb->retain == NULL && vcb->release == NULL) + return; // XXX_PCB keep dictionary intact during finalization. + } if (__CFDictionaryGetType(dict) == __kCFDictionaryImmutable) { __CFBitfieldSetValue(((CFRuntimeBase *)dict)->_info, 1, 0, __kCFDictionaryFixedMutable); } - CFDictionaryRemoveAllValues(dict); + + const CFDictionaryKeyCallBacks *cb = __CFDictionaryGetKeyCallBacks(dict); + const CFDictionaryValueCallBacks *vcb = __CFDictionaryGetValueCallBacks(dict); + if (vcb->release || cb->release) { + const void **keys = dict->_keys; + CFIndex idx, nbuckets = dict->_bucketsNum; + for (idx = 0; idx < nbuckets; idx++) { + if (dict->_marker != (uintptr_t)keys[idx] && ~dict->_marker != (uintptr_t)keys[idx]) { + const void *oldkey = keys[idx]; + if (vcb->release) { + INVOKE_CALLBACK3(((void (*)(CFAllocatorRef, const void *, void *))vcb->release), allocator, dict->_values[idx], dict->_context); + } + if (cb->release) { + INVOKE_CALLBACK3(((void (*)(CFAllocatorRef, const void *, void *))cb->release), allocator, oldkey, dict->_context); + } + } + } + } + if (__CFDictionaryGetType(dict) == __kCFDictionaryMutable && dict->_keys) { - CFAllocatorDeallocate(allocator, dict->_keys); + _CFAllocatorDeallocateGC(allocator, dict->_keys); + if (_CFDictionaryIsSplit(dict)) { // iff GC, split allocations + _CFAllocatorDeallocateGC(allocator, dict->_values); + } + dict->_keys = NULL; + dict->_values = NULL; } } +/* + * When running under GC, we suss up dictionaries with standard string copy to hold + * onto everything, including the copies of incoming keys, in strong memory without retain counts. + * This is the routine that makes that copy. + * Not for inputs of constant strings we'll get a constant string back, and so the result + * is not guaranteed to be from the auto zone, hence the call to CFRelease since it will figure + * out where the refcount really is. + */ +static CFStringRef _CFStringCreateCopyCollected(CFAllocatorRef allocator, CFStringRef theString) { + return CFMakeCollectable(CFStringCreateCopy(NULL, theString)); +} + static CFTypeID __kCFDictionaryTypeID = _kCFRuntimeNotATypeID; static const CFRuntimeClass __CFDictionaryClass = { - 0, + _kCFRuntimeScannedObject, "CFDictionary", NULL, // init NULL, // copy @@ -455,14 +493,53 @@ CFTypeID CFDictionaryGetTypeID(void) { return __kCFDictionaryTypeID; } -static CFDictionaryRef __CFDictionaryInit(CFAllocatorRef allocator, uint32_t flags, CFIndex capacity, const CFDictionaryKeyCallBacks *callBacks, const CFDictionaryValueCallBacks *valueCallBacks) { +static CFDictionaryRef __CFDictionaryInit(CFAllocatorRef allocator, uint32_t flags, CFIndex capacity, const CFDictionaryKeyCallBacks *keyCallBacks, const CFDictionaryValueCallBacks *valueCallBacks) { struct __CFDictionary *memory; uint32_t size; CFIndex idx; __CFBitfieldSetValue(flags, 31, 2, 0); - if (__CFDictionaryKeyCallBacksMatchNull(callBacks)) { + CFDictionaryKeyCallBacks nonRetainingKeyCallbacks; + CFDictionaryValueCallBacks nonRetainingValueCallbacks; + CFOptionFlags xflags = 0; + if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { + // preserve NULL for key or value CB, otherwise fix up. + if (!keyCallBacks || (keyCallBacks->retain == NULL && keyCallBacks->release == NULL)) { + xflags = __kCFDictionaryWeakKeys; + } + else { + if (keyCallBacks->retain == __CFTypeCollectionRetain && keyCallBacks->release == __CFTypeCollectionRelease) { + // copy everything + nonRetainingKeyCallbacks = *keyCallBacks; + nonRetainingKeyCallbacks.retain = NULL; + nonRetainingKeyCallbacks.release = NULL; + keyCallBacks = &nonRetainingKeyCallbacks; + xflags = __kCFDictionaryRestoreKeys; + } + else if (keyCallBacks->retain == CFStringCreateCopy && keyCallBacks->release == __CFTypeCollectionRelease) { + // copy everything + nonRetainingKeyCallbacks = *keyCallBacks; + nonRetainingKeyCallbacks.retain = (void *)_CFStringCreateCopyCollected; // XXX fix with better cast + nonRetainingKeyCallbacks.release = NULL; + keyCallBacks = &nonRetainingKeyCallbacks; + xflags = (__kCFDictionaryRestoreKeys | __kCFDictionaryRestoreStringKeys); + } + } + if (!valueCallBacks || (valueCallBacks->retain == NULL && valueCallBacks->release == NULL)) { + xflags |= __kCFDictionaryWeakValues; + } + else { + if (valueCallBacks->retain == __CFTypeCollectionRetain && valueCallBacks->release == __CFTypeCollectionRelease) { + nonRetainingValueCallbacks = *valueCallBacks; + nonRetainingValueCallbacks.retain = NULL; + nonRetainingValueCallbacks.release = NULL; + valueCallBacks = &nonRetainingValueCallbacks; + xflags |= __kCFDictionaryRestoreValues; + } + } + } + if (__CFDictionaryKeyCallBacksMatchNull(keyCallBacks)) { __CFBitfieldSetValue(flags, 3, 2, __kCFDictionaryHasNullCallBacks); - } else if (__CFDictionaryKeyCallBacksMatchCFType(callBacks)) { + } else if (__CFDictionaryKeyCallBacksMatchCFType(keyCallBacks)) { __CFBitfieldSetValue(flags, 3, 2, __kCFDictionaryHasCFTypeCallBacks); } else { __CFBitfieldSetValue(flags, 3, 2, __kCFDictionaryHasCustomCallBacks); @@ -478,10 +555,10 @@ static CFDictionaryRef __CFDictionaryInit(CFAllocatorRef allocator, uint32_t fla switch (__CFBitfieldGetValue(flags, 1, 0)) { case __kCFDictionaryImmutable: case __kCFDictionaryFixedMutable: - size += 2 * __CFDictionaryNumBucketsForCapacity(capacity) * sizeof(const void *); - break; + size += 2 * __CFDictionaryNumBucketsForCapacity(capacity) * sizeof(const void *); + break; case __kCFDictionaryMutable: - break; + break; } memory = (struct __CFDictionary *)_CFRuntimeCreateInstance(allocator, __kCFDictionaryTypeID, size, NULL); if (NULL == memory) { @@ -492,9 +569,15 @@ static CFDictionaryRef __CFDictionaryInit(CFAllocatorRef allocator, uint32_t fla memory->_marker = (uintptr_t)0xa1b1c1d3; memory->_context = NULL; memory->_deletes = 0; + memory->_xflags = xflags; switch (__CFBitfieldGetValue(flags, 1, 0)) { case __kCFDictionaryImmutable: - if (__CFOASafe) __CFSetLastAllocationEventName(memory, "CFDictionary (immutable)"); + // GC note: we can't support weak part of mixed weak/strong in fixed case, we treat it as strong XXX blaine + if (CF_IS_COLLECTABLE_ALLOCATOR(allocator) + && (xflags & (__kCFDictionaryWeakKeys|__kCFDictionaryWeakValues)) == (__kCFDictionaryWeakKeys|__kCFDictionaryWeakValues)) { // if both weak, don't scan + auto_zone_set_layout_type(__CFCollectableZone, memory, AUTO_OBJECT_UNSCANNED); + } + 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)); @@ -504,6 +587,11 @@ static CFDictionaryRef __CFDictionaryInit(CFAllocatorRef allocator, uint32_t fla } break; case __kCFDictionaryFixedMutable: + // GC note: we can't support weak part of mixed weak/strong in fixed case, we treat it as strong XXX blaine + if (CF_IS_COLLECTABLE_ALLOCATOR(allocator) + && (xflags & (__kCFDictionaryWeakKeys|__kCFDictionaryWeakValues)) == (__kCFDictionaryWeakKeys|__kCFDictionaryWeakValues)) { // if both weak, don't scan + auto_zone_set_layout_type(__CFCollectableZone, memory, AUTO_OBJECT_UNSCANNED); + } if (__CFOASafe) __CFSetLastAllocationEventName(memory, "CFDictionary (mutable-fixed)"); memory->_capacity = capacity; /* Don't round up capacity */ memory->_bucketsNum = __CFDictionaryNumBucketsForCapacity(memory->_capacity); @@ -522,8 +610,8 @@ static CFDictionaryRef __CFDictionaryInit(CFAllocatorRef allocator, uint32_t fla break; } if (__kCFDictionaryHasCustomCallBacks == __CFBitfieldGetValue(flags, 3, 2)) { - const CFDictionaryKeyCallBacks *cb = __CFDictionaryGetKeyCallBacks((CFDictionaryRef)memory); - *(CFDictionaryKeyCallBacks *)cb = *callBacks; + CFDictionaryKeyCallBacks *cb = (CFDictionaryKeyCallBacks *)__CFDictionaryGetKeyCallBacks((CFDictionaryRef)memory); + *cb = *keyCallBacks; FAULT_CALLBACK((void **)&(cb->retain)); FAULT_CALLBACK((void **)&(cb->release)); FAULT_CALLBACK((void **)&(cb->copyDescription)); @@ -531,8 +619,8 @@ static CFDictionaryRef __CFDictionaryInit(CFAllocatorRef allocator, uint32_t fla FAULT_CALLBACK((void **)&(cb->hash)); } if (__kCFDictionaryHasCustomCallBacks == __CFBitfieldGetValue(flags, 5, 4)) { - const CFDictionaryValueCallBacks *vcb = __CFDictionaryGetValueCallBacks((CFDictionaryRef)memory); - *(CFDictionaryValueCallBacks *)vcb = *valueCallBacks; + CFDictionaryValueCallBacks *vcb = (CFDictionaryValueCallBacks *)__CFDictionaryGetValueCallBacks((CFDictionaryRef)memory); + *vcb = *valueCallBacks; FAULT_CALLBACK((void **)&(vcb->retain)); FAULT_CALLBACK((void **)&(vcb->release)); FAULT_CALLBACK((void **)&(vcb->copyDescription)); @@ -549,6 +637,7 @@ CFDictionaryRef CFDictionaryCreate(CFAllocatorRef allocator, const void **keys, result = __CFDictionaryInit(allocator, __kCFDictionaryImmutable, numValues, keyCallBacks, valueCallBacks); flags = __CFBitfieldGetValue(((const CFRuntimeBase *)result)->_info, 1, 0); if (flags == __kCFDictionaryImmutable) { + // tweak flags so that we can add our immutable values __CFBitfieldSetValue(((CFRuntimeBase *)result)->_info, 1, 0, __kCFDictionaryFixedMutable); } for (idx = 0; idx < numValues; idx++) { @@ -565,8 +654,12 @@ CFMutableDictionaryRef CFDictionaryCreateMutable(CFAllocatorRef allocator, CFInd static void __CFDictionaryGrow(CFMutableDictionaryRef dict, CFIndex numNewValues); +// This creates a dictionary which is for CFTypes or NSObjects, with an ownership transfer -- +// the dictionary does not take a retain, and the caller does not need to release the inserted objects. +// The incoming objects must also be collectable if allocated out of a collectable allocator. CFDictionaryRef _CFDictionaryCreate_ex(CFAllocatorRef allocator, bool mutable, const void **keys, const void **values, CFIndex numValues) { CFDictionaryRef result; + void *bucketsBase; uint32_t flags; CFIndex idx; result = __CFDictionaryInit(allocator, mutable ? __kCFDictionaryMutable : __kCFDictionaryImmutable, numValues, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); @@ -579,20 +672,26 @@ CFDictionaryRef _CFDictionaryCreate_ex(CFAllocatorRef allocator, bool mutable, c __CFDictionaryGrow((CFMutableDictionaryRef)result, numValues); } } + // GC: since kCFTypeDictionaryKeyCallBacks & kCFTypeDictionaryValueCallBacks are used, the keys + // and values will be allocated contiguously. + bool collectableContainer = CF_IS_COLLECTABLE_ALLOCATOR(allocator); + bucketsBase = (collectableContainer ? auto_zone_base_pointer(__CFCollectableZone, result->_keys) : NULL); 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]; + if (!collectableContainer) CFRelease(result->_values[match]); + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, bucketsBase, result->_values[match], values[idx]); + // GC: generation(_values) <= generation(values), but added for completeness. } 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]; + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, bucketsBase, result->_keys[nomatch], newKey); + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, bucketsBase, result->_values[nomatch], values[idx]); + // GC: generation(_keys/_values) <= generation(keys/values), but added for completeness. ((CFMutableDictionaryRef)result)->_count++; } } @@ -607,16 +706,38 @@ CFDictionaryRef CFDictionaryCreateCopy(CFAllocatorRef allocator, CFDictionaryRef CFIndex numValues = CFDictionaryGetCount(dict); const void **list, *buffer[256]; const void **vlist, *vbuffer[256]; - list = (numValues <= 256) ? buffer : CFAllocatorAllocate(allocator, numValues * sizeof(void *), 0); + list = (numValues <= 256) ? buffer : CFAllocatorAllocate(allocator, numValues * sizeof(void *), 0); // XXX_PCB GC OK if (list != buffer && __CFOASafe) __CFSetLastAllocationEventName(list, "CFDictionary (temp)"); - vlist = (numValues <= 256) ? vbuffer : CFAllocatorAllocate(allocator, numValues * sizeof(void *), 0); + vlist = (numValues <= 256) ? vbuffer : CFAllocatorAllocate(allocator, numValues * sizeof(void *), 0); // XXX_PCB GC OK 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); + CFDictionaryKeyCallBacks patchedKeyCB; + CFDictionaryValueCallBacks patchedValueCB; + if (CF_IS_OBJC(__kCFDictionaryTypeID, dict)) { + cb = &kCFTypeDictionaryKeyCallBacks; + vcb = &kCFTypeDictionaryValueCallBacks; + } + else { + cb = __CFDictionaryGetKeyCallBacks(dict); + vcb = __CFDictionaryGetValueCallBacks(dict); + if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { + if (dict->_xflags & __kCFDictionaryRestoreKeys) { + patchedKeyCB = *cb; // copy + cb = &patchedKeyCB; // reset to copy + patchedKeyCB.retain = (dict->_xflags & __kCFDictionaryRestoreStringKeys) ? CFStringCreateCopy : __CFTypeCollectionRetain; + patchedKeyCB.release = __CFTypeCollectionRelease; + } + if (dict->_xflags & __kCFDictionaryRestoreValues) { + patchedValueCB = *vcb; // copy + vcb = &patchedValueCB; // reset to copy + patchedValueCB.retain = __CFTypeCollectionRetain; + patchedValueCB.release = __CFTypeCollectionRelease; + } + } + } result = CFDictionaryCreate(allocator, list, vlist, numValues, cb, vcb); - if (list != buffer) CFAllocatorDeallocate(allocator, list); - if (vlist != vbuffer) CFAllocatorDeallocate(allocator, vlist); + if (list != buffer) CFAllocatorDeallocate(allocator, list); // GC OK + if (vlist != vbuffer) CFAllocatorDeallocate(allocator, vlist); // GC OK return result; } @@ -628,26 +749,51 @@ CFMutableDictionaryRef CFDictionaryCreateMutableCopy(CFAllocatorRef allocator, C 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); + list = (numValues <= 256) ? buffer : CFAllocatorAllocate(allocator, numValues * sizeof(void *), 0); // XXX_PCB GC OK if (list != buffer && __CFOASafe) __CFSetLastAllocationEventName(list, "CFDictionary (temp)"); - vlist = (numValues <= 256) ? vbuffer : CFAllocatorAllocate(allocator, numValues * sizeof(void *), 0); + vlist = (numValues <= 256) ? vbuffer : CFAllocatorAllocate(allocator, numValues * sizeof(void *), 0); // XXX_PCB GC OK 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); + CFDictionaryKeyCallBacks patchedKeyCB; + CFDictionaryValueCallBacks patchedValueCB; + if (CF_IS_OBJC(__kCFDictionaryTypeID, dict)) { + cb = &kCFTypeDictionaryKeyCallBacks; + vcb = &kCFTypeDictionaryValueCallBacks; + } + else { + cb = __CFDictionaryGetKeyCallBacks(dict); + vcb = __CFDictionaryGetValueCallBacks(dict); + if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { + if (dict->_xflags & __kCFDictionaryRestoreKeys) { + patchedKeyCB = *cb; // copy + cb = &patchedKeyCB; // reset to copy + patchedKeyCB.retain = (dict->_xflags & __kCFDictionaryRestoreStringKeys) ? CFStringCreateCopy : __CFTypeCollectionRetain; + patchedKeyCB.release = __CFTypeCollectionRelease; + } + if (dict->_xflags & __kCFDictionaryRestoreValues) { + patchedValueCB = *vcb; // copy + vcb = &patchedValueCB; // reset to copy + patchedValueCB.retain = __CFTypeCollectionRetain; + patchedValueCB.release = __CFTypeCollectionRelease; + } + } + } 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); + if (list != buffer) CFAllocatorDeallocate(allocator, list); // XXX_PCB GC OK + if (vlist != vbuffer) CFAllocatorDeallocate(allocator, vlist); // XXX_PCB GC OK return result; } + + // Used by NSMapTables and KVO void _CFDictionarySetContext(CFDictionaryRef dict, void *context) { - ((struct __CFDictionary *)dict)->_context = context; + CFAllocatorRef allocator = CFGetAllocator(dict); + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, dict, dict->_context, context); } void *_CFDictionaryGetContext(CFDictionaryRef dict) { @@ -685,7 +831,7 @@ CFIndex CFDictionaryGetCountOfValue(CFDictionaryRef dict, const void *value) { 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))) { + if ((dict->_values[idx] == value) || (vcb->equal && INVOKE_CALLBACK3((Boolean (*)(const void *, const void *, void*))vcb->equal, dict->_values[idx], value, dict->_context))) { cnt++; } } @@ -718,7 +864,7 @@ Boolean CFDictionaryContainsValue(CFDictionaryRef dict, const void *value) { 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))) { + if ((dict->_values[idx] == value) || (vcb->equal && INVOKE_CALLBACK3((Boolean (*)(const void *, const void *, void*))vcb->equal, dict->_values[idx], value, dict->_context))) { return true; } } @@ -749,7 +895,7 @@ Boolean CFDictionaryGetValueIfPresent(CFDictionaryRef dict, const void *key, con } else { match = __CFDictionaryFindBuckets1b(dict, key); } - return (kCFNotFound != match ? ((value ? *value = dict->_values[match] : NULL), true) : false); + return (kCFNotFound != match ? ((value ? __CFObjCStrongAssign(dict->_values[match], value) : NULL), true) : false); } bool CFDictionaryGetKeyIfPresent(CFDictionaryRef dict, const void *key, const void **actualkey) { @@ -762,13 +908,18 @@ bool CFDictionaryGetKeyIfPresent(CFDictionaryRef dict, const void *key, const vo } else { match = __CFDictionaryFindBuckets1b(dict, key); } - return (kCFNotFound != match ? ((actualkey ? *actualkey = dict->_keys[match] : NULL), true) : false); + return (kCFNotFound != match ? ((actualkey ? __CFObjCStrongAssign(dict->_keys[match], actualkey) : 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); + if (CF_USING_COLLECTABLE_MEMORY) { + // GC: speculatively issue a write-barrier on the copied to buffers (3743553). + __CFObjCWriteBarrierRange(keys, dict->_count * sizeof(void *)); + __CFObjCWriteBarrierRange(values, dict->_count * sizeof(void *)); + } nbuckets = dict->_bucketsNum; for (idx = 0; idx < nbuckets; idx++) { if (dict->_marker != (uintptr_t)dict->_keys[idx] && ~dict->_marker != (uintptr_t)dict->_keys[idx]) { @@ -802,28 +953,50 @@ static void __CFDictionaryGrow(CFMutableDictionaryRef dict, CFIndex numNewValues const void **oldvalues = dict->_values; CFIndex idx, oldnbuckets = dict->_bucketsNum; CFIndex oldCount = dict->_count; + CFAllocatorRef allocator = __CFGetAllocator(dict), keysAllocator, valuesAllocator; + void *keysBase, *valuesBase; 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 (_CFDictionaryIsSplit(dict)) { // iff GC, use split memory sometimes unscanned memory + unsigned weakOrStrong = (dict->_xflags & __kCFDictionaryWeakKeys) ? AUTO_MEMORY_UNSCANNED : AUTO_MEMORY_SCANNED; + void *mem = _CFAllocatorAllocateGC(allocator, dict->_bucketsNum * sizeof(const void *), weakOrStrong); + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, dict, dict->_keys, mem); + keysAllocator = (dict->_xflags & __kCFDictionaryWeakKeys) ? kCFAllocatorNull : allocator; // GC: avoids write-barrier in weak case. + keysBase = mem; + + weakOrStrong = (dict->_xflags & __kCFDictionaryWeakValues) ? AUTO_MEMORY_UNSCANNED : AUTO_MEMORY_SCANNED; + mem = _CFAllocatorAllocateGC(allocator, dict->_bucketsNum * sizeof(const void *), weakOrStrong); + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, dict, dict->_values, mem); + valuesAllocator = (dict->_xflags & __kCFDictionaryWeakValues) ? kCFAllocatorNull : allocator; // GC: avoids write-barrier in weak case. + valuesBase = mem; + } else { + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, dict, dict->_keys, _CFAllocatorAllocateGC(allocator, 2 * dict->_bucketsNum * sizeof(const void *), AUTO_MEMORY_SCANNED)); + dict->_values = (const void **)(dict->_keys + dict->_bucketsNum); + keysAllocator = valuesAllocator = allocator; + keysBase = valuesBase = dict->_keys; + } + if (NULL == dict->_keys || NULL == dict->_values) HALT; if (__CFOASafe) __CFSetLastAllocationEventName(dict->_keys, "CFDictionary (store)"); for (idx = dict->_bucketsNum; idx--;) { - dict->_keys[idx] = (const void *)dict->_marker; + dict->_keys[idx] = (const void *)dict->_marker; + dict->_values[idx] = 0; } 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]; - } + 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]); + if (kCFNotFound != nomatch) { + CF_WRITE_BARRIER_BASE_ASSIGN(keysAllocator, keysBase, dict->_keys[nomatch], oldkeys[idx]); + CF_WRITE_BARRIER_BASE_ASSIGN(valuesAllocator, valuesBase, dict->_values[nomatch], oldvalues[idx]); + } + } } CFAssert1(dict->_count == oldCount, __kCFLogAssertion, "%s(): dict count differs after rehashing; error", __PRETTY_FUNCTION__); - CFAllocatorDeallocate(__CFGetAllocator(dict), oldkeys); + _CFAllocatorDeallocateGC(allocator, oldkeys); + if (_CFDictionaryIsSplit(dict)) _CFAllocatorDeallocateGC(allocator, oldvalues); } // This function is for Foundation's benefit; no one else should use it. @@ -837,10 +1010,6 @@ void _CFDictionarySetCapacity(CFMutableDictionaryRef dict, CFIndex cap) { __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; @@ -865,15 +1034,18 @@ void CFDictionaryAddValue(CFMutableDictionaryRef dict, const void *key, const vo __CFDictionaryFindBuckets2(dict, key, &match, &nomatch); if (kCFNotFound != match) { } else { + CFAllocatorRef allocator = __CFGetAllocator(dict); + CFAllocatorRef keysAllocator = (dict->_xflags & __kCFDictionaryWeakKeys) ? kCFAllocatorNull : allocator; + CFAllocatorRef valuesAllocator = (dict->_xflags & __kCFDictionaryWeakValues) ? kCFAllocatorNull : allocator; 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); + newKey = (void *)INVOKE_CALLBACK3(((const void *(*)(CFAllocatorRef, const void *, void *))cb->retain), allocator, 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); + newValue = (void *)INVOKE_CALLBACK3(((const void *(*)(CFAllocatorRef, const void *, void *))vcb->retain), allocator, value, dict->_context); } else { newValue = value; } @@ -881,8 +1053,8 @@ void CFDictionaryAddValue(CFMutableDictionaryRef dict, const void *key, const vo __CFDictionaryFindNewMarker(dict); } CF_OBJC_KVO_WILLCHANGE(dict, key); - dict->_keys[nomatch] = newKey; - dict->_values[nomatch] = newValue; + CF_WRITE_BARRIER_ASSIGN(keysAllocator, dict->_keys[nomatch], newKey); + CF_WRITE_BARRIER_ASSIGN(valuesAllocator, dict->_values[nomatch], newValue); dict->_count++; CF_OBJC_KVO_DIDCHANGE(dict, key); } @@ -892,6 +1064,7 @@ void CFDictionaryReplaceValue(CFMutableDictionaryRef dict, const void *key, cons CFIndex match; const CFDictionaryValueCallBacks *vcb; const void *newValue; + CFAllocatorRef allocator, valuesAllocator; CF_OBJC_FUNCDISPATCH2(__kCFDictionaryTypeID, void, dict, "_replaceObject:forKey:", value, key); __CFGenericValidateType(dict, __kCFDictionaryTypeID); switch (__CFDictionaryGetType(dict)) { @@ -910,16 +1083,18 @@ void CFDictionaryReplaceValue(CFMutableDictionaryRef dict, const void *key, cons } if (kCFNotFound == match) return; vcb = __CFDictionaryGetValueCallBacks(dict); + allocator = __CFGetAllocator(dict); + valuesAllocator = (dict->_xflags & __kCFDictionaryWeakValues) ? kCFAllocatorNull : allocator; if (vcb->retain) { - newValue = (void *)INVOKE_CALLBACK3(((const void *(*)(CFAllocatorRef, const void *, void *))vcb->retain), __CFGetAllocator(dict), value, dict->_context); + newValue = (void *)INVOKE_CALLBACK3(((const void *(*)(CFAllocatorRef, const void *, void *))vcb->retain), allocator, 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); + INVOKE_CALLBACK3(((void (*)(CFAllocatorRef, const void *, void *))vcb->release), allocator, dict->_values[match], dict->_context); } - dict->_values[match] = newValue; + CF_WRITE_BARRIER_ASSIGN(valuesAllocator, dict->_values[match], newValue); CF_OBJC_KVO_DIDCHANGE(dict, key); } @@ -928,6 +1103,7 @@ void CFDictionarySetValue(CFMutableDictionaryRef dict, const void *key, const vo const CFDictionaryKeyCallBacks *cb; const CFDictionaryValueCallBacks *vcb; const void *newKey, *newValue; + CFAllocatorRef allocator, keysAllocator, valuesAllocator; CF_OBJC_FUNCDISPATCH2(__kCFDictionaryTypeID, void, dict, "setObject:forKey:", value, key); __CFGenericValidateType(dict, __kCFDictionaryTypeID); switch (__CFDictionaryGetType(dict)) { @@ -944,23 +1120,26 @@ void CFDictionarySetValue(CFMutableDictionaryRef dict, const void *key, const vo } __CFDictionaryFindBuckets2(dict, key, &match, &nomatch); vcb = __CFDictionaryGetValueCallBacks(dict); + allocator = __CFGetAllocator(dict); + keysAllocator = (dict->_xflags & __kCFDictionaryWeakKeys) ? kCFAllocatorNull : allocator; + valuesAllocator = (dict->_xflags & __kCFDictionaryWeakValues) ? kCFAllocatorNull : allocator; if (vcb->retain) { - newValue = (void *)INVOKE_CALLBACK3(((const void *(*)(CFAllocatorRef, const void *, void *))vcb->retain), __CFGetAllocator(dict), value, dict->_context); + newValue = (void *)INVOKE_CALLBACK3(((const void *(*)(CFAllocatorRef, const void *, void *))vcb->retain), allocator, 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); + INVOKE_CALLBACK3(((void (*)(CFAllocatorRef, const void *, void *))vcb->release), allocator, dict->_values[match], dict->_context); } - dict->_values[match] = newValue; + CF_WRITE_BARRIER_ASSIGN(valuesAllocator, 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); + newKey = (void *)INVOKE_CALLBACK3(((const void *(*)(CFAllocatorRef, const void *, void *))cb->retain), allocator, key, dict->_context); } else { newKey = key; } @@ -968,8 +1147,8 @@ void CFDictionarySetValue(CFMutableDictionaryRef dict, const void *key, const vo __CFDictionaryFindNewMarker(dict); } CF_OBJC_KVO_WILLCHANGE(dict, key); - dict->_keys[nomatch] = newKey; - dict->_values[nomatch] = newValue; + CF_WRITE_BARRIER_ASSIGN(keysAllocator, dict->_keys[nomatch], newKey); + CF_WRITE_BARRIER_ASSIGN(valuesAllocator, dict->_values[nomatch], newValue); dict->_count++; CF_OBJC_KVO_DIDCHANGE(dict, key); } @@ -1000,11 +1179,13 @@ void CFDictionaryRemoveValue(CFMutableDictionaryRef dict, const void *key) { cb = __CFDictionaryGetKeyCallBacks(dict); vcb = __CFDictionaryGetValueCallBacks(dict); const void *oldkey = dict->_keys[match]; + CFAllocatorRef allocator = CFGetAllocator(dict); 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); + INVOKE_CALLBACK3(((void (*)(CFAllocatorRef, const void *, void *))vcb->release), allocator, dict->_values[match], dict->_context); } dict->_keys[match] = (const void *)~dict->_marker; + dict->_values[match] = 0; dict->_count--; CF_OBJC_KVO_DIDCHANGE(dict, oldkey); if (cb->release) { @@ -1070,6 +1251,7 @@ void CFDictionaryRemoveAllValues(CFMutableDictionaryRef dict) { INVOKE_CALLBACK3(((void (*)(CFAllocatorRef, const void *, void *))vcb->release), allocator, dict->_values[idx], dict->_context); } keys[idx] = (const void *)~dict->_marker; + dict->_values[idx] = 0; dict->_count--; CF_OBJC_KVO_DIDCHANGE(dict, oldkey); if (cb->release) { @@ -1077,8 +1259,10 @@ void CFDictionaryRemoveAllValues(CFMutableDictionaryRef dict) { } } } + // XXX need memset here for (idx = 0; idx < nbuckets; idx++) { keys[idx] = (const void *)dict->_marker; + dict->_values[idx] = 0; } dict->_count = 0; dict->_deletes = 0; diff --git a/Collections.subproj/CFDictionary.h b/Collections.subproj/CFDictionary.h index 16a4727..86a0a75 100644 --- a/Collections.subproj/CFDictionary.h +++ b/Collections.subproj/CFDictionary.h @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -23,7 +21,7 @@ * @APPLE_LICENSE_HEADER_END@ */ /* CFDictionary.h - Copyright (c) 1998-2003, Apple, Inc. All rights reserved. + Copyright (c) 1998-2005, Apple, Inc. All rights reserved. */ /*! diff --git a/Collections.subproj/CFSet.c b/Collections.subproj/CFSet.c index 5984c52..7c58ff7 100644 --- a/Collections.subproj/CFSet.c +++ b/Collections.subproj/CFSet.c @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -34,15 +32,14 @@ const CFSetCallBacks kCFTypeSetCallBacks = {0, __CFTypeCollectionRetain, __CFTyp 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 +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, @@ -64,47 +61,47 @@ CF_INLINE CFIndex __CFSetNumBucketsForCapacity(CFIndex capacity) { } enum { /* Bits 1-0 */ - __kCFSetImmutable = 0, /* unchangable and fixed capacity */ + __kCFSetImmutable = 0, /* unchangable and fixed capacity */ __kCFSetMutable = 1, /* changeable and variable capacity */ - __kCFSetFixedMutable = 3 /* changeable and fixed capacity */ + __kCFSetFixedMutable = 3 /* changeable and fixed capacity */ }; -enum { /* Bits 3-2 */ +enum { /* Bits 5-4 (value), 3-2 (key) */ __kCFSetHasNullCallBacks = 0, __kCFSetHasCFTypeCallBacks = 1, __kCFSetHasCustomCallBacks = 3 /* callbacks are at end of header */ }; -struct __CFSetBucket { - const void *_key; +// Under GC, we fudge the key/value memory in two ways +// First, if we had null callbacks or null for both retain/release, we use unscanned memory +// This means that if people were doing addValue:[xxx new] and never removing, well, that doesn't work +// +// Second, if we notice standard retain/release implementations we substitute scanned memory +// and zero out the retain/release callbacks. This is fine, but when copying we need to restore them + +enum { + __kCFSetRestoreKeys = (1 << 0), + __kCFSetRestoreValues = (1 << 1), + __kCFSetRestoreStringKeys = (1 << 2), + __kCFSetWeakKeys = (1 << 3) }; 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; + uintptr_t _marker; void *_context; /* private */ - struct __CFSetBucket *_buckets; /* can be NULL if not allocated yet */ + CFIndex _deletes; + CFOptionFlags _xflags; /* bits for GC */ + const void **_keys; /* 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 */ +/* 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 __CFSetGetType(CFSetRef set) { return __CFBitfieldGetValue(((const CFRuntimeBase *)set)->_info, 1, 0); @@ -118,7 +115,7 @@ CF_INLINE CFIndex __CFSetGetSizeOfType(CFIndex t) { return size; } -CF_INLINE const CFSetCallBacks *__CFSetGetCallBacks(CFSetRef set) { +CF_INLINE const CFSetCallBacks *__CFSetGetKeyCallBacks(CFSetRef set) { CFSetCallBacks *result = NULL; switch (__CFBitfieldGetValue(((const CFRuntimeBase *)set)->_info, 3, 2)) { case __kCFSetHasNullCallBacks: @@ -150,126 +147,151 @@ CF_INLINE bool __CFSetCallBacksMatchCFType(const CFSetCallBacks *c) { c->hash == kCFTypeSetCallBacks.hash)); } +#define CF_OBJC_KVO_WILLCHANGE(obj, sel) +#define CF_OBJC_KVO_DIDCHANGE(obj, sel) -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; +static CFIndex __CFSetFindBuckets1a(CFSetRef set, const void *key) { + CFHashCode keyHash = (CFHashCode)key; + const void **keys = set->_keys; + uintptr_t marker = set->_marker; + CFIndex probe = keyHash % set->_bucketsNum; + CFIndex probeskip = 1; // See RemoveValue() for notes before changing this value + CFIndex start = probe; for (;;) { - struct __CFSetBucket *currentBucket = buckets + probe; - if (__CFSetBucketIsEmpty(set, currentBucket)) { - return; - } else if (__CFSetBucketIsDeleted(set, currentBucket)) { + uintptr_t currKey = (uintptr_t)keys[probe]; + if (marker == currKey) { /* empty */ + return kCFNotFound; + } else if (~marker == currKey) { /* deleted */ /* 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; + } 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 (set->_bucketsNum <= probe) { + probe -= set->_bucketsNum; + } + if (start == probe) { + return kCFNotFound; } - 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; +static CFIndex __CFSetFindBuckets1b(CFSetRef set, const void *key) { + const CFSetCallBacks *cb = __CFSetGetKeyCallBacks(set); 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; + const void **keys = set->_keys; + uintptr_t marker = set->_marker; + CFIndex probe = keyHash % set->_bucketsNum; + CFIndex probeskip = 1; // See RemoveValue() for notes before changing this value + CFIndex start = probe; 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; + 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 (*)(const void *, const void *, void*))cb->equal, (void *)currKey, key, set->_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 (set->_bucketsNum <= probe) { + probe -= set->_bucketsNum; + } + if (start == probe) { + return kCFNotFound; } - 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; +static void __CFSetFindBuckets2(CFSetRef set, const void *key, CFIndex *match, CFIndex *nomatch) { + const CFSetCallBacks *cb = __CFSetGetKeyCallBacks(set); + CFHashCode keyHash = cb->hash ? (CFHashCode)INVOKE_CALLBACK2(((CFHashCode (*)(const void *, void *))cb->hash), key, set->_context) : (CFHashCode)key; + const void **keys = set->_keys; + uintptr_t marker = set->_marker; + CFIndex probe = keyHash % set->_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 (nomatch) *nomatch = probe; + return; + } else if (~marker == currKey) { /* deleted */ + if (nomatch) { + *nomatch = probe; + nomatch = NULL; } + } else if (currKey == (uintptr_t)key || (cb->equal && INVOKE_CALLBACK3((Boolean (*)(const void *, const void *, void*))cb->equal, (void *)currKey, key, set->_context))) { + *match = probe; + return; } - } while (hit); - for (idx = 0; idx < nbuckets; idx++) { - if (set->_emptyMarker == buckets[idx]._key) { - buckets[idx]._key = newEmpty; + probe = probe + probeskip; + // This alternative to probe % buckets assumes that + // probeskip is always positive and less than the + // number of buckets. + if (set->_bucketsNum <= probe) { + probe -= set->_bucketsNum; + } + if (start == probe) { + return; } } - ((struct __CFSet *)set)->_emptyMarker = newEmpty; } -static void __CFSetFindNewDeletedMarker(CFSetRef set) { - struct __CFSetBucket *buckets; - const void *newDeleted; - bool hit; +static void __CFSetFindNewMarker(CFSetRef set) { + const void **keys = set->_keys; + uintptr_t newMarker; CFIndex idx, nbuckets; - buckets = set->_buckets; + bool hit; + nbuckets = set->_bucketsNum; - newDeleted = set->_deletedMarker; + newMarker = set->_marker; do { - (intptr_t)newDeleted += 2; + newMarker--; hit = false; for (idx = 0; idx < nbuckets; idx++) { - if (newDeleted == buckets[idx]._key) { + if (newMarker == (uintptr_t)keys[idx] || ~newMarker == (uintptr_t)keys[idx]) { hit = true; break; } } } while (hit); for (idx = 0; idx < nbuckets; idx++) { - if (set->_deletedMarker == buckets[idx]._key) { - buckets[idx]._key = newDeleted; + if (set->_marker == (uintptr_t)keys[idx]) { + keys[idx] = (const void *)newMarker; + } else if (~set->_marker == (uintptr_t)keys[idx]) { + keys[idx] = (const void *)~newMarker; } } - ((struct __CFSet *)set)->_deletedMarker = newDeleted; + ((struct __CFSet *)set)->_marker = newMarker; } static bool __CFSetEqual(CFTypeRef cf1, CFTypeRef cf2) { CFSetRef set1 = (CFSetRef)cf1; CFSetRef set2 = (CFSetRef)cf2; const CFSetCallBacks *cb1, *cb2; - const struct __CFSetBucket *buckets; + const void **keys; CFIndex idx, nbuckets; if (set1 == set2) return true; if (set1->_count != set2->_count) return false; - cb1 = __CFSetGetCallBacks(set1); - cb2 = __CFSetGetCallBacks(set2); + cb1 = __CFSetGetKeyCallBacks(set1); + cb2 = __CFSetGetKeyCallBacks(set2); if (cb1->equal != cb2->equal) return false; if (0 == set1->_count) return true; /* after function comparison! */ - buckets = set1->_buckets; + keys = set1->_keys; nbuckets = set1->_bucketsNum; for (idx = 0; idx < nbuckets; idx++) { - if (__CFSetBucketIsOccupied(set1, &buckets[idx])) { - if (1 != CFSetGetCountOfValue(set2, buckets[idx]._key)) { - return false; - } + if (set1->_marker != (uintptr_t)keys[idx] && ~set1->_marker != (uintptr_t)keys[idx]) { + const void *value; + if (!CFSetGetValueIfPresent(set2, keys[idx], &value)) return false; } } return true; @@ -282,26 +304,38 @@ static CFHashCode __CFSetHash(CFTypeRef cf) { static CFStringRef __CFSetCopyDescription(CFTypeRef cf) { CFSetRef set = (CFSetRef)cf; + CFAllocatorRef allocator; const CFSetCallBacks *cb; - const struct __CFSetBucket *buckets; + const void **keys; CFIndex idx, nbuckets; CFMutableStringRef result; - cb = __CFSetGetCallBacks(set); - buckets = set->_buckets; + cb = __CFSetGetKeyCallBacks(set); + keys = set->_keys; nbuckets = set->_bucketsNum; - result = CFStringCreateMutable(kCFAllocatorSystemDefault, 0); - CFStringAppendFormat(result, NULL, CFSTR("{count = %u, capacity = %u, values = (\n"), set, CFGetAllocator(set), set->_count, set->_capacity); + allocator = CFGetAllocator(set); + result = CFStringCreateMutable(allocator, 0); + switch (__CFSetGetType(set)) { + case __kCFSetImmutable: + CFStringAppendFormat(result, NULL, CFSTR("{type = immutable, count = %u, capacity = %u, pairs = (\n"), cf, allocator, set->_count, set->_capacity); + break; + case __kCFSetFixedMutable: + CFStringAppendFormat(result, NULL, CFSTR("{type = fixed-mutable, count = %u, capacity = %u, pairs = (\n"), cf, allocator, set->_count, set->_capacity); + break; + case __kCFSetMutable: + CFStringAppendFormat(result, NULL, CFSTR("{type = mutable, count = %u, capacity = %u, pairs = (\n"), cf, allocator, set->_count, set->_capacity); + break; + } for (idx = 0; idx < nbuckets; idx++) { - if (__CFSetBucketIsOccupied(set, &buckets[idx])) { - CFStringRef desc = NULL; + if (set->_marker != (uintptr_t)keys[idx] && ~set->_marker != (uintptr_t)keys[idx]) { + CFStringRef kDesc = NULL; if (NULL != cb->copyDescription) { - desc = (CFStringRef)INVOKE_CALLBACK2(((CFStringRef (*)(const void *, void *))cb->copyDescription), buckets[idx]._key, set->_context); + kDesc = (CFStringRef)INVOKE_CALLBACK2(((CFStringRef (*)(const void *, void *))cb->copyDescription), keys[idx], set->_context); } - if (NULL != desc) { - CFStringAppendFormat(result, NULL, CFSTR("\t%u : %@\n"), idx, desc, NULL); - CFRelease(desc); + if (NULL != kDesc) { + CFStringAppendFormat(result, NULL, CFSTR("\t%u : %@\n"), idx, kDesc); + CFRelease(kDesc); } else { - CFStringAppendFormat(result, NULL, CFSTR("\t%u : <%p>\n"), idx, buckets[idx]._key, NULL); + CFStringAppendFormat(result, NULL, CFSTR("\t%u : <%p>\n"), idx, keys[idx]); } } } @@ -312,19 +346,49 @@ static CFStringRef __CFSetCopyDescription(CFTypeRef cf) { static void __CFSetDeallocate(CFTypeRef cf) { CFMutableSetRef set = (CFMutableSetRef)cf; CFAllocatorRef allocator = __CFGetAllocator(set); + if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { + const CFSetCallBacks *kcb = __CFSetGetKeyCallBacks(set); + if (kcb->retain == NULL && kcb->release == NULL) + return; // XXX_PCB keep set intact during finalization. + } if (__CFSetGetType(set) == __kCFSetImmutable) { __CFBitfieldSetValue(((CFRuntimeBase *)set)->_info, 1, 0, __kCFSetFixedMutable); } - CFSetRemoveAllValues(set); - if (__CFSetGetType(set) == __kCFSetMutable && set->_buckets) { - CFAllocatorDeallocate(allocator, set->_buckets); + + const CFSetCallBacks *cb = __CFSetGetKeyCallBacks(set); + if (cb->release) { + const void **keys = set->_keys; + CFIndex idx, nbuckets = set->_bucketsNum; + for (idx = 0; idx < nbuckets; idx++) { + if (set->_marker != (uintptr_t)keys[idx] && ~set->_marker != (uintptr_t)keys[idx]) { + const void *oldkey = keys[idx]; + INVOKE_CALLBACK3(((void (*)(CFAllocatorRef, const void *, void *))cb->release), allocator, oldkey, set->_context); + } + } + } + + if (__CFSetGetType(set) == __kCFSetMutable && set->_keys) { + _CFAllocatorDeallocateGC(allocator, set->_keys); + set->_keys = NULL; } } +/* + * When running under GC, we suss up sets with standard string copy to hold + * onto everything, including the copies of incoming keys, in strong memory without retain counts. + * This is the routine that makes that copy. + * Not for inputs of constant strings we'll get a constant string back, and so the result + * is not guaranteed to be from the auto zone, hence the call to CFRelease since it will figure + * out where the refcount really is. + */ +static CFStringRef _CFStringCreateCopyCollected(CFAllocatorRef allocator, CFStringRef theString) { + return CFMakeCollectable(CFStringCreateCopy(NULL, theString)); +} + static CFTypeID __kCFSetTypeID = _kCFRuntimeNotATypeID; static const CFRuntimeClass __CFSetClass = { - 0, + _kCFRuntimeScannedObject, "CFSet", NULL, // init NULL, // copy @@ -343,14 +407,40 @@ CFTypeID CFSetGetTypeID(void) { return __kCFSetTypeID; } -static CFSetRef __CFSetInit(CFAllocatorRef allocator, UInt32 flags, CFIndex capacity, const CFSetCallBacks *callBacks) { +static CFSetRef __CFSetInit(CFAllocatorRef allocator, uint32_t flags, CFIndex capacity, const CFSetCallBacks *keyCallBacks) { struct __CFSet *memory; - UInt32 size; + uint32_t size; CFIndex idx; __CFBitfieldSetValue(flags, 31, 2, 0); - if (__CFSetCallBacksMatchNull(callBacks)) { + CFSetCallBacks nonRetainingKeyCallbacks; + CFOptionFlags xflags = 0; + if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { + // preserve NULL for key or value CB, otherwise fix up. + if (!keyCallBacks || (keyCallBacks->retain == NULL && keyCallBacks->release == NULL)) { + xflags = __kCFSetWeakKeys; + } + else { + if (keyCallBacks->retain == __CFTypeCollectionRetain && keyCallBacks->release == __CFTypeCollectionRelease) { + // copy everything + nonRetainingKeyCallbacks = *keyCallBacks; + nonRetainingKeyCallbacks.retain = NULL; + nonRetainingKeyCallbacks.release = NULL; + keyCallBacks = &nonRetainingKeyCallbacks; + xflags = __kCFSetRestoreKeys; + } + else if (keyCallBacks->retain == CFStringCreateCopy && keyCallBacks->release == __CFTypeCollectionRelease) { + // copy everything + nonRetainingKeyCallbacks = *keyCallBacks; + nonRetainingKeyCallbacks.retain = (void *)_CFStringCreateCopyCollected; // XXX fix with better cast + nonRetainingKeyCallbacks.release = NULL; + keyCallBacks = &nonRetainingKeyCallbacks; + xflags = (__kCFSetRestoreKeys | __kCFSetRestoreStringKeys); + } + } + } + if (__CFSetCallBacksMatchNull(keyCallBacks)) { __CFBitfieldSetValue(flags, 3, 2, __kCFSetHasNullCallBacks); - } else if (__CFSetCallBacksMatchCFType(callBacks)) { + } else if (__CFSetCallBacksMatchCFType(keyCallBacks)) { __CFBitfieldSetValue(flags, 3, 2, __kCFSetHasCFTypeCallBacks); } else { __CFBitfieldSetValue(flags, 3, 2, __kCFSetHasCustomCallBacks); @@ -359,10 +449,10 @@ static CFSetRef __CFSetInit(CFAllocatorRef allocator, UInt32 flags, CFIndex capa switch (__CFBitfieldGetValue(flags, 1, 0)) { case __kCFSetImmutable: case __kCFSetFixedMutable: - size += __CFSetNumBucketsForCapacity(capacity) * sizeof(struct __CFSetBucket); - break; + size += __CFSetNumBucketsForCapacity(capacity) * sizeof(const void *); + break; case __kCFSetMutable: - break; + break; } memory = (struct __CFSet *)_CFRuntimeCreateInstance(allocator, __kCFSetTypeID, size, NULL); if (NULL == memory) { @@ -370,39 +460,45 @@ static CFSetRef __CFSetInit(CFAllocatorRef allocator, UInt32 flags, CFIndex capa } __CFBitfieldSetValue(memory->_base._info, 6, 0, flags); memory->_count = 0; - memory->_bucketsUsed = 0; - memory->_emptyMarker = (const void *)0xa1b1c1d3; - memory->_deletedMarker = (const void *)0xa1b1c1d5; + memory->_marker = (uintptr_t)0xa1b1c1d3; memory->_context = NULL; + memory->_deletes = 0; + memory->_xflags = xflags; switch (__CFBitfieldGetValue(flags, 1, 0)) { case __kCFSetImmutable: - if (__CFOASafe) __CFSetLastAllocationEventName(memory, "CFSet (immutable)"); + if (CF_IS_COLLECTABLE_ALLOCATOR(allocator) && (xflags & __kCFSetWeakKeys)) { // if weak, don't scan + auto_zone_set_layout_type(__CFCollectableZone, memory, AUTO_OBJECT_UNSCANNED); + } + 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)); + memory->_keys = (const void **)((uint8_t *)memory + __CFSetGetSizeOfType(flags)); for (idx = memory->_bucketsNum; idx--;) { - memory->_buckets[idx]._key = memory->_emptyMarker; + memory->_keys[idx] = (const void *)memory->_marker; } break; case __kCFSetFixedMutable: + if (CF_IS_COLLECTABLE_ALLOCATOR(allocator) && (xflags & __kCFSetWeakKeys)) { // if weak, don't scan + auto_zone_set_layout_type(__CFCollectableZone, memory, AUTO_OBJECT_UNSCANNED); + } 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)); + memory->_keys = (const void **)((uint8_t *)memory + __CFSetGetSizeOfType(flags)); for (idx = memory->_bucketsNum; idx--;) { - memory->_buckets[idx]._key = memory->_emptyMarker; + memory->_keys[idx] = (const void *)memory->_marker; } break; case __kCFSetMutable: if (__CFOASafe) __CFSetLastAllocationEventName(memory, "CFSet (mutable-variable)"); memory->_capacity = __CFSetRoundUpCapacity(1); memory->_bucketsNum = 0; - memory->_buckets = NULL; + memory->_keys = NULL; break; } if (__kCFSetHasCustomCallBacks == __CFBitfieldGetValue(flags, 3, 2)) { - const CFSetCallBacks *cb = __CFSetGetCallBacks((CFSetRef)memory); - *(CFSetCallBacks *)cb = *callBacks; + CFSetCallBacks *cb = (CFSetCallBacks *)__CFSetGetKeyCallBacks((CFSetRef)memory); + *cb = *keyCallBacks; FAULT_CALLBACK((void **)&(cb->retain)); FAULT_CALLBACK((void **)&(cb->release)); FAULT_CALLBACK((void **)&(cb->copyDescription)); @@ -412,26 +508,71 @@ static CFSetRef __CFSetInit(CFAllocatorRef allocator, UInt32 flags, CFIndex capa return (CFSetRef)memory; } -CFSetRef CFSetCreate(CFAllocatorRef allocator, const void **values, CFIndex numValues, const CFSetCallBacks *callBacks) { +CFSetRef CFSetCreate(CFAllocatorRef allocator, const void **keys, CFIndex numValues, const CFSetCallBacks *keyCallBacks) { CFSetRef result; - UInt32 flags; + uint32_t flags; CFIndex idx; CFAssert2(0 <= numValues, __kCFLogAssertion, "%s(): numValues (%d) cannot be less than zero", __PRETTY_FUNCTION__, numValues); - result = __CFSetInit(allocator, __kCFSetImmutable, numValues, callBacks); + result = __CFSetInit(allocator, __kCFSetImmutable, numValues, keyCallBacks); flags = __CFBitfieldGetValue(((const CFRuntimeBase *)result)->_info, 1, 0); if (flags == __kCFSetImmutable) { + // tweak flags so that we can add our immutable values __CFBitfieldSetValue(((CFRuntimeBase *)result)->_info, 1, 0, __kCFSetFixedMutable); } for (idx = 0; idx < numValues; idx++) { - CFSetAddValue((CFMutableSetRef)result, values[idx]); + CFSetAddValue((CFMutableSetRef)result, keys[idx]); } __CFBitfieldSetValue(((CFRuntimeBase *)result)->_info, 1, 0, flags); return result; } -CFMutableSetRef CFSetCreateMutable(CFAllocatorRef allocator, CFIndex capacity, const CFSetCallBacks *callBacks) { +CFMutableSetRef CFSetCreateMutable(CFAllocatorRef allocator, CFIndex capacity, const CFSetCallBacks *keyCallBacks) { 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); + return (CFMutableSetRef)__CFSetInit(allocator, (0 == capacity) ? __kCFSetMutable : __kCFSetFixedMutable, capacity, keyCallBacks); +} + + +static void __CFSetGrow(CFMutableSetRef set, CFIndex numNewValues); + +// This creates a set which is for CFTypes or NSObjects, with an ownership transfer -- +// the set does not take a retain, and the caller does not need to release the inserted objects. +// The incoming objects must also be collectable if allocated out of a collectable allocator. +CFSetRef _CFSetCreate_ex(CFAllocatorRef allocator, bool mutable, const void **keys, CFIndex numValues) { + CFSetRef result; + void *bucketsBase; + uint32_t flags; + CFIndex idx; + result = __CFSetInit(allocator, mutable ? __kCFSetMutable : __kCFSetImmutable, numValues, &kCFTypeSetCallBacks); + flags = __CFBitfieldGetValue(((const CFRuntimeBase *)result)->_info, 1, 0); + if (!mutable) { + __CFBitfieldSetValue(((CFRuntimeBase *)result)->_info, 1, 0, __kCFSetFixedMutable); + } + if (mutable) { + if (result->_count == result->_capacity || NULL == result->_keys) { + __CFSetGrow((CFMutableSetRef)result, numValues); + } + } + // GC: since kCFTypeSetCallBacks are used, the keys + // and values will be allocated contiguously. + bool collectableContainer = CF_IS_COLLECTABLE_ALLOCATOR(allocator); + bucketsBase = (collectableContainer ? (void *)auto_zone_base_pointer(__CFCollectableZone, result->_keys) : NULL); + for (idx = 0; idx < numValues; idx++) { + CFIndex match, nomatch; + const void *newKey; + __CFSetFindBuckets2(result, keys[idx], &match, &nomatch); + if (kCFNotFound != match) { + } else { + newKey = keys[idx]; + if (result->_marker == (uintptr_t)newKey || ~result->_marker == (uintptr_t)newKey) { + __CFSetFindNewMarker(result); + } + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, bucketsBase, result->_keys[nomatch], newKey); + // GC: generation(_keys/_values) <= generation(keys/values), but added for completeness. + ((CFMutableSetRef)result)->_count++; + } + } + __CFBitfieldSetValue(((CFRuntimeBase *)result)->_info, 1, 0, flags); + return result; } CFSetRef CFSetCreateCopy(CFAllocatorRef allocator, CFSetRef set) { @@ -439,12 +580,26 @@ CFSetRef CFSetCreateCopy(CFAllocatorRef allocator, CFSetRef set) { const CFSetCallBacks *cb; CFIndex numValues = CFSetGetCount(set); const void **list, *buffer[256]; - list = (numValues <= 256) ? buffer : CFAllocatorAllocate(allocator, numValues * sizeof(void *), 0); + list = (numValues <= 256) ? buffer : CFAllocatorAllocate(allocator, numValues * sizeof(void *), 0); // XXX_PCB GC OK if (list != buffer && __CFOASafe) __CFSetLastAllocationEventName(list, "CFSet (temp)"); CFSetGetValues(set, list); - cb = CF_IS_OBJC(__kCFSetTypeID, set) ? &kCFTypeSetCallBacks : __CFSetGetCallBacks(set); + CFSetCallBacks patchedKeyCB; + if (CF_IS_OBJC(__kCFSetTypeID, set)) { + cb = &kCFTypeSetCallBacks; + } + else { + cb = __CFSetGetKeyCallBacks(set); + if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { + if (set->_xflags & __kCFSetRestoreKeys) { + patchedKeyCB = *cb; // copy + cb = &patchedKeyCB; // reset to copy + patchedKeyCB.retain = (set->_xflags & __kCFSetRestoreStringKeys) ? CFStringCreateCopy : __CFTypeCollectionRetain; + patchedKeyCB.release = __CFTypeCollectionRelease; + } + } + } result = CFSetCreate(allocator, list, numValues, cb); - if (list != buffer) CFAllocatorDeallocate(allocator, list); + if (list != buffer) CFAllocatorDeallocate(allocator, list); // GC OK return result; } @@ -454,21 +609,41 @@ CFMutableSetRef CFSetCreateMutableCopy(CFAllocatorRef allocator, CFIndex capacit 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); + list = (numValues <= 256) ? buffer : CFAllocatorAllocate(allocator, numValues * sizeof(void *), 0); // XXX_PCB GC OK if (list != buffer && __CFOASafe) __CFSetLastAllocationEventName(list, "CFSet (temp)"); CFSetGetValues(set, list); - cb = CF_IS_OBJC(__kCFSetTypeID, set) ? &kCFTypeSetCallBacks : __CFSetGetCallBacks(set); + CFSetCallBacks patchedKeyCB; + if (CF_IS_OBJC(__kCFSetTypeID, set)) { + cb = &kCFTypeSetCallBacks; + } + else { + cb = __CFSetGetKeyCallBacks(set); + if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { + if (set->_xflags & __kCFSetRestoreKeys) { + patchedKeyCB = *cb; // copy + cb = &patchedKeyCB; // reset to copy + patchedKeyCB.retain = (set->_xflags & __kCFSetRestoreStringKeys) ? CFStringCreateCopy : __CFTypeCollectionRetain; + patchedKeyCB.release = __CFTypeCollectionRelease; + } + } + } 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); + if (list != buffer) CFAllocatorDeallocate(allocator, list); // XXX_PCB GC OK return result; } +// Used by NSHashTables and KVO void _CFSetSetContext(CFSetRef set, void *context) { - ((struct __CFSet *)set)->_context = context; + CFAllocatorRef allocator = CFGetAllocator(set); + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, set, set->_context, context); +} + +void *_CFSetGetContext(CFSetRef set) { + return ((struct __CFSet *)set)->_context; } CFIndex CFSetGetCount(CFSetRef set) { @@ -477,98 +652,125 @@ CFIndex CFSetGetCount(CFSetRef set) { return set->_count; } -CFIndex CFSetGetCountOfValue(CFSetRef set, const void *value) { - struct __CFSetBucket *match; - CF_OBJC_FUNCDISPATCH1(__kCFSetTypeID, CFIndex, set, "countForObject:", value); +CFIndex CFSetGetCountOfValue(CFSetRef set, const void *key) { + CFIndex match; + CF_OBJC_FUNCDISPATCH1(__kCFSetTypeID, CFIndex, set, "countForObject:", key); __CFGenericValidateType(set, __kCFSetTypeID); if (0 == set->_count) return 0; - __CFSetFindBuckets1(set, value, &match); - return (match ? 1 : 0); + if (__kCFSetHasNullCallBacks == __CFBitfieldGetValue(((const CFRuntimeBase *)set)->_info, 3, 2)) { + match = __CFSetFindBuckets1a(set, key); + } else { + match = __CFSetFindBuckets1b(set, key); + } + return (kCFNotFound != match ? 1 : 0); } -Boolean CFSetContainsValue(CFSetRef set, const void *value) { - struct __CFSetBucket *match; - CF_OBJC_FUNCDISPATCH1(__kCFSetTypeID, char, set, "containsObject:", value); +Boolean CFSetContainsValue(CFSetRef set, const void *key) { + CFIndex match; + CF_OBJC_FUNCDISPATCH1(__kCFSetTypeID, char, set, "containsObject:", key); __CFGenericValidateType(set, __kCFSetTypeID); if (0 == set->_count) return false; - __CFSetFindBuckets1(set, value, &match); - return (match ? true : false); + if (__kCFSetHasNullCallBacks == __CFBitfieldGetValue(((const CFRuntimeBase *)set)->_info, 3, 2)) { + match = __CFSetFindBuckets1a(set, key); + } else { + match = __CFSetFindBuckets1b(set, key); + } + return (kCFNotFound != match ? true : false); } -const void *CFSetGetValue(CFSetRef set, const void *value) { - struct __CFSetBucket *match; - CF_OBJC_FUNCDISPATCH1(__kCFSetTypeID, const void *, set, "member:", value); +const void *CFSetGetValue(CFSetRef set, const void *key) { + CFIndex match; + CF_OBJC_FUNCDISPATCH1(__kCFSetTypeID, const void *, set, "member:", key); __CFGenericValidateType(set, __kCFSetTypeID); if (0 == set->_count) return NULL; - __CFSetFindBuckets1(set, value, &match); - return (match ? match->_key : NULL); + if (__kCFSetHasNullCallBacks == __CFBitfieldGetValue(((const CFRuntimeBase *)set)->_info, 3, 2)) { + match = __CFSetFindBuckets1a(set, key); + } else { + match = __CFSetFindBuckets1b(set, key); + } + return (kCFNotFound != match ? set->_keys[match] : 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); +Boolean CFSetGetValueIfPresent(CFSetRef set, const void *key, const void **actualkey) { + CFIndex match; + CF_OBJC_FUNCDISPATCH2(__kCFSetTypeID, char, set, "_getValue:forObj:", (void * *) actualkey, key); __CFGenericValidateType(set, __kCFSetTypeID); if (0 == set->_count) return false; - __CFSetFindBuckets1(set, candidate, &match); - return (match ? ((value ? *value = match->_key : NULL), true) : false); + if (__kCFSetHasNullCallBacks == __CFBitfieldGetValue(((const CFRuntimeBase *)set)->_info, 3, 2)) { + match = __CFSetFindBuckets1a(set, key); + } else { + match = __CFSetFindBuckets1b(set, key); + } + return (kCFNotFound != match ? ((actualkey ? __CFObjCStrongAssign(set->_keys[match], actualkey) : NULL), true) : false); } -void CFSetGetValues(CFSetRef set, const void **values) { - struct __CFSetBucket *buckets; +void CFSetGetValues(CFSetRef set, const void **keys) { CFIndex idx, cnt, nbuckets; - CF_OBJC_FUNCDISPATCH1(__kCFSetTypeID, void, set, "getObjects:", (void * *)values); + CF_OBJC_FUNCDISPATCH1(__kCFSetTypeID, void, set, "getObjects:", (void * *) keys); __CFGenericValidateType(set, __kCFSetTypeID); - buckets = set->_buckets; + if (CF_USING_COLLECTABLE_MEMORY) { + // GC: speculatively issue a write-barrier on the copied to buffers (3743553). + __CFObjCWriteBarrierRange(keys, set->_count * sizeof(void *)); + } nbuckets = set->_bucketsNum; for (idx = 0; idx < nbuckets; idx++) { - if (__CFSetBucketIsOccupied(set, &buckets[idx])) { + if (set->_marker != (uintptr_t)set->_keys[idx] && ~set->_marker != (uintptr_t)set->_keys[idx]) { for (cnt = 1; cnt--;) { - if (values) *values++ = buckets[idx]._key; + if (keys) CF_WRITE_BARRIER_ASSIGN(NULL, *keys++, set->_keys[idx]); } } } } void CFSetApplyFunction(CFSetRef set, CFSetApplierFunction applier, void *context) { - struct __CFSetBucket *buckets; + const void **keys; CFIndex idx, cnt, nbuckets; FAULT_CALLBACK((void **)&(applier)); CF_OBJC_FUNCDISPATCH2(__kCFSetTypeID, void, set, "_applyValues:context:", applier, context); __CFGenericValidateType(set, __kCFSetTypeID); - buckets = set->_buckets; + keys = set->_keys; nbuckets = set->_bucketsNum; for (idx = 0; idx < nbuckets; idx++) { - if (__CFSetBucketIsOccupied(set, &buckets[idx])) { + if (set->_marker != (uintptr_t)keys[idx] && ~set->_marker != (uintptr_t)keys[idx]) { for (cnt = 1; cnt--;) { - INVOKE_CALLBACK2(applier, buckets[idx]._key, context); + INVOKE_CALLBACK2(applier, keys[idx], context); } } } } + static void __CFSetGrow(CFMutableSetRef set, CFIndex numNewValues) { - struct __CFSetBucket *oldbuckets = set->_buckets; + const void **oldkeys = set->_keys; CFIndex idx, oldnbuckets = set->_bucketsNum; CFIndex oldCount = set->_count; + CFAllocatorRef allocator = __CFGetAllocator(set), keysAllocator; + void *keysBase; 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)"); + set->_deletes = 0; + void *buckets = _CFAllocatorAllocateGC(allocator, set->_bucketsNum * sizeof(const void *), (set->_xflags & __kCFSetWeakKeys) ? AUTO_MEMORY_UNSCANNED : AUTO_MEMORY_SCANNED); + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, set, set->_keys, buckets); + keysAllocator = allocator; + keysBase = set->_keys; + if (NULL == set->_keys) HALT; + if (__CFOASafe) __CFSetLastAllocationEventName(set->_keys, "CFSet (store)"); for (idx = set->_bucketsNum; idx--;) { - set->_buckets[idx]._key = set->_emptyMarker; + set->_keys[idx] = (const void *)set->_marker; } - if (NULL == oldbuckets) return; + if (NULL == oldkeys) 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; - } + if (set->_marker != (uintptr_t)oldkeys[idx] && ~set->_marker != (uintptr_t)oldkeys[idx]) { + CFIndex match, nomatch; + __CFSetFindBuckets2(set, 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], set->_keys[match]); + if (kCFNotFound != nomatch) { + CF_WRITE_BARRIER_BASE_ASSIGN(keysAllocator, keysBase, set->_keys[nomatch], oldkeys[idx]); + } + } } CFAssert1(set->_count == oldCount, __kCFLogAssertion, "%s(): set count differs after rehashing; error", __PRETTY_FUNCTION__); - CFAllocatorDeallocate(__CFGetAllocator(set), oldbuckets); + _CFAllocatorDeallocateGC(allocator, oldkeys); } // This function is for Foundation's benefit; no one else should use it. @@ -582,20 +784,16 @@ void _CFSetSetCapacity(CFMutableSetRef set, CFIndex cap) { __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; +void CFSetAddValue(CFMutableSetRef set, const void *key) { + CFIndex match, nomatch; const CFSetCallBacks *cb; - const void *newValue; - CF_OBJC_FUNCDISPATCH1(__kCFSetTypeID, void, set, "addObject:", value); + const void *newKey; + CF_OBJC_FUNCDISPATCH1(__kCFSetTypeID, void, set, "addObject:", key); __CFGenericValidateType(set, __kCFSetTypeID); switch (__CFSetGetType(set)) { case __kCFSetMutable: - if (set->_bucketsUsed == set->_capacity || NULL == set->_buckets) { + if (set->_count == set->_capacity || NULL == set->_keys) { __CFSetGrow(set, 1); } break; @@ -606,36 +804,36 @@ void CFSetAddValue(CFMutableSetRef set, const void *value) { CFAssert2(__CFSetGetType(set) != __kCFSetImmutable, __kCFLogAssertion, "%s(): immutable set %p passed to mutating operation", __PRETTY_FUNCTION__, set); break; } - __CFSetFindBuckets2(set, value, &match, &nomatch); - if (match) { + __CFSetFindBuckets2(set, key, &match, &nomatch); + if (kCFNotFound != match) { } else { - cb = __CFSetGetCallBacks(set); + CFAllocatorRef allocator = __CFGetAllocator(set); + CFAllocatorRef keysAllocator = (set->_xflags & __kCFSetWeakKeys) ? kCFAllocatorNull : allocator; + cb = __CFSetGetKeyCallBacks(set); if (cb->retain) { - newValue = (void *)INVOKE_CALLBACK3(((const void *(*)(CFAllocatorRef, const void *, void *))cb->retain), __CFGetAllocator(set), value, set->_context); + newKey = (void *)INVOKE_CALLBACK3(((const void *(*)(CFAllocatorRef, const void *, void *))cb->retain), allocator, key, set->_context); } else { - newValue = value; + newKey = key; } - if (set->_emptyMarker == newValue) { - __CFSetFindNewEmptyMarker(set); + if (set->_marker == (uintptr_t)newKey || ~set->_marker == (uintptr_t)newKey) { + __CFSetFindNewMarker(set); } - if (set->_deletedMarker == newValue) { - __CFSetFindNewDeletedMarker(set); - } - nomatch->_key = newValue; - set->_bucketsUsed++; + CF_OBJC_KVO_WILLCHANGE(set, key); + CF_WRITE_BARRIER_ASSIGN(keysAllocator, set->_keys[nomatch], newKey); set->_count++; + CF_OBJC_KVO_DIDCHANGE(set, key); } } -__private_extern__ const void *__CFSetAddValueAndReturn(CFMutableSetRef set, const void *value) { - struct __CFSetBucket *match, *nomatch; +__private_extern__ const void *__CFSetAddValueAndReturn(CFMutableSetRef set, const void *key) { + CFIndex match, nomatch; const CFSetCallBacks *cb; - const void *newValue; + const void *newKey; // #warning not toll-free bridged, but internal __CFGenericValidateType(set, __kCFSetTypeID); switch (__CFSetGetType(set)) { case __kCFSetMutable: - if (set->_bucketsUsed == set->_capacity || NULL == set->_buckets) { + if (set->_count == set->_capacity || NULL == set->_keys) { __CFSetGrow(set, 1); } break; @@ -646,34 +844,35 @@ __private_extern__ const void *__CFSetAddValueAndReturn(CFMutableSetRef set, con 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; + __CFSetFindBuckets2(set, key, &match, &nomatch); + if (kCFNotFound != match) { + return set->_keys[match]; } else { - cb = __CFSetGetCallBacks(set); + CFAllocatorRef allocator = __CFGetAllocator(set); + CFAllocatorRef keysAllocator = (set->_xflags & __kCFSetWeakKeys) ? kCFAllocatorNull : allocator; + cb = __CFSetGetKeyCallBacks(set); if (cb->retain) { - newValue = (void *)INVOKE_CALLBACK3(((const void *(*)(CFAllocatorRef, const void *, void *))cb->retain), __CFGetAllocator(set), value, set->_context); + newKey = (void *)INVOKE_CALLBACK3(((const void *(*)(CFAllocatorRef, const void *, void *))cb->retain), allocator, key, set->_context); } else { - newValue = value; - } - if (set->_emptyMarker == newValue) { - __CFSetFindNewEmptyMarker(set); + newKey = key; } - if (set->_deletedMarker == newValue) { - __CFSetFindNewDeletedMarker(set); + if (set->_marker == (uintptr_t)newKey || ~set->_marker == (uintptr_t)newKey) { + __CFSetFindNewMarker(set); } - nomatch->_key = newValue; - set->_bucketsUsed++; + CF_OBJC_KVO_WILLCHANGE(set, key); + CF_WRITE_BARRIER_ASSIGN(keysAllocator, set->_keys[nomatch], newKey); set->_count++; - return newValue; + CF_OBJC_KVO_DIDCHANGE(set, key); + return newKey; } } -void CFSetReplaceValue(CFMutableSetRef set, const void *value) { - struct __CFSetBucket *match; +void CFSetReplaceValue(CFMutableSetRef set, const void *key) { + CFIndex match; const CFSetCallBacks *cb; - const void *newValue; - CF_OBJC_FUNCDISPATCH1(__kCFSetTypeID, void, set, "_replaceObject:", value); + const void *newKey; + CFAllocatorRef allocator; + CF_OBJC_FUNCDISPATCH1(__kCFSetTypeID, void, set, "_replaceObject:", key); __CFGenericValidateType(set, __kCFSetTypeID); switch (__CFSetGetType(set)) { case __kCFSetMutable: @@ -684,36 +883,37 @@ void CFSetReplaceValue(CFMutableSetRef set, const void *value) { break; } if (0 == set->_count) return; - __CFSetFindBuckets1(set, value, &match); - if (!match) return; - cb = __CFSetGetCallBacks(set); + if (__kCFSetHasNullCallBacks == __CFBitfieldGetValue(((const CFRuntimeBase *)set)->_info, 3, 2)) { + match = __CFSetFindBuckets1a(set, key); + } else { + match = __CFSetFindBuckets1b(set, key); + } + if (kCFNotFound == match) return; + cb = __CFSetGetKeyCallBacks(set); + allocator = (set->_xflags & __kCFSetWeakKeys) ? kCFAllocatorNull : __CFGetAllocator(set); if (cb->retain) { - newValue = (void *)INVOKE_CALLBACK3(((const void *(*)(CFAllocatorRef, const void *, void *))cb->retain), __CFGetAllocator(set), value, set->_context); + newKey = (void *)INVOKE_CALLBACK3(((const void *(*)(CFAllocatorRef, const void *, void *))cb->retain), allocator, key, set->_context); } else { - newValue = value; + newKey = key; } + CF_OBJC_KVO_WILLCHANGE(set, key); 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); + INVOKE_CALLBACK3(((void (*)(CFAllocatorRef, const void *, void *))cb->release), allocator, set->_keys[match], set->_context); } - if (set->_deletedMarker == newValue) { - __CFSetFindNewDeletedMarker(set); - } - match->_key = newValue; + CF_WRITE_BARRIER_ASSIGN(allocator, set->_keys[match], newKey); + CF_OBJC_KVO_DIDCHANGE(set, key); } -void CFSetSetValue(CFMutableSetRef set, const void *value) { - struct __CFSetBucket *match, *nomatch; +void CFSetSetValue(CFMutableSetRef set, const void *key) { + CFIndex match, nomatch; const CFSetCallBacks *cb; - const void *newValue; - CF_OBJC_FUNCDISPATCH1(__kCFSetTypeID, void, set, "_setObject:", value); + const void *newKey; + CFAllocatorRef allocator; + CF_OBJC_FUNCDISPATCH1(__kCFSetTypeID, void, set, "_setObject:", key); __CFGenericValidateType(set, __kCFSetTypeID); switch (__CFSetGetType(set)) { case __kCFSetMutable: - if (set->_bucketsUsed == set->_capacity || NULL == set->_buckets) { + if (set->_count == set->_capacity || NULL == set->_keys) { __CFSetGrow(set, 1); } break; @@ -723,43 +923,37 @@ void CFSetSetValue(CFMutableSetRef set, const void *value) { 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); + __CFSetFindBuckets2(set, key, &match, &nomatch); + cb = __CFSetGetKeyCallBacks(set); + allocator = (set->_xflags & __kCFSetWeakKeys) ? kCFAllocatorNull : __CFGetAllocator(set); if (cb->retain) { - newValue = (void *)INVOKE_CALLBACK3(((const void *(*)(CFAllocatorRef, const void *, void *))cb->retain), __CFGetAllocator(set), value, set->_context); + newKey = (void *)INVOKE_CALLBACK3(((const void *(*)(CFAllocatorRef, const void *, void *))cb->retain), allocator, key, set->_context); } else { - newValue = value; + newKey = key; } - if (match) { + if (kCFNotFound != match) { + CF_OBJC_KVO_WILLCHANGE(set, key); if (cb->release) { - INVOKE_CALLBACK3(((void (*)(CFAllocatorRef, const void *, void *))cb->release), __CFGetAllocator(set), match->_key, set->_context); - match->_key = set->_deletedMarker; + INVOKE_CALLBACK3(((void (*)(CFAllocatorRef, const void *, void *))cb->release), allocator, set->_keys[match], set->_context); } - if (set->_emptyMarker == newValue) { - __CFSetFindNewEmptyMarker(set); - } - if (set->_deletedMarker == newValue) { - __CFSetFindNewDeletedMarker(set); - } - match->_key = newValue; + CF_WRITE_BARRIER_ASSIGN(allocator, set->_keys[match], newKey); + CF_OBJC_KVO_DIDCHANGE(set, key); } 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->_marker == (uintptr_t)newKey || ~set->_marker == (uintptr_t)newKey) { + __CFSetFindNewMarker(set); } - if (set->_deletedMarker == newValue) { - __CFSetFindNewDeletedMarker(set); - } - nomatch->_key = newValue; - set->_bucketsUsed++; + CF_OBJC_KVO_WILLCHANGE(set, key); + CF_WRITE_BARRIER_ASSIGN(allocator, set->_keys[nomatch], newKey); set->_count++; + CF_OBJC_KVO_DIDCHANGE(set, key); } } -void CFSetRemoveValue(CFMutableSetRef set, const void *value) { - struct __CFSetBucket *match; +void CFSetRemoveValue(CFMutableSetRef set, const void *key) { + CFIndex match; const CFSetCallBacks *cb; - CF_OBJC_FUNCDISPATCH1(__kCFSetTypeID, void, set, "removeObject:", value); + CF_OBJC_FUNCDISPATCH1(__kCFSetTypeID, void, set, "removeObject:", key); __CFGenericValidateType(set, __kCFSetTypeID); switch (__CFSetGetType(set)) { case __kCFSetMutable: @@ -770,21 +964,53 @@ void CFSetRemoveValue(CFMutableSetRef set, const void *value) { break; } if (0 == set->_count) return; - __CFSetFindBuckets1(set, value, &match); - if (!match) return; - set->_count--; + if (__kCFSetHasNullCallBacks == __CFBitfieldGetValue(((const CFRuntimeBase *)set)->_info, 3, 2)) { + match = __CFSetFindBuckets1a(set, key); + } else { + match = __CFSetFindBuckets1b(set, key); + } + if (kCFNotFound == match) return; + cb = __CFSetGetKeyCallBacks(set); if (1) { - cb = __CFSetGetCallBacks(set); + const void *oldkey = set->_keys[match]; + CF_OBJC_KVO_WILLCHANGE(set, oldkey); + set->_keys[match] = (const void *)~set->_marker; + set->_count--; + CF_OBJC_KVO_DIDCHANGE(set, oldkey); if (cb->release) { - INVOKE_CALLBACK3(((void (*)(CFAllocatorRef, const void *, void *))cb->release), __CFGetAllocator(set), match->_key, set->_context); + INVOKE_CALLBACK3(((void (*)(CFAllocatorRef, const void *, void *))cb->release), __CFGetAllocator(set), oldkey, set->_context); + } + set->_deletes++; + if ((__kCFSetMutable == __CFSetGetType(set)) && (set->_bucketsNum < 4 * set->_deletes || (512 < set->_capacity && 3.236067 * set->_count < set->_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 sets + __CFSetGrow(set, 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. + // _CFSetDecrementValue() below has this same code. + if (match < set->_bucketsNum - 1 && set->_keys[match + 1] == (const void *)set->_marker) { + while (0 <= match && set->_keys[match] == (const void *)~set->_marker) { + set->_keys[match] = (const void *)set->_marker; + set->_deletes--; + match--; + } + } } - match->_key = set->_deletedMarker; - set->_bucketsUsed--; } } void CFSetRemoveAllValues(CFMutableSetRef set) { - struct __CFSetBucket *buckets; + const void **keys; const CFSetCallBacks *cb; CFAllocatorRef allocator; CFIndex idx, nbuckets; @@ -799,19 +1025,30 @@ void CFSetRemoveAllValues(CFMutableSetRef set) { break; } if (0 == set->_count) return; - buckets = set->_buckets; + keys = set->_keys; nbuckets = set->_bucketsNum; - cb = __CFSetGetCallBacks(set); + cb = __CFSetGetKeyCallBacks(set); allocator = __CFGetAllocator(set); for (idx = 0; idx < nbuckets; idx++) { - if (__CFSetBucketIsOccupied(set, &buckets[idx])) { + if (set->_marker != (uintptr_t)keys[idx] && ~set->_marker != (uintptr_t)keys[idx]) { + const void *oldkey = keys[idx]; + CF_OBJC_KVO_WILLCHANGE(set, oldkey); + keys[idx] = (const void *)~set->_marker; + set->_count--; + CF_OBJC_KVO_DIDCHANGE(set, oldkey); if (cb->release) { - INVOKE_CALLBACK3(((void (*)(CFAllocatorRef, const void *, void *))cb->release), allocator, buckets[idx]._key, set->_context); + INVOKE_CALLBACK3(((void (*)(CFAllocatorRef, const void *, void *))cb->release), allocator, oldkey, set->_context); } - buckets[idx]._key = set->_emptyMarker; } } - set->_bucketsUsed = 0; + // XXX need memset here + for (idx = 0; idx < nbuckets; idx++) { + keys[idx] = (const void *)set->_marker; + } set->_count = 0; + set->_deletes = 0; + if ((__kCFSetMutable == __CFSetGetType(set)) && (512 < set->_capacity)) { + __CFSetGrow(set, 256); + } } diff --git a/Collections.subproj/CFSet.h b/Collections.subproj/CFSet.h index 260cdad..cfeb293 100644 --- a/Collections.subproj/CFSet.h +++ b/Collections.subproj/CFSet.h @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -23,7 +21,7 @@ * @APPLE_LICENSE_HEADER_END@ */ /* CFSet.h - Copyright (c) 1998-2003, Apple, Inc. All rights reserved. + Copyright (c) 1998-2005, Apple, Inc. All rights reserved. */ /*! @header CFSet diff --git a/Collections.subproj/CFStorage.c b/Collections.subproj/CFStorage.c index fb1880f..35b4b6e 100644 --- a/Collections.subproj/CFStorage.c +++ b/Collections.subproj/CFStorage.c @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -47,10 +45,10 @@ enum { __CFStorageMaxLeafCapacity = 65536 }; -#define COPYMEM(src,dst,n) memmove((dst), (src), (n)) +#define COPYMEM(src,dst,n) CF_WRITE_BARRIER_MEMMOVE((dst), (src), (n)) #define PAGE_LIMIT ((CFIndex)vm_page_size / 2) -static int roundToPage(int num) { +CF_INLINE int roundToPage(int num) { return (num + vm_page_size - 1) & ~(vm_page_size - 1); } @@ -59,7 +57,7 @@ typedef struct __CFStorageNode { bool isLeaf; union { struct { - CFIndex capacityInBytes; + CFIndex capacityInBytes; // capacityInBytes is capacity of memory; this is either 0, or >= numBytes uint8_t *memory; } leaf; struct { @@ -71,62 +69,74 @@ typedef struct __CFStorageNode { struct __CFStorage { CFRuntimeBase base; CFIndex valueSize; - CFRange cachedRange; - uint8_t *cachedNodeMemory; - CFIndex maxLeafCapacity; + CFRange cachedRange; // In terms of values, not bytes + CFStorageNode *cachedNode; // If cachedRange is valid, then either this or + uint8_t *cachedNodeMemory; // this should be non-NULL + CFIndex maxLeafCapacity; // In terms of bytes CFStorageNode rootNode; + CFOptionFlags nodeHint; // auto_memory_type_t, AUTO_MEMORY_SCANNED or AUTO_MEMORY_UNSCANNED. }; -/* 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 +/* Allocates the memory and initializes the capacity in a leaf. __CFStorageAllocLeafNodeMemory() is the entry point; __CFStorageAllocLeafNodeMemoryAux is called if actual reallocation is needed. */ -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; +static void __CFStorageAllocLeafNodeMemoryAux(CFAllocatorRef allocator, CFStorageRef storage, CFStorageNode *node, CFIndex cap) { + CF_WRITE_BARRIER_ASSIGN(allocator, node->info.leaf.memory, _CFAllocatorReallocateGC(allocator, node->info.leaf.memory, cap, storage->nodeHint)); // This will free... ??? Use allocator + if (__CFOASafe) __CFSetLastAllocationEventName(node->info.leaf.memory, "CFStorage (node bytes)"); + node->info.leaf.capacityInBytes = cap; } -/* Allocates the memory and initializes the capacity in a leaf -*/ -static void __CFStorageAllocLeafNodeMemory(CFStorageRef storage, CFStorageNode *node, CFIndex cap, bool compact) { +CF_INLINE void __CFStorageAllocLeafNodeMemory(CFAllocatorRef allocator, CFStorageRef storage, CFStorageNode *node, CFIndex cap, bool compact) { if (cap > PAGE_LIMIT) { cap = roundToPage(cap); if (cap > storage->maxLeafCapacity) cap = storage->maxLeafCapacity; } else { cap = (((cap + 63) / 64) * 64); } - if (compact ? (cap != node->info.leaf.capacityInBytes) : (cap > node->info.leaf.capacityInBytes)) { - 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; + if (compact ? (cap != node->info.leaf.capacityInBytes) : (cap > node->info.leaf.capacityInBytes)) __CFStorageAllocLeafNodeMemoryAux(allocator, storage, node, cap); +} + +/* Sets the cache to point at the specified node or memory. loc and len are in terms of values, not bytes. To clear the cache set these two to 0. + At least one of node or memory should be non-NULL. memory is consulted first when using the cache. +*/ +CF_INLINE void __CFStorageSetCache(CFStorageRef storage, CFStorageNode *node, uint8_t *memory, CFIndex loc, CFIndex len) { + CFAllocatorRef allocator = __CFGetAllocator(storage); + storage->cachedRange.location = loc; + storage->cachedRange.length = len; + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, storage, storage->cachedNode, node); + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, storage, storage->cachedNodeMemory, memory); +} + +/* Gets the location for the specified absolute loc from the cached info; call only after verifying that the cache is OK to use. + Note that we assume if !storage->cachedNodeMemory, then storage->cachedNode must be non-NULL. + However, it is possible to have storage->cachedNodeMemory without storage->cachedNode. We check the memory before node. +*/ +CF_INLINE uint8_t *__CFStorageGetFromCache(CFStorageRef storage, CFIndex loc) { + if (!storage->cachedNodeMemory && !(storage->cachedNodeMemory = storage->cachedNode->info.leaf.memory)) { + CFAllocatorRef allocator = CFGetAllocator(storage); + __CFStorageAllocLeafNodeMemory(allocator, storage, storage->cachedNode, storage->cachedNode->numBytes, false); + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, storage, storage->cachedNodeMemory, storage->cachedNode->info.leaf.memory); } + return storage->cachedNodeMemory + (loc - storage->cachedRange.location) * storage->valueSize; } -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; +/* Returns the number of the child containing the desired value and the relative index of the value in that child. + forInsertion = true means that we are looking for the child in which to insert; this changes the behavior when the index is at the end of a child + relativeByteNum (not optional, for performance reasons) returns the relative byte number of the specified byte in the child. + Don't call with leaf nodes! +*/ +CF_INLINE void __CFStorageFindChild(CFStorageNode *node, CFIndex byteNum, bool forInsertion, CFIndex *childNum, CFIndex *relativeByteNum) { + if (forInsertion) byteNum--; /* If for insertion, we do <= checks, not <, so this accomplishes the same thing */ + if (byteNum < node->info.notLeaf.child[0]->numBytes) *childNum = 0; + else { + byteNum -= node->info.notLeaf.child[0]->numBytes; + if (byteNum < node->info.notLeaf.child[1]->numBytes) *childNum = 1; + else { + byteNum -= node->info.notLeaf.child[1]->numBytes; + *childNum = 2; + } } - return newNode; + if (forInsertion) byteNum++; + *relativeByteNum = byteNum; } /* Finds the location where the specified byte is stored. If validConsecutiveByteRange is not NULL, returns @@ -136,7 +146,7 @@ static CFStorageNode *__CFStorageCreateNode(CFStorageRef storage, bool isLeaf, C 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); + __CFStorageAllocLeafNodeMemory(CFGetAllocator(storage), storage, node, node->numBytes, false); return node->info.leaf.memory + byteNum; } else { void *result; @@ -152,6 +162,46 @@ static void *__CFStorageFindByte(CFStorageRef storage, CFStorageNode *node, CFIn } } +/* Guts of CFStorageGetValueAtIndex(); note that validConsecutiveValueRange is not optional. + Consults and updates cache. +*/ +CF_INLINE void *__CFStorageGetValueAtIndex(CFStorageRef storage, CFIndex idx, CFRange *validConsecutiveValueRange) { + uint8_t *result; + if (idx < storage->cachedRange.location + storage->cachedRange.length && idx >= storage->cachedRange.location) { + result = __CFStorageGetFromCache(storage, idx); + } else { + CFRange range; + result = __CFStorageFindByte(storage, &storage->rootNode, idx * storage->valueSize, &range); + __CFStorageSetCache(storage, NULL, result - (idx * storage->valueSize - range.location), range.location / storage->valueSize, range.length / storage->valueSize); + } + *validConsecutiveValueRange = storage->cachedRange; + return result; +} + +static CFStorageNode *__CFStorageCreateNode(CFAllocatorRef allocator, bool isLeaf, CFIndex numBytes) { + CFStorageNode *newNode = _CFAllocatorAllocateGC(allocator, 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; +} + +static void __CFStorageNodeDealloc(CFAllocatorRef allocator, CFStorageNode *node, bool freeNodeItself) { + if (node->isLeaf) { + _CFAllocatorDeallocateGC(allocator, node->info.leaf.memory); + } else { + int cnt; + for (cnt = 0; cnt < 3; cnt++) if (node->info.notLeaf.child[cnt]) __CFStorageNodeDealloc(allocator, node->info.notLeaf.child[cnt], true); + } + if (freeNodeItself) _CFAllocatorDeallocateGC(allocator, node); +} + static CFIndex __CFStorageGetNumChildren(CFStorageNode *node) { if (!node || node->isLeaf) return 0; if (node->info.notLeaf.child[2]) return 3; @@ -162,13 +212,13 @@ static CFIndex __CFStorageGetNumChildren(CFStorageNode *node) { /* The boolean compact indicates whether leaf nodes that get smaller should be realloced. */ -static void __CFStorageDelete(CFStorageRef storage, CFStorageNode *node, CFRange range, bool compact) { +static void __CFStorageDelete(CFAllocatorRef allocator, CFStorageRef storage, CFStorageNode *node, CFRange range, bool compact) { if (node->isLeaf) { node->numBytes -= range.length; // If this node had memory allocated, readjust the bytes... if (node->info.leaf.memory) { COPYMEM(node->info.leaf.memory + range.location + range.length, node->info.leaf.memory + range.location, node->numBytes - range.location); - if (compact) __CFStorageAllocLeafNodeMemory(storage, node, node->numBytes, true); + if (compact) __CFStorageAllocLeafNodeMemory(allocator, storage, node, node->numBytes, true); } } else { bool childrenAreLeaves = node->info.notLeaf.child[0]->isLeaf; @@ -185,12 +235,12 @@ static void __CFStorageDelete(CFStorageRef storage, CFStorageNode *node, CFRange rangeToDelete.length = range.length; rangeToDelete.location = relativeByteNum - range.length; } - __CFStorageDelete(storage, node->info.notLeaf.child[childNum], rangeToDelete, compact); + __CFStorageDelete(allocator, storage, node->info.notLeaf.child[childNum], rangeToDelete, compact); if (node->info.notLeaf.child[childNum]->numBytes == 0) { // Delete empty node and compact int cnt; - CFAllocatorDeallocate(CFGetAllocator(storage), node->info.notLeaf.child[childNum]); + _CFAllocatorDeallocateGC(allocator, node->info.notLeaf.child[childNum]); for (cnt = childNum; cnt < 2; cnt++) { - node->info.notLeaf.child[cnt] = node->info.notLeaf.child[cnt+1]; + CF_WRITE_BARRIER_ASSIGN(allocator, node->info.notLeaf.child[cnt], node->info.notLeaf.child[cnt+1]); } node->info.notLeaf.child[2] = NULL; } @@ -200,15 +250,15 @@ static void __CFStorageDelete(CFStorageRef storage, CFStorageNode *node, CFRange 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); + __CFStorageAllocLeafNodeMemory(allocator, storage, node->info.notLeaf.child[0], node->numBytes, false); if (node->info.notLeaf.child[1] && node->info.notLeaf.child[1]->numBytes) { COPYMEM(node->info.notLeaf.child[1]->info.leaf.memory, node->info.notLeaf.child[0]->info.leaf.memory + node->info.notLeaf.child[0]->numBytes, node->info.notLeaf.child[1]->numBytes); if (node->info.notLeaf.child[2] && node->info.notLeaf.child[2]->numBytes) { COPYMEM(node->info.notLeaf.child[2]->info.leaf.memory, node->info.notLeaf.child[0]->info.leaf.memory + node->info.notLeaf.child[0]->numBytes + node->info.notLeaf.child[1]->numBytes, node->info.notLeaf.child[2]->numBytes); - CFAllocatorDeallocate(CFGetAllocator(storage), node->info.notLeaf.child[2]); + __CFStorageNodeDealloc(allocator, node->info.notLeaf.child[2], true); node->info.notLeaf.child[2] = NULL; } - CFAllocatorDeallocate(CFGetAllocator(storage), node->info.notLeaf.child[1]); + __CFStorageNodeDealloc(allocator, node->info.notLeaf.child[1], true); node->info.notLeaf.child[1] = NULL; } node->info.notLeaf.child[0]->numBytes = node->numBytes; @@ -240,14 +290,17 @@ static void __CFStorageDelete(CFStorageRef storage, CFStorageNode *node, CFRange // 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); + if (!node->info.notLeaf.child[cCnt]) { + CFStorageNode *newNode = __CFStorageCreateNode(allocator, false, 0); + CF_WRITE_BARRIER_ASSIGN(allocator, node->info.notLeaf.child[cCnt], newNode); + } for (cnt = 0; cnt < sCnt; cnt++) { node->info.notLeaf.child[cCnt]->numBytes += gChildren[gCnt]->numBytes; - node->info.notLeaf.child[cCnt]->info.notLeaf.child[cnt] = gChildren[gCnt++]; + CF_WRITE_BARRIER_ASSIGN(allocator, 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]); + _CFAllocatorDeallocateGC(allocator, node->info.notLeaf.child[cCnt]); node->info.notLeaf.child[cCnt] = NULL; } } @@ -256,48 +309,56 @@ static void __CFStorageDelete(CFStorageRef storage, CFStorageNode *node, CFRange } } + /* 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) { +static CFStorageNode *__CFStorageInsert(CFAllocatorRef allocator, CFStorageRef storage, CFStorageNode *node, CFIndex byteNum, CFIndex size, CFIndex absoluteByteNum) { if (node->isLeaf) { if (size + node->numBytes > storage->maxLeafCapacity) { // Need to create more child nodes if (byteNum == node->numBytes) { // Inserting at end; easy... - CFStorageNode *newNode = __CFStorageCreateNode(storage, true, size); + CFStorageNode *newNode = __CFStorageCreateNode(allocator, true, size); + __CFStorageSetCache(storage, newNode, NULL, absoluteByteNum / storage->valueSize, size / storage->valueSize); return newNode; } else if (byteNum == 0) { // Inserting at front; also easy, but we need to swap node and newNode - CFStorageNode *newNode = __CFStorageCreateNode(storage, true, 0); - *newNode = *node; + CFStorageNode *newNode = __CFStorageCreateNode(allocator, true, 0); + CF_WRITE_BARRIER_MEMMOVE(newNode, node, sizeof(CFStorageNode)); node->isLeaf = true; node->numBytes = size; node->info.leaf.capacityInBytes = 0; node->info.leaf.memory = NULL; + __CFStorageSetCache(storage, node, NULL, absoluteByteNum / storage->valueSize, size / storage->valueSize); return newNode; } else if (byteNum + size <= storage->maxLeafCapacity) { // Inserting at middle; inserted region will fit into existing child // Create new node to hold the overflow - CFStorageNode *newNode = __CFStorageCreateNode(storage, true, node->numBytes - byteNum); + CFStorageNode *newNode = __CFStorageCreateNode(allocator, true, node->numBytes - byteNum); if (node->info.leaf.memory) { // We allocate memory lazily... - __CFStorageAllocLeafNodeMemory(storage, newNode, node->numBytes - byteNum, false); + __CFStorageAllocLeafNodeMemory(allocator, 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); + __CFStorageAllocLeafNodeMemory(allocator, storage, node, byteNum + size, false); } - node->numBytes += size - (node->numBytes - byteNum); + node->numBytes = byteNum + size; + __CFStorageSetCache(storage, node, node->info.leaf.memory, (absoluteByteNum - byteNum) / storage->valueSize, node->numBytes / storage->valueSize); return newNode; } else { // Inserting some of new into one node, rest into another; remember that the assumption is size <= storage->maxLeafCapacity - CFStorageNode *newNode = __CFStorageCreateNode(storage, true, node->numBytes + size - storage->maxLeafCapacity); // New stuff + CFStorageNode *newNode = __CFStorageCreateNode(allocator, 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); + __CFStorageAllocLeafNodeMemory(allocator, storage, newNode, node->numBytes + size - storage->maxLeafCapacity, false); COPYMEM(node->info.leaf.memory + byteNum, newNode->info.leaf.memory + byteNum + size - storage->maxLeafCapacity, node->numBytes - byteNum); + __CFStorageAllocLeafNodeMemory(allocator, storage, node, storage->maxLeafCapacity, false); } node->numBytes = storage->maxLeafCapacity; + __CFStorageSetCache(storage, node, node->info.leaf.memory, (absoluteByteNum - byteNum) / storage->valueSize, node->numBytes / storage->valueSize); + //__CFStorageSetCache(storage, NULL, NULL, 0, 0); return newNode; } } else { // No need to create new nodes! if (node->info.leaf.memory) { - __CFStorageAllocLeafNodeMemory(storage, node, node->numBytes + size, false); + __CFStorageAllocLeafNodeMemory(allocator, storage, node, node->numBytes + size, false); COPYMEM(node->info.leaf.memory + byteNum, node->info.leaf.memory + byteNum + size, node->numBytes - byteNum); } node->numBytes += size; + __CFStorageSetCache(storage, node, node->info.leaf.memory, (absoluteByteNum - byteNum) / storage->valueSize, node->numBytes / storage->valueSize); return NULL; } } else { @@ -305,27 +366,27 @@ static CFStorageNode *__CFStorageInsert(CFStorageRef storage, CFStorageNode *nod CFIndex childNum; CFStorageNode *newNode; __CFStorageFindChild(node, byteNum, true, &childNum, &relativeByteNum); - newNode = __CFStorageInsert(storage, node->info.notLeaf.child[childNum], relativeByteNum, size); + newNode = __CFStorageInsert(allocator, storage, node->info.notLeaf.child[childNum], relativeByteNum, size, absoluteByteNum); if (newNode) { if (node->info.notLeaf.child[2] == NULL) { // There's an empty slot for the new node, cool - if (childNum == 0) node->info.notLeaf.child[2] = node->info.notLeaf.child[1]; // Make room - node->info.notLeaf.child[childNum + 1] = newNode; + if (childNum == 0) CF_WRITE_BARRIER_ASSIGN(allocator, node->info.notLeaf.child[2], node->info.notLeaf.child[1]); // Make room + CF_WRITE_BARRIER_ASSIGN(allocator, node->info.notLeaf.child[childNum + 1], newNode); node->numBytes += size; return NULL; } else { - CFStorageNode *anotherNode = __CFStorageCreateNode(storage, false, 0); // Create another node + CFStorageNode *anotherNode = __CFStorageCreateNode(allocator, 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; + CF_WRITE_BARRIER_ASSIGN(allocator, anotherNode->info.notLeaf.child[0], node->info.notLeaf.child[1]); + CF_WRITE_BARRIER_ASSIGN(allocator, anotherNode->info.notLeaf.child[1], node->info.notLeaf.child[2]); + CF_WRITE_BARRIER_ASSIGN(allocator, node->info.notLeaf.child[1], newNode); node->info.notLeaf.child[2] = NULL; } else if (childNum == 1) { // Last child goes to new node - anotherNode->info.notLeaf.child[0] = newNode; - anotherNode->info.notLeaf.child[1] = node->info.notLeaf.child[2]; + CF_WRITE_BARRIER_ASSIGN(allocator, anotherNode->info.notLeaf.child[0], newNode); + CF_WRITE_BARRIER_ASSIGN(allocator, anotherNode->info.notLeaf.child[1], node->info.notLeaf.child[2]); node->info.notLeaf.child[2] = NULL; } else { // New node contains the new comers... - anotherNode->info.notLeaf.child[0] = node->info.notLeaf.child[2]; - anotherNode->info.notLeaf.child[1] = newNode; + CF_WRITE_BARRIER_ASSIGN(allocator, anotherNode->info.notLeaf.child[0], node->info.notLeaf.child[2]); + CF_WRITE_BARRIER_ASSIGN(allocator, anotherNode->info.notLeaf.child[1], newNode); node->info.notLeaf.child[2] = NULL; } node->numBytes = node->info.notLeaf.child[0]->numBytes + node->info.notLeaf.child[1]->numBytes; @@ -408,32 +469,24 @@ CFIndex __CFStorageGetValueSize(CFStorageRef storage) { 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)); + CFAllocatorRef allocator = CFGetAllocator(storage); + result = CFStringCreateMutable(allocator, 0); + CFStringAppendFormat(result, NULL, CFSTR("[count = %u, capacity = %u]\n"), storage, allocator, __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); + if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) return; // XXX_PCB GC will take care of us. __CFStorageNodeDealloc(allocator, &storage->rootNode, false); } static CFTypeID __kCFStorageTypeID = _kCFRuntimeNotATypeID; static const CFRuntimeClass __CFStorageClass = { - 0, + _kCFRuntimeScannedObject, "CFStorage", NULL, // init NULL, // copy @@ -460,6 +513,7 @@ CFStorageRef CFStorageCreate(CFAllocatorRef allocator, CFIndex valueSize) { storage->valueSize = valueSize; storage->cachedRange.location = 0; storage->cachedRange.length = 0; + storage->cachedNode = NULL; storage->cachedNodeMemory = NULL; storage->maxLeafCapacity = __CFStorageMaxLeafCapacity; if (valueSize && ((storage->maxLeafCapacity % valueSize) != 0)) { @@ -467,6 +521,7 @@ CFStorageRef CFStorageCreate(CFAllocatorRef allocator, CFIndex valueSize) { } memset(&(storage->rootNode), 0, sizeof(CFStorageNode)); storage->rootNode.isLeaf = true; + storage->nodeHint = AUTO_MEMORY_SCANNED; if (__CFOASafe) __CFSetLastAllocationEventName(storage, "CFStorage"); return storage; } @@ -481,21 +536,10 @@ CFIndex CFStorageGetCount(CFStorageRef 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; + CFRange range; + return __CFStorageGetValueAtIndex(storage, idx, validConsecutiveValueRange ? validConsecutiveValueRange : &range); } /* Makes space for range.length values at location range.location @@ -506,39 +550,40 @@ void CFStorageInsertValues(CFStorageRef storage, CFRange range) { CFIndex byteNum = range.location * storage->valueSize; while (numBytesToInsert > 0) { CFStorageNode *newNode; + CFAllocatorRef allocator = CFGetAllocator(storage); CFIndex insertThisTime = numBytesToInsert; if (insertThisTime > storage->maxLeafCapacity) { insertThisTime = (storage->maxLeafCapacity / storage->valueSize) * storage->valueSize; } - newNode = __CFStorageInsert(storage, &storage->rootNode, byteNum, insertThisTime); + newNode = __CFStorageInsert(allocator, storage, &storage->rootNode, byteNum, insertThisTime, byteNum); if (newNode) { - CFStorageNode *tempRootNode = __CFStorageCreateNode(storage, false, 0); // Will copy the (static) rootNode over to this - *tempRootNode = storage->rootNode; + CFStorageNode *tempRootNode = __CFStorageCreateNode(allocator, false, 0); // Will copy the (static) rootNode over to this + CF_WRITE_BARRIER_MEMMOVE(tempRootNode, &storage->rootNode, sizeof(CFStorageNode)); storage->rootNode.isLeaf = false; - storage->rootNode.info.notLeaf.child[0] = tempRootNode; - storage->rootNode.info.notLeaf.child[1] = newNode; + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, storage, storage->rootNode.info.notLeaf.child[0], tempRootNode); + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, storage, storage->rootNode.info.notLeaf.child[1], newNode); storage->rootNode.info.notLeaf.child[2] = NULL; storage->rootNode.numBytes = tempRootNode->numBytes + newNode->numBytes; + if (storage->cachedNode == &(storage->rootNode)) + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, storage, storage->cachedNode, tempRootNode); // The cache should follow the node } 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) { + CFAllocatorRef allocator = CFGetAllocator(storage); range.location *= storage->valueSize; range.length *= storage->valueSize; - __CFStorageDelete(storage, &storage->rootNode, range, true); + __CFStorageDelete(allocator, 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); + CF_WRITE_BARRIER_MEMMOVE(&storage->rootNode, child, sizeof(CFStorageNode)); + _CFAllocatorDeallocateGC(allocator, child); } if (__CFStorageGetNumChildren(&storage->rootNode) == 0 && !storage->rootNode.isLeaf) { storage->rootNode.isLeaf = true; @@ -546,14 +591,13 @@ void CFStorageDeleteValues(CFStorageRef storage, CFRange range) { 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); + 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); @@ -567,13 +611,13 @@ void CFStorageApplyFunction(CFStorageRef storage, CFRange range, CFStorageApplie while (0 < range.length) { CFRange leafRange; const void *storagePtr; - CFIndex idx, cnt; + 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; - } + 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; } @@ -582,7 +626,7 @@ void CFStorageApplyFunction(CFStorageRef storage, CFRange range, CFStorageApplie void CFStorageReplaceValues(CFStorageRef storage, CFRange range, const void *values) { while (range.length > 0) { CFRange leafRange; - void *storagePtr = CFStorageGetValueAtIndex(storage, range.location, &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); @@ -592,6 +636,24 @@ void CFStorageReplaceValues(CFStorageRef storage, CFRange range, const void *val } } +/* Used by CFArray.c */ + +static void __CFStorageNodeSetLayoutType(CFStorageNode *node, auto_zone_t *zone, auto_memory_type_t type) { + if (node->isLeaf) { + auto_zone_set_layout_type(zone, node->info.leaf.memory, type); + } else { + CFStorageNode **children = node->info.notLeaf.child; + if (children[0]) __CFStorageNodeSetLayoutType(children[0], zone, type); + if (children[1]) __CFStorageNodeSetLayoutType(children[1], zone, type); + if (children[2]) __CFStorageNodeSetLayoutType(children[2], zone, type); + } +} + +__private_extern__ void _CFStorageSetWeak(CFStorageRef storage) { + storage->nodeHint = AUTO_MEMORY_UNSCANNED; + __CFStorageNodeSetLayoutType(&storage->rootNode, __CFCollectableZone, storage->nodeHint); +} + #undef COPYMEM #undef PAGE_LIMIT diff --git a/Collections.subproj/CFStorage.h b/Collections.subproj/CFStorage.h index e0e9069..382cc47 100644 --- a/Collections.subproj/CFStorage.h +++ b/Collections.subproj/CFStorage.h @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -23,7 +21,7 @@ * @APPLE_LICENSE_HEADER_END@ */ /* CFStorage.h - Copyright (c) 1999-2003, Apple, Inc. All rights reserved. + Copyright (c) 1999-2005, Apple, Inc. All rights reserved. */ /*! @header CFStorage diff --git a/Collections.subproj/CFTree.c b/Collections.subproj/CFTree.c index b6edeaf..476c7c2 100644 --- a/Collections.subproj/CFTree.c +++ b/Collections.subproj/CFTree.c @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -29,7 +27,7 @@ #include #include "CFInternal.h" -#include "CFUtilities.h" +#include "CFUtilitiesPriv.h" struct __CFTreeCallBacks { CFTreeRetainCallBack retain; @@ -42,6 +40,7 @@ struct __CFTree { CFTreeRef _parent; /* Not retained */ CFTreeRef _sibling; /* Not retained */ CFTreeRef _child; /* All children get a retain from the parent */ + CFTreeRef _rightmostChild; /* Not retained */ /* 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. */ @@ -86,6 +85,7 @@ static CFStringRef __CFTreeCopyDescription(CFTypeRef cf) { CFTreeRef tree = (CFTreeRef)cf; CFMutableStringRef result; CFStringRef contextDesc = NULL; + Boolean safeToReleaseContextDesc = true; const struct __CFTreeCallBacks *cb; CFAllocatorRef allocator; allocator = CFGetAllocator(tree); @@ -93,31 +93,38 @@ static CFStringRef __CFTreeCopyDescription(CFTypeRef cf) { cb = __CFTreeGetCallBacks(tree); if (NULL != cb->copyDescription) { contextDesc = (CFStringRef)INVOKE_CALLBACK1(cb->copyDescription, tree->_info); + safeToReleaseContextDesc = _CFExecutableLinkedOnOrAfter(CFSystemVersionTiger); // Because it came from elsewhere, only free it compatibly (3593254) } if (NULL == contextDesc) { contextDesc = CFStringCreateWithFormat(allocator, NULL, CFSTR(""), tree->_info); } CFStringAppendFormat(result, NULL, CFSTR("{children = %u, context = %@}"), cf, allocator, CFTreeGetChildCount(tree), contextDesc); + if (contextDesc && safeToReleaseContextDesc) CFRelease(contextDesc); return result; } static void __CFTreeDeallocate(CFTypeRef cf) { CFTreeRef tree = (CFTreeRef)cf; const struct __CFTreeCallBacks *cb; - CFTreeRemoveAllChildren(tree); + CFAllocatorRef allocator = __CFGetAllocator(tree); + if (!CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { + // GC: keep the tree intact during finalization. + CFTreeRemoveAllChildren(tree); + } cb = __CFTreeGetCallBacks(tree); if (NULL != cb->release) { INVOKE_CALLBACK1(cb->release, tree->_info); } if (__kCFTreeHasCustomCallBacks == __CFTreeGetCallBacksType(tree)) { - CFAllocatorDeallocate(CFGetAllocator(tree), tree->_callbacks); + _CFAllocatorDeallocateGC(CFGetAllocator(tree), tree->_callbacks); } } + static CFTypeID __kCFTreeTypeID = _kCFRuntimeNotATypeID; static const CFRuntimeClass __CFTreeClass = { - 0, + _kCFRuntimeScannedObject, "CFTree", NULL, // init NULL, // copy @@ -150,6 +157,7 @@ CFTreeRef CFTreeCreate(CFAllocatorRef allocator, const CFTreeContext *context) { memory->_parent = NULL; memory->_sibling = NULL; memory->_child = NULL; + memory->_rightmostChild = NULL; /* Start the context off in a recognizable state */ __CFBitfieldSetValue(memory->_base._info, 1, 0, __kCFTreeHasNullCallBacks); @@ -214,34 +222,35 @@ void CFTreeSetContext(CFTreeRef tree, const CFTreeContext *context) { struct __CFTreeCallBacks *oldcb = (struct __CFTreeCallBacks *)__CFTreeGetCallBacks(tree); struct __CFTreeCallBacks *newcb; void *oldinfo = tree->_info; - + CFAllocatorRef allocator = CFGetAllocator(tree); + if (__CFTreeCallBacksMatchNull(context)) { - newtype = __kCFTreeHasNullCallBacks; + newtype = __kCFTreeHasNullCallBacks; } else if (__CFTreeCallBacksMatchCFType(context)) { - newtype = __kCFTreeHasCFTypeCallBacks; + newtype = __kCFTreeHasCFTypeCallBacks; } else { - newtype = __kCFTreeHasCustomCallBacks; - tree->_callbacks = CFAllocatorAllocate(CFGetAllocator(tree), sizeof(struct __CFTreeCallBacks), 0); - if (__CFOASafe) __CFSetLastAllocationEventName(tree->_callbacks, "CFTree (callbacks)"); + newtype = __kCFTreeHasCustomCallBacks; + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, tree, tree->_callbacks, _CFAllocatorAllocateGC(allocator, 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)); + 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; + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, tree, tree->_info, context->info); } if (NULL != oldcb->release) { - INVOKE_CALLBACK1(oldcb->release, oldinfo); + INVOKE_CALLBACK1(oldcb->release, oldinfo); } if (oldtype == __kCFTreeHasCustomCallBacks) { - CFAllocatorDeallocate(CFGetAllocator(tree), oldcb); + _CFAllocatorDeallocateGC(allocator, oldcb); } } @@ -318,17 +327,22 @@ void CFTreeApplyFunctionToChildren(CFTreeRef tree, CFTreeApplierFunction applier } void CFTreePrependChild(CFTreeRef tree, CFTreeRef newChild) { + CFAllocatorRef allocator = CFGetAllocator(tree); __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; + _CFRetainGC(newChild); + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, newChild, newChild->_parent, tree); + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, newChild, newChild->_sibling, tree->_child); + if (!tree->_child) { + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, tree, tree->_rightmostChild, newChild); + } + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, tree, tree->_child, newChild); } void CFTreeAppendChild(CFTreeRef tree, CFTreeRef newChild) { + CFAllocatorRef allocator; __CFGenericValidateType(tree, __kCFTreeTypeID); __CFGenericValidateType(newChild, __kCFTreeTypeID); CFAssert1(NULL == newChild->_parent, __kCFLogAssertion, "%s(): must remove newChild from previous parent first", __PRETTY_FUNCTION__); @@ -336,48 +350,61 @@ void CFTreeAppendChild(CFTreeRef tree, CFTreeRef newChild) { if (newChild->_parent) { HALT; } - CFRetain(newChild); - newChild->_parent = tree; + _CFRetainGC(newChild); + allocator = CFGetAllocator(tree); + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, newChild, newChild->_parent, tree); newChild->_sibling = NULL; if (!tree->_child) { - tree->_child = newChild; + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, tree, tree->_child, newChild); } else { - CFTreeRef child; - for (child = tree->_child; child->_sibling; child = child->_sibling) - ; - child->_sibling = newChild; + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, tree->_rightmostChild, tree->_rightmostChild->_sibling, newChild); } + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, tree, tree->_rightmostChild, newChild); } void CFTreeInsertSibling(CFTreeRef tree, CFTreeRef newSibling) { + CFAllocatorRef allocator; __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; + _CFRetainGC(newSibling); + allocator = CFGetAllocator(tree); + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, newSibling, newSibling->_parent, tree->_parent); + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, newSibling, newSibling->_sibling, tree->_sibling); + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, tree, tree->_sibling, newSibling); + if (tree->_parent) { + if (tree->_parent->_rightmostChild == tree) { + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, tree->_parent, tree->_parent->_rightmostChild, newSibling); + } + } } void CFTreeRemove(CFTreeRef tree) { __CFGenericValidateType(tree, __kCFTreeTypeID); if (NULL != tree->_parent) { + CFAllocatorRef allocator = CFGetAllocator(tree); if (tree == tree->_parent->_child) { - tree->_parent->_child = tree->_sibling; + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, tree->_parent, tree->_parent->_child, tree->_sibling); + if (tree->_sibling == NULL) { + tree->_parent->_rightmostChild = NULL; + } } else { CFTreeRef prevSibling = NULL; for (prevSibling = tree->_parent->_child; prevSibling; prevSibling = prevSibling->_sibling) { if (prevSibling->_sibling == tree) { - prevSibling->_sibling = tree->_sibling; + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, prevSibling, prevSibling->_sibling, tree->_sibling); + if (tree->_parent->_rightmostChild == tree) { + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, tree->_parent, tree->_parent->_rightmostChild, prevSibling); + } break; } } } tree->_parent = NULL; tree->_sibling = NULL; - CFRelease(tree); + _CFReleaseGC(tree); } } @@ -386,11 +413,12 @@ void CFTreeRemoveAllChildren(CFTreeRef tree) { __CFGenericValidateType(tree, __kCFTreeTypeID); nextChild = tree->_child; tree->_child = NULL; + tree->_rightmostChild = NULL; while (NULL != nextChild) { CFTreeRef nextSibling = nextChild->_sibling; nextChild->_parent = NULL; nextChild->_sibling = NULL; - CFRelease(nextChild); + _CFReleaseGC(nextChild); nextChild = nextSibling; } } @@ -416,8 +444,9 @@ void CFTreeSortChildren(CFTreeRef tree, CFComparatorFunction comparator, void *c CFTreeRef nextChild; struct _tcompareContext ctx; CFTreeRef *list, buffer[128]; + CFAllocatorRef allocator = __CFGetAllocator(tree); - list = (children < 128) ? buffer : CFAllocatorAllocate(kCFAllocatorDefault, children * sizeof(CFTreeRef), 0); + list = (children < 128) ? buffer : CFAllocatorAllocate(kCFAllocatorDefault, children * sizeof(CFTreeRef), 0); // XXX_PCB GC OK if (__CFOASafe && list != buffer) __CFSetLastAllocationEventName(tree->_callbacks, "CFTree (temp)"); nextChild = tree->_child; for (idx = 0; NULL != nextChild; idx++) { @@ -429,12 +458,13 @@ void CFTreeSortChildren(CFTreeRef tree, CFComparatorFunction comparator, void *c ctx.context = context; CFQSortArray(list, children, sizeof(CFTreeRef), (CFComparatorFunction)__CFTreeCompareValues, &ctx); - tree->_child = list[0]; + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, tree, tree->_child, list[0]); for (idx = 1; idx < children; idx++) { - list[idx - 1]->_sibling = list[idx]; + CF_WRITE_BARRIER_BASE_ASSIGN(allocator, list[idx - 1], list[idx - 1]->_sibling, list[idx]); } list[idx - 1]->_sibling = NULL; - if (list != buffer) CFAllocatorDeallocate(kCFAllocatorDefault, list); + tree->_rightmostChild = list[children - 1]; + if (list != buffer) CFAllocatorDeallocate(kCFAllocatorDefault, list); // XXX_PCB GC OK } } diff --git a/Collections.subproj/CFTree.h b/Collections.subproj/CFTree.h index 9a950ba..31dcc53 100644 --- a/Collections.subproj/CFTree.h +++ b/Collections.subproj/CFTree.h @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -23,7 +21,7 @@ * @APPLE_LICENSE_HEADER_END@ */ /* CFTree.h - Copyright (c) 1998-2003, Apple, Inc. All rights reserved. + Copyright (c) 1998-2005, Apple, Inc. All rights reserved. */ /*! @header CFTree diff --git a/Locale.subproj/CFLocale.h b/Locale.subproj/CFLocale.h index 5fa2e48..6f175c3 100644 --- a/Locale.subproj/CFLocale.h +++ b/Locale.subproj/CFLocale.h @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -23,15 +21,17 @@ * @APPLE_LICENSE_HEADER_END@ */ /* CFLocale.h - Copyright (c) 2002-2003, Apple, Inc. All rights reserved. + Copyright (c) 2002-2005, 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 +#include +#include + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_3 #if defined(__cplusplus) extern "C" { @@ -39,6 +39,8 @@ extern "C" { typedef const struct __CFLocale *CFLocaleRef; + + #if defined(__cplusplus) } #endif diff --git a/Makefile b/Makefile index 1535edf..8692a09 100644 --- a/Makefile +++ b/Makefile @@ -1,324 +1,120 @@ -# 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 +# Define sets of files to build, other info specific to this project. # -CURRENT_PROJECT_VERSION = 299.35 +NAME = CoreFoundation -# 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 +SUBPROJECTS = AppServices Base Collections Locale NumberDate Parsing PlugIn Preferences \ + RunLoop Stream String StringEncodings URL AppServices_PUBHEADERS = CFUserNotification.h AppServices_SOURCES = CFUserNotification.c -Base_PROJHEADERS = CFPriv.h CFInternal.h ForFoundationOnly.h CFRuntime.h \ - CFUtilities.h +Base_PROJHEADERS = CFInternal.h ForFoundationOnly.h auto_stubs.h CFRuntime.h CFUtilities.h +Base_PRIVHEADERS = CFPriv.h CFRuntime.h CFUtilities.h CFUtilitiesPriv.h Base_PUBHEADERS = CFBase.h CFByteOrder.h CoreFoundation.h CFUUID.h -Base_SOURCES = CFBase.c CFUtilities.c CFSortFunctions.c \ +Base_SOURCES = CFBase.c CFUtilities.c CFSortFunctions.c CFSystemDirectories.c \ CFRuntime.c CFFileUtilities.c CFPlatform.c CFUUID.c uuid.c -Collections_PROJHEADERS = CFStorage.h +Collections_PRIVHEADERS = CFStorage.h Collections_PUBHEADERS = CFArray.h CFBag.h CFBinaryHeap.h CFBitVector.h \ - CFData.h CFDictionary.h CFSet.h CFTree.h + CFData.h CFDictionary.h CFSet.h CFStorage.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_PROJHEADERS = CFBundle_BinaryTypes.h CFBundle_Internal.h CFPlugIn_Factory.h +PlugIn_PRIVHEADERS = CFBundlePriv.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 +Preferences_PUBHEADERS = CFPreferences.h +Preferences_SOURCES = CFApplicationPreferences.c CFPreferences.c CFXMLPreferencesDomain.c +RunLoop_PUBHEADERS = CFMachPort.h CFMessagePort.h CFRunLoop.h CFSocket.h +RunLoop_PRIVHEADERS = CFRunLoopPriv.h +RunLoop_SOURCES = CFMachPort.c CFMessagePort.c CFRunLoop.c CFSocket.c +ifeq "$(PLATFORM)" "CYGWIN" +RunLoop_PUBHEADERS += CFWindowsMessageQueue.h +RunLoop_SOURCES += CFWindowsMessageQueue.c +endif +Stream_PRIVHEADERS = CFStreamPriv.h CFStreamAbstract.h +Stream_PUBHEADERS = CFStream.h +Stream_SOURCES = CFStream.c CFConcreteStreams.c CFSocketStream.c +String_PRIVHEADERS = CFCharacterSetPriv.h CFStringDefaultEncoding.h +String_PUBHEADERS = CFCharacterSet.h CFString.h CFStringEncodingExt.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_PROJHEADERS = CFUniCharPriv.h CFStringEncodingConverterPriv.h +StringEncodings_PRIVHEADERS = CFUniChar.h CFStringEncodingConverter.h \ + CFUnicodeDecomposition.h CFUnicodePrecomposition.h \ + CFStringEncodingConverterExt.h 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 actual vars that are used by framework.make +PUBLIC_HFILES = $(foreach S, $(SUBPROJECTS), $(foreach F, $($(S)_PUBHEADERS), $(SRCROOT)/$(S).subproj/$(F))) +PRIVATE_HFILES = $(foreach S, $(SUBPROJECTS), $(foreach F, $($(S)_PRIVHEADERS), $(SRCROOT)/$(S).subproj/$(F))) +PROJECT_HFILES = $(foreach S, $(SUBPROJECTS), $(foreach F, $($(S)_PROJHEADERS), $(SRCROOT)/$(S).subproj/$(F))) +CFILES = $(foreach S, $(SUBPROJECTS), $(foreach F, $($(S)_SOURCES), $(SRCROOT)/$(S).subproj/$(F))) + + +-include nonOpenSource.make + +include framework.make + + +# +# Misc additional options +# + +CURRENT_PROJECT_VERSION = 338 -# 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 +# common items all build styles should be defining +CFLAGS += -DCF_BUILDING_CF=1 +CPPFLAGS += -DCF_BUILDING_CF=1 -installsrc: - $(SILENT) $(ECHO) "Installing source..." -ifeq "$(SRCROOT)" "." - $(SILENT) $(ECHO) "SRCROOT must be defined to be the destination directory; it cannot be '.'" - exit 1 +# base addr is set to come before CFNetwork - use the rebase MS command to see the sizes +# more info at http://msdn.microsoft.com/library/en-us/tools/tools/rebase.asp +ifeq "$(PLATFORM)" "CYGWIN" +C_WARNING_FLAGS += -Wno-endif-labels +CPP_WARNING_FLAGS += -Wno-endif-labels +LIBS += -lole32 -lws2_32 +LFLAGS += -Wl,--image-base=0x66000000 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 +ifeq "$(PLATFORM)" "Darwin" +CFLAGS += -F/System/Library/Frameworks/CoreServices.framework/Frameworks +CPPFLAGS += -F/System/Library/Frameworks/CoreServices.framework/Frameworks +LIBS += -licucore -lobjc +LFLAGS += -compatibility_version 150 -current_version $(CURRENT_PROJECT_VERSION) -Wl,-init,___CFInitialize 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 + +ifeq "$(PLATFORM)" "FreeBSD" +LFLAGS += -shared 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) +ifeq "$(PLATFORM)" "Linux" +LIBS += -lpthread 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 +CHARACTERSETS_INSTALLDIR = /usr/local/share/$(NAME) +else +CHARACTERSETS_INSTALLDIR = /System/Library/CoreServices endif + +# +# Additional steps we add to predefined targets +# + +install_after:: $(SILENT) $(MKDIRS) $(DSTROOT)/$(CHARACTERSETS_INSTALLDIR) -$(SILENT) $(CHMOD) 755 $(DSTROOT)/$(CHARACTERSETS_INSTALLDIR) $(SILENT) $(MKDIRS) $(DSTROOT)/$(CHARACTERSETS_INSTALLDIR)/CharacterSets @@ -327,110 +123,28 @@ endif $(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) 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 +prebuild_after:: 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) + $(SILENT) $(COPY_RECUR) CharacterSets $(RESOURCE_DIR) + $(SILENT) $(REMOVE_RECUR) $(RESOURCE_DIR)/CharacterSets/CVS +ifneq "$(PLATFORM)" "Darwin" +# All other platforms need the compatibility headers + $(SILENT) $(COPY) OSXCompatibilityHeaders/*.h $(PUBLIC_HEADER_DIR)/.. #*/ + $(SILENT) $(MKDIRS) $(PUBLIC_HEADER_DIR)/../GNUCompatibility + $(SILENT) $(COPY) OSXCompatibilityHeaders/GNUCompatibility/*.h $(PUBLIC_HEADER_DIR)/../GNUCompatibility #*/ 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 +clean_after:: + $(REMOVE_RECUR) -f $(RESOURCE_DIR)/CharacterSets 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!" +compile-after:: + $(SILENT) $(CC) $(CFLAGS) $(SRCROOT)/version.c -DVERSION=$(CURRENT_PROJECT_VERSION) -DUSER=$(USER) -c -o $(OFILE_DIR)/version.o -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 +test: + cd Tests; $(MAKE) test SYMROOT=$(SYMROOT) USE_OBJC=NO diff --git a/NumberDate.subproj/CFDate.c b/NumberDate.subproj/CFDate.c index dd62749..557e2d3 100644 --- a/NumberDate.subproj/CFDate.c +++ b/NumberDate.subproj/CFDate.c @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -53,86 +51,43 @@ struct __CFDate { CFAbsoluteTime _time; /* immutable */ }; -#if defined(__MACH__) +#if defined(__WIN32__) +// We should export this as SPI or API to clients - 3514284 +CFAbsoluteTime _CFAbsoluteTimeFromFileTime(const FILETIME *ft) { + CFAbsoluteTime ret = (CFTimeInterval)ft->dwHighDateTime * 429.49672960; + ret += (CFTimeInterval)ft->dwLowDateTime / 10000000.0; + ret -= (11644473600.0 + kCFAbsoluteTimeIntervalSince1970); + /* seconds between 1601 and 1970, 1970 and 2001 */ + return ret; +} +#endif + +#if defined(__MACH__) || defined(__WIN32__) static double __CFTSRRate = 0.0; -static CFAbsoluteTime __CFBootAbsoluteTime = 0.0; -static CFTimeInterval __CFLastSyncOffset = -1.0E+20; +static double __CF1_TSRRate = 0.0; __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); + return (CFTimeInterval)((double)tsr * __CF1_TSRRate); } #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__) +#if defined(__MACH__) || 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); +#elif defined(__WIN32__) + FILETIME ft; + GetSystemTimeAsFileTime(&ft); + ret = _CFAbsoluteTimeFromFileTime(&ft); +#else +#error CFAbsoluteTimeGetCurrent unimplemented for this platform #endif return ret; } @@ -173,6 +128,15 @@ __private_extern__ void __CFDateInitialize(void) { struct mach_timebase_info info; mach_timebase_info(&info); __CFTSRRate = (1.0E9 / (double)info.numer) * (double)info.denom; + __CF1_TSRRate = 1.0 / __CFTSRRate; +#endif +#if defined(__WIN32__) + LARGE_INTEGER freq; + if (!QueryPerformanceFrequency(&freq)) { + HALT; + } + __CFTSRRate = freq.QuadPart; + __CF1_TSRRate = 1.0 / __CFTSRRate; #endif __kCFDateTypeID = _CFRuntimeRegisterClass(&__CFDateClass); } diff --git a/NumberDate.subproj/CFDate.h b/NumberDate.subproj/CFDate.h index 91da62c..35d379a 100644 --- a/NumberDate.subproj/CFDate.h +++ b/NumberDate.subproj/CFDate.h @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -23,7 +21,7 @@ * @APPLE_LICENSE_HEADER_END@ */ /* CFDate.h - Copyright (c) 1998-2003, Apple, Inc. All rights reserved. + Copyright (c) 1998-2005, Apple, Inc. All rights reserved. */ #if !defined(__COREFOUNDATION_CFDATE__) diff --git a/NumberDate.subproj/CFNumber.c b/NumberDate.subproj/CFNumber.c index 84ea524..e63376d 100644 --- a/NumberDate.subproj/CFNumber.c +++ b/NumberDate.subproj/CFNumber.c @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -29,10 +27,10 @@ #include #include "CFInternal.h" -#include "CFUtilities.h" +#include "CFUtilitiesPriv.h" #include #include -#if defined(__WIN32__) +#if defined(__WIN32__) && !defined(__MINGW32__) && !defined(__CYGWIN__) #define isnan _isnan #define isinf !_finite #endif @@ -143,14 +141,10 @@ static struct __CFNumber __kCFNumberPositiveInfinity = { const CFNumberRef kCFNumberPositiveInfinity = &__kCFNumberPositiveInfinity; -/* Eight bits in base: - Bits 6..0: type (bits 4..0 is CFNumberType) +/* Seven bits in base: + Bits 4..0: CFNumber type + Bit 6: is unsigned number */ -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. @@ -225,12 +219,12 @@ CF_INLINE CFIndex __CFNumberSizeOfType(CFNumberType type) { // 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; \ + case kCFNumberSInt8Type: *(int8_t *)(resultPtr) = (int8_t)value; break; \ + case kCFNumberSInt16Type: *(int16_t *)(resultPtr) = (int16_t)value; break; \ + case kCFNumberSInt32Type: *(SInt32 *)(resultPtr) = (SInt32)value; break; \ + case kCFNumberSInt64Type: *(int64_t *)(resultPtr) = (int64_t)value; break; \ + case kCFNumberFloat32Type: *(Float32 *)(resultPtr) = (Float32)value; break; \ + case kCFNumberFloat64Type: *(Float64 *)(resultPtr) = (Float64)value; break; \ default: break; \ } @@ -238,10 +232,10 @@ CF_INLINE CFIndex __CFNumberSizeOfType(CFNumberType type) { // 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; + case kCFNumberSInt32Type: GET_VALUE(value->valSInt32, typeToGet, valuePtr); break; + case kCFNumberSInt64Type: GET_VALUE(value->valSInt64, typeToGet, valuePtr); break; + case kCFNumberFloat32Type: GET_VALUE(value->valFloat32, typeToGet, valuePtr); break; + case kCFNumberFloat64Type: GET_VALUE(value->valFloat64, typeToGet, valuePtr); break; default: break; \ } } @@ -446,39 +440,81 @@ __private_extern__ void __CFNumberInitialize(void) { _CFRuntimeSetInstanceTypeID(&__kCFNumberNaN, __kCFNumberTypeID); __kCFNumberNaN._base._isa = __CFISAForTypeID(__kCFNumberTypeID); - __CFBitfieldSetValue(__kCFNumberNaN._base._info, 6, 0, __kCFNumberIsNaN); + __CFBitfieldSetValue(__kCFNumberNaN._base._info, 4, 0, kCFNumberFloat64Type); __kCFNumberNaN.value.valFloat64 = *(double *)&dnan; _CFRuntimeSetInstanceTypeID(& __kCFNumberNegativeInfinity, __kCFNumberTypeID); __kCFNumberNegativeInfinity._base._isa = __CFISAForTypeID(__kCFNumberTypeID); - __CFBitfieldSetValue(__kCFNumberNegativeInfinity._base._info, 6, 0, __kCFNumberIsNegativeInfinity); + __CFBitfieldSetValue(__kCFNumberNegativeInfinity._base._info, 4, 0, kCFNumberFloat64Type); __kCFNumberNegativeInfinity.value.valFloat64 = *(double *)&negInf; _CFRuntimeSetInstanceTypeID(& __kCFNumberPositiveInfinity, __kCFNumberTypeID); __kCFNumberPositiveInfinity._base._isa = __CFISAForTypeID(__kCFNumberTypeID); - __CFBitfieldSetValue(__kCFNumberPositiveInfinity._base._info, 6, 0, __kCFNumberIsPositiveInfinity); + __CFBitfieldSetValue(__kCFNumberPositiveInfinity._base._info, 4, 0, kCFNumberFloat64Type); __kCFNumberPositiveInfinity.value.valFloat64 = *(double *)&posInf; } +Boolean _CFNumberIsU(CFNumberRef num) { + return __CFBitfieldGetValue(__kCFNumberPositiveInfinity._base._info, 6, 6); +} + +void _CFNumberSetU(CFNumberRef num, Boolean unsign) { + __CFBitfieldSetValue(__kCFNumberPositiveInfinity._base._info, 6, 6, (unsign ? 1 : 0)); +} + CFTypeID CFNumberGetTypeID(void) { return __kCFNumberTypeID; } +#define MinCachedInt (-1) +#define MaxCachedInt (12) +#define NotToBeCached (MinCachedInt - 1) +static CFNumberRef _CFNumberCache[MaxCachedInt - MinCachedInt + 1] = {NULL}; // Storing CFNumberRefs for SInt32 range MinCachedInt..MaxCachedInt +static CFSpinLock_t _CFNumberCacheLock = 0; + CFNumberRef CFNumberCreate(CFAllocatorRef allocator, CFNumberType type, const void *valuePtr) { CFNumberRef num; CFNumberType equivType, storageType; + int32_t valToBeCached = NotToBeCached; + + equivType = __CFNumberGetCanonicalTypeForType(type); - 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); + // Take care of some special cases; in some cases we will return here without actually creating a new CFNumber + switch (equivType) { + case kCFNumberFloat32Type: { + Float32 val = *(Float32 *)(valuePtr); + if (isnan(val)) return CFRetain(kCFNumberNaN); + if (isinf(val)) return CFRetain((val < 0.0) ? kCFNumberNegativeInfinity : kCFNumberPositiveInfinity); + break; + } + case kCFNumberFloat64Type: { + Float64 val = *(Float64 *)(valuePtr); + if (isnan(val)) return CFRetain(kCFNumberNaN); + if (isinf(val)) return CFRetain((val < 0.0) ? kCFNumberNegativeInfinity : kCFNumberPositiveInfinity); + break; + } + default: { + switch (equivType) { + case kCFNumberSInt64Type: {SInt64 val = *(SInt64 *)(valuePtr); if (val >= MinCachedInt && val <= MaxCachedInt) valToBeCached = val; break;} + case kCFNumberSInt32Type: {SInt32 val = *(SInt32 *)(valuePtr); if (val >= MinCachedInt && val <= MaxCachedInt) valToBeCached = val; break;} + case kCFNumberSInt16Type: {SInt16 val = *(SInt16 *)(valuePtr); if (val >= MinCachedInt && val <= MaxCachedInt) valToBeCached = val; break;} + case kCFNumberSInt8Type: {SInt8 val = *(SInt8 *)(valuePtr); if (val >= MinCachedInt && val <= MaxCachedInt) valToBeCached = val; break;} + default:; + } + if (valToBeCached != NotToBeCached) { // Even if not yet cached, this will assure that we cache it after the number is created + __CFSpinLock(&_CFNumberCacheLock); + CFNumberRef result = _CFNumberCache[valToBeCached - MinCachedInt]; + __CFSpinUnlock(&_CFNumberCacheLock); + if (result) return CFRetain(result); + // Turns out it's a number we want do cache, but don't have cached yet; so let's normalize it so we're only caching 32-bit int + valuePtr = &valToBeCached; + type = kCFNumberSInt32Type; + equivType = __CFNumberGetCanonicalTypeForType(type); + } + break; + } } - equivType = __CFNumberGetCanonicalTypeForType(type); storageType = __CFNumberGetStorageTypeForType(type); num = (CFNumberRef)_CFRuntimeCreateInstance(allocator, __kCFNumberTypeID, __CFNumberSizeOfType(storageType), NULL); @@ -487,6 +523,15 @@ CFNumberRef CFNumberCreate(CFAllocatorRef allocator, CFNumberType type, const vo } SET_VALUE((__CFNumberValue *)&(num->value), equivType, valuePtr); __CFBitfieldSetValue(((struct __CFNumber *)num)->_base._info, 6, 0, storageType); + + // If this was a number worth caching, cache it + if (valToBeCached != NotToBeCached) { + int slot = valToBeCached - MinCachedInt; + __CFSpinLock(&_CFNumberCacheLock); + if (_CFNumberCache[slot] == NULL) _CFNumberCache[slot] = num; + __CFSpinUnlock(&_CFNumberCacheLock); + if (_CFNumberCache[slot] == num) CFRetain(num); // Extra retain for the cached number + } return num; } diff --git a/NumberDate.subproj/CFNumber.h b/NumberDate.subproj/CFNumber.h index b83b658..e6c7437 100644 --- a/NumberDate.subproj/CFNumber.h +++ b/NumberDate.subproj/CFNumber.h @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -23,7 +21,7 @@ * @APPLE_LICENSE_HEADER_END@ */ /* CFNumber.h - Copyright (c) 1999-2003, Apple, Inc. All rights reserved. + Copyright (c) 1999-2005, Apple, Inc. All rights reserved. */ #if !defined(__COREFOUNDATION_CFNUMBER__) diff --git a/NumberDate.subproj/CFTimeZone.c b/NumberDate.subproj/CFTimeZone.c index 14af1dc..1b758ee 100644 --- a/NumberDate.subproj/CFTimeZone.c +++ b/NumberDate.subproj/CFTimeZone.c @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -29,7 +27,7 @@ #include #include -#include "CFUtilities.h" +#include "CFUtilitiesPriv.h" #include "CFInternal.h" #include #include @@ -64,8 +62,8 @@ 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 CFMutableDictionaryRef __CFTimeZoneCompatibilityMappingDict = NULL; +static CFMutableDictionaryRef __CFTimeZoneCompatibilityMappingDict2 = NULL; static CFSpinLock_t __CFTimeZoneCompatibilityMappingLock = 0; static CFArrayRef __CFKnownTimeZoneList = NULL; static CFMutableDictionaryRef __CFTimeZoneCache = NULL; @@ -136,13 +134,13 @@ static CFMutableArrayRef __CFCopyWindowsTimeZoneList() { } #endif +#if !defined(__WIN32__) 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) @@ -204,9 +202,9 @@ static CFMutableArrayRef __CFCopyRecursiveDirectoryList(const char *topDir) { CFRelease(result); return NULL; } -#endif return result; } +#endif typedef struct _CFTZPeriod { int32_t startSec; @@ -265,7 +263,7 @@ static CFComparisonResult __CFCompareTZPeriods(const void *val1, const void *val return kCFCompareGreaterThan; } -CF_INLINE CFIndex __CFBSearchTZPeriods(CFTimeZoneRef tz, CFAbsoluteTime at) { +static CFIndex __CFBSearchTZPeriods(CFTimeZoneRef tz, CFAbsoluteTime at) { CFTZPeriod elem; CFIndex idx; __CFTZPeriodInit(&elem, (int32_t)(float)floor(at), NULL, 0, false); @@ -369,7 +367,7 @@ static Boolean __CFParseTimeZoneData(CFAllocatorRef allocator, CFDataRef data, C 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)"); + if (__CFOASafe) __CFSetLastAllocationEventName(abbrs, "CFTimeZone (temp)"); for (idx = 0; idx < charcnt + 1; idx++) { abbrs[idx] = NULL; } @@ -415,6 +413,7 @@ static Boolean __CFParseTimeZoneData(CFAllocatorRef allocator, CFDataRef data, C // 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))) { + if (NULL != (*tzpp + idx)->abbrev) CFRelease((*tzpp + idx)->abbrev); cnt--; memmove((*tzpp + idx), (*tzpp + idx + 1), sizeof(CFTZPeriod) * (cnt - idx)); idx--; @@ -423,6 +422,7 @@ static Boolean __CFParseTimeZoneData(CFAllocatorRef allocator, CFDataRef data, C // 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))) { + if (NULL != (*tzpp + idx)->abbrev) CFRelease((*tzpp + idx)->abbrev); cnt--; memmove((*tzpp + idx), (*tzpp + idx + 1), sizeof(CFTZPeriod) * (cnt - idx)); idx--; @@ -834,18 +834,21 @@ CFTimeZoneRef CFTimeZoneCreate(CFAllocatorRef allocator, CFStringRef name, CFDat if (NULL != tzp) CFAllocatorDeallocate(allocator, tzp); return NULL; } - ((struct __CFTimeZone *)memory)->_name = CFRetain(name); - ((struct __CFTimeZone *)memory)->_data = CFRetain(data); + ((struct __CFTimeZone *)memory)->_name = CFStringCreateCopy(allocator, name); + ((struct __CFTimeZone *)memory)->_data = CFDataCreateCopy(allocator, data); ((struct __CFTimeZone *)memory)->_periods = tzp; ((struct __CFTimeZone *)memory)->_periodCnt = cnt; if (NULL == __CFTimeZoneCache) { - __CFTimeZoneCache = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFDictionaryKeyCallBacks kcb = kCFTypeDictionaryKeyCallBacks; + kcb.retain = kcb.release = NULL; + __CFTimeZoneCache = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kcb, &kCFTypeDictionaryValueCallBacks); } - CFDictionaryAddValue(__CFTimeZoneCache, name, memory); + CFDictionaryAddValue(__CFTimeZoneCache, ((struct __CFTimeZone *)memory)->_name, memory); __CFTimeZoneUnlockGlobal(); return memory; } +#if !defined(__WIN32__) static CFTimeZoneRef __CFTimeZoneCreateFixed(CFAllocatorRef allocator, int32_t seconds, CFStringRef name, int isDST) { CFTimeZoneRef result; CFDataRef data; @@ -853,7 +856,7 @@ static CFTimeZoneRef __CFTimeZoneCreateFixed(CFAllocatorRef allocator, int32_t s #if defined(__WIN32__) unsigned char *dataBytes = CFAllocatorAllocate(allocator, 52 + nameLen + 1, 0); if (!dataBytes) return NULL; - if (__CFOASafe) __CFSetLastAllocationEventName(*tzpp, "CFTimeZone (temp)"); + if (__CFOASafe) __CFSetLastAllocationEventName(dataBytes, "CFTimeZone (temp)"); #else unsigned char dataBytes[52 + nameLen + 1]; #endif @@ -873,6 +876,7 @@ static CFTimeZoneRef __CFTimeZoneCreateFixed(CFAllocatorRef allocator, int32_t s #endif return result; } +#endif // rounds offset to nearest minute CFTimeZoneRef CFTimeZoneCreateWithTimeIntervalFromGMT(CFAllocatorRef allocator, CFTimeInterval ti) { @@ -920,9 +924,6 @@ CFTimeZoneRef CFTimeZoneCreateWithName(CFAllocatorRef allocator, CFStringRef nam CFTimeZoneRef result = NULL; CFStringRef tzName = NULL; CFDataRef data = NULL; - CFURLRef baseURL, tempURL; - void *bytes; - CFIndex length; if (allocator == NULL) allocator = __CFGetDefaultAllocator(); __CFGenericValidateType(allocator, CFAllocatorGetTypeID()); @@ -939,6 +940,10 @@ CFTimeZoneRef CFTimeZoneCreateWithName(CFAllocatorRef allocator, CFStringRef nam } __CFTimeZoneUnlockGlobal(); #if !defined(__WIN32__) + CFURLRef baseURL, tempURL; + void *bytes; + CFIndex length; + baseURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, CFSTR(TZZONEINFO), kCFURLPOSIXPathStyle, true); if (tryAbbrev) { CFDictionaryRef abbrevs = CFTimeZoneCopyAbbreviationDictionary(); @@ -1105,7 +1110,7 @@ BOOL __CFTimeZoneGetWin32SystemTime(SYSTEMTIME * sys_time, CFAbsoluteTime time) * seconds between 1970 and 2001 : 978307200, * FILETIME - number of 100-nanosecond intervals since January 1, 1601 */ - l=(time+11644473600+978307200)*10000000; + l=(time+11644473600LL+978307200)*10000000; if (FileTimeToSystemTime(ftime,sys_time)) return TRUE; else @@ -1113,6 +1118,33 @@ BOOL __CFTimeZoneGetWin32SystemTime(SYSTEMTIME * sys_time, CFAbsoluteTime time) } #endif +CFTimeInterval _CFTimeZoneGetDSTOffset(CFTimeZoneRef tz, CFAbsoluteTime at) { +#if !defined(__WIN32__) +// #warning this does not work for non-CFTimeZoneRefs + CFIndex idx; + idx = __CFBSearchTZPeriods(tz, at); + // idx 0 is never returned if it is in DST + if (__CFTZPeriodIsDST(&(tz->_periods[idx]))) { + return __CFTZPeriodGMTOffset(&(tz->_periods[idx])) - __CFTZPeriodGMTOffset(&(tz->_periods[idx - 1])); + } +#endif + return 0.0; +} + +// returns 0.0 if there is no data for the next switch after 'at' +CFAbsoluteTime _CFTimeZoneGetNextDSTSwitch(CFTimeZoneRef tz, CFAbsoluteTime at) { +#if !defined(__WIN32__) +// #warning this does not work for non-CFTimeZoneRefs + CFIndex idx; + idx = __CFBSearchTZPeriods(tz, at); + if (tz->_periodCnt <= idx + 1) { + return 0.0; + } + return (CFAbsoluteTime)__CFTZPeriodStartSeconds(&(tz->_periods[idx + 1])); +#endif + return 0.0; +} + CFTimeInterval CFTimeZoneGetSecondsFromGMT(CFTimeZoneRef tz, CFAbsoluteTime at) { #if !defined(__WIN32__) CFIndex idx; @@ -1281,189 +1313,149 @@ CFTimeInterval _CFTimeZoneGetDSTDelta(CFTimeZoneRef tz, CFAbsoluteTime at) { 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); + __CFTimeZoneCompatibilityMappingDict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 112, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + + // Empty string means delete/ignore these + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Factory"), CFSTR("")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("US/Pacific-New"), CFSTR("")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Mideast/Riyadh87"), CFSTR("")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Mideast/Riyadh88"), CFSTR("")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Mideast/Riyadh89"), CFSTR("")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("SystemV/AST4"), CFSTR("")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("SystemV/AST4ADT"), CFSTR("")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("SystemV/CST6"), CFSTR("")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("SystemV/CST6CDT"), CFSTR("")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("SystemV/EST5"), CFSTR("")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("SystemV/EST5EDT"), CFSTR("")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("SystemV/HST10"), CFSTR("")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("SystemV/MST7"), CFSTR("")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("SystemV/MST7MDT"), CFSTR("")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("SystemV/PST8"), CFSTR("")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("SystemV/PST8PDT"), CFSTR("")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("SystemV/YST9"), CFSTR("")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("SystemV/YST9YDT"), CFSTR("")); + + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("America/Atka"), CFSTR("America/Adak")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("America/Ensenada"), CFSTR("America/Tijuana")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("America/Fort_Wayne"), CFSTR("America/Indianapolis")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("America/Indiana/Indianapolis"), CFSTR("America/Indianapolis")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("America/Kentucky/Louisville"), CFSTR("America/Louisville")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("America/Knox_IN"), CFSTR("America/Indiana/Knox")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("America/Porto_Acre"), CFSTR("America/Rio_Branco")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("America/Rosario"), CFSTR("America/Cordoba")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("America/Shiprock"), CFSTR("America/Denver")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("America/Virgin"), CFSTR("America/St_Thomas")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Antarctica/South_Pole"), CFSTR("Antarctica/McMurdo")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Asia/Ashkhabad"), CFSTR("Asia/Ashgabat")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Asia/Chungking"), CFSTR("Asia/Chongqing")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Asia/Macao"), CFSTR("Asia/Macau")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Asia/Tel_Aviv"), CFSTR("Asia/Jerusalem")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Asia/Thimbu"), CFSTR("Asia/Thimphu")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Asia/Ujung_Pandang"), CFSTR("Asia/Makassar")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Asia/Ulan_Bator"), CFSTR("Asia/Ulaanbaatar")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Australia/ACT"), CFSTR("Australia/Sydney")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Australia/LHI"), CFSTR("Australia/Lord_Howe")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Australia/NSW"), CFSTR("Australia/Sydney")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Australia/North"), CFSTR("Australia/Darwin")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Australia/Queensland"), CFSTR("Australia/Brisbane")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Australia/South"), CFSTR("Australia/Adelaide")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Australia/Tasmania"), CFSTR("Australia/Hobart")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Australia/Victoria"), CFSTR("Australia/Melbourne")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Australia/West"), CFSTR("Australia/Perth")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Australia/Yancowinna"), CFSTR("Australia/Broken_Hill")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Brazil/Acre"), CFSTR("America/Porto_Acre")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Brazil/DeNoronha"), CFSTR("America/Noronha")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Brazil/West"), CFSTR("America/Manaus")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("CST6CDT"), CFSTR("America/Chicago")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Canada/Central"), CFSTR("America/Winnipeg")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Canada/East-Saskatchewan"), CFSTR("America/Regina")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Canada/Pacific"), CFSTR("America/Vancouver")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Canada/Yukon"), CFSTR("America/Whitehorse")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Chile/Continental"), CFSTR("America/Santiago")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Chile/EasterIsland"), CFSTR("Pacific/Easter")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Cuba"), CFSTR("America/Havana")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("EST5EDT"), CFSTR("America/New_York")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Egypt"), CFSTR("Africa/Cairo")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Eire"), CFSTR("Europe/Dublin")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Etc/GMT+0"), CFSTR("GMT")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Etc/GMT-0"), CFSTR("GMT")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Etc/GMT0"), CFSTR("GMT")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Etc/Greenwich"), CFSTR("GMT")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Etc/Universal"), CFSTR("UTC")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Etc/Zulu"), CFSTR("UTC")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Europe/Nicosia"), CFSTR("Asia/Nicosia")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Europe/Tiraspol"), CFSTR("Europe/Chisinau")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("GB-Eire"), CFSTR("Europe/London")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("GB"), CFSTR("Europe/London")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("GMT+0"), CFSTR("GMT")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("GMT-0"), CFSTR("GMT")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("GMT0"), CFSTR("GMT")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Greenwich"), CFSTR("GMT")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Hongkong"), CFSTR("Asia/Hong_Kong")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Iceland"), CFSTR("Atlantic/Reykjavik")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Iran"), CFSTR("Asia/Tehran")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Israel"), CFSTR("Asia/Jerusalem")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Jamaica"), CFSTR("America/Jamaica")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Kwajalein"), CFSTR("Pacific/Kwajalein")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Libya"), CFSTR("Africa/Tripoli")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("MST7MDT"), CFSTR("America/Denver")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Mexico/BajaNorte"), CFSTR("America/Tijuana")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Mexico/BajaSur"), CFSTR("America/Mazatlan")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Mexico/General"), CFSTR("America/Mexico_City")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("NZ-CHAT"), CFSTR("Pacific/Chatham")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("NZ"), CFSTR("Pacific/Auckland")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Navajo"), CFSTR("America/Denver")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("PRC"), CFSTR("Asia/Shanghai")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("PST8PDT"), CFSTR("America/Los_Angeles")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Pacific/Samoa"), CFSTR("Pacific/Pago_Pago")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Poland"), CFSTR("Europe/Warsaw")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Portugal"), CFSTR("Europe/Lisbon")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("ROC"), CFSTR("Asia/Taipei")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("ROK"), CFSTR("Asia/Seoul")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Singapore"), CFSTR("Asia/Singapore")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Turkey"), CFSTR("Europe/Istanbul")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("UCT"), CFSTR("UTC")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("US/Alaska"), CFSTR("America/Anchorage")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("US/Aleutian"), CFSTR("America/Adak")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("US/Arizona"), CFSTR("America/Phoenix")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("US/East-Indiana"), CFSTR("America/Indianapolis")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("US/Hawaii"), CFSTR("Pacific/Honolulu")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("US/Indiana-Starke"), CFSTR("America/Indiana/Knox")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("US/Michigan"), CFSTR("America/Detroit")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("US/Samoa"), CFSTR("Pacific/Pago_Pago")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Universal"), CFSTR("UTC")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("W-SU"), CFSTR("Europe/Moscow")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict, CFSTR("Zulu"), CFSTR("UTC")); } 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); - } + __CFTimeZoneCompatibilityMappingDict2 = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 16, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict2, CFSTR("Asia/Dacca"), CFSTR("Asia/Dhaka")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict2, CFSTR("Asia/Istanbul"), CFSTR("Europe/Istanbul")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict2, CFSTR("Australia/Canberra"), CFSTR("Australia/Sydney")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict2, CFSTR("Brazil/East"), CFSTR("America/Sao_Paulo")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict2, CFSTR("Canada/Atlantic"), CFSTR("America/Halifax")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict2, CFSTR("Canada/Eastern"), CFSTR("America/Montreal")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict2, CFSTR("Canada/Mountain"), CFSTR("America/Edmonton")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict2, CFSTR("Canada/Newfoundland"), CFSTR("America/St_Johns")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict2, CFSTR("Canada/Saskatchewan"), CFSTR("America/Regina")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict2, CFSTR("Japan"), CFSTR("Asia/Tokyo")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict2, CFSTR("US/Central"), CFSTR("America/Chicago")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict2, CFSTR("US/Eastern"), CFSTR("America/New_York")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict2, CFSTR("US/Mountain"), CFSTR("America/Denver")); + CFDictionaryAddValue(__CFTimeZoneCompatibilityMappingDict2, CFSTR("US/Pacific"), CFSTR("America/Los_Angeles")); + } dict = __CFTimeZoneCompatibilityMappingDict2 ? CFRetain(__CFTimeZoneCompatibilityMappingDict2) : NULL; __CFTimeZoneUnlockCompatibilityMapping(); return dict; diff --git a/NumberDate.subproj/CFTimeZone.h b/NumberDate.subproj/CFTimeZone.h index 603ad34..0e26e71 100644 --- a/NumberDate.subproj/CFTimeZone.h +++ b/NumberDate.subproj/CFTimeZone.h @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -23,7 +21,7 @@ * @APPLE_LICENSE_HEADER_END@ */ /* CFTimeZone.h - Copyright (c) 1998-2003, Apple, Inc. All rights reserved. + Copyright (c) 1998-2005, Apple, Inc. All rights reserved. */ #if !defined(__COREFOUNDATION_CFTIMEZONE__) diff --git a/Parsing.subproj/CFBinaryPList.c b/Parsing.subproj/CFBinaryPList.c index 9ada609..9495ad1 100644 --- a/Parsing.subproj/CFBinaryPList.c +++ b/Parsing.subproj/CFBinaryPList.c @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -38,13 +36,18 @@ #include #include #include -#include "ForFoundationOnly.h" +#include #include #include #include #include "CFInternal.h" +CF_INLINE CFTypeID __CFGenericTypeID_genericobj_inline(const void *cf) { + CFTypeID typeID = __CFBitfieldGetValue(((const CFRuntimeBase *)cf)->_info, 15, 8); + return CF_IS_OBJC(typeID, cf) ? CFGetTypeID(cf) : typeID; +} + struct __CFKeyedArchiverUID { CFRuntimeBase _base; uint32_t _value; @@ -106,19 +109,21 @@ typedef struct { uint8_t buffer[8192 - 16]; } __CFBinaryPlistWriteBuffer; +CF_INLINE void writeBytes(__CFBinaryPlistWriteBuffer *buf, const UInt8 *bytes, CFIndex length) { + if (buf->streamIsData) { + CFDataAppendBytes((CFMutableDataRef)buf->stream, bytes, length); + } else { + CFWriteStreamWrite((CFWriteStreamRef)buf->stream, bytes, length); + } +} + 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 { - } + writeBytes(buf, buf->buffer, buf->used); buf->written += buf->used; buf->used = 0; - if (buf->streamIsData) { - CFDataAppendBytes((CFMutableDataRef)buf->stream, buffer, count); - } else { - } + writeBytes(buf, buffer, count); buf->written += count; return; } @@ -126,10 +131,7 @@ static void bufferWrite(__CFBinaryPlistWriteBuffer *buf, const uint8_t *buffer, 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 { - } + writeBytes(buf, buf->buffer, sizeof(buf->buffer)); buf->written += sizeof(buf->buffer); memmove(buf->buffer, buffer + copyLen, count - copyLen); buf->used = count - copyLen; @@ -137,10 +139,7 @@ static void bufferWrite(__CFBinaryPlistWriteBuffer *buf, const uint8_t *buffer, } static void bufferFlush(__CFBinaryPlistWriteBuffer *buf) { - if (buf->streamIsData) { - CFDataAppendBytes((CFMutableDataRef)buf->stream, buf->buffer, buf->used); - } else { - } + writeBytes(buf, buf->buffer, buf->used); buf->written += buf->used; buf->used = 0; } @@ -163,7 +162,7 @@ OBJECT TABLE 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 + string 0110 nnnn [int] ... // Unicode string, nnnn is # of chars, else 1111 then int count, then big-endian 2-byte uint16_t 0111 xxxx // unused uid 1000 nnnn ... // nnnn+1 is # of bytes 1001 xxxx // unused @@ -188,8 +187,8 @@ TRAILER */ -static CFTypeID stringtype = -1, datatype = -1, numbertype = -1, booltype = -1; -static CFTypeID datetype = -1, dicttype = -1, arraytype = -1; +static CFTypeID stringtype = -1, datatype = -1, numbertype = -1, datetype = -1; +static CFTypeID booltype = -1, dicttype = -1, arraytype = -1; static void _appendInt(__CFBinaryPlistWriteBuffer *buf, uint64_t bigint) { uint8_t marker; @@ -239,29 +238,39 @@ 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 (__CFGenericTypeID_genericobj_inline(cf1) != __CFGenericTypeID_genericobj_inline(cf2)) return false; + if (__CFGenericTypeID_genericobj_inline(cf1) == numbertype) { 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) { +static void _flattenPlist(CFPropertyListRef plist, CFMutableArrayRef objlist, CFMutableDictionaryRef objtable, CFMutableSetRef uniquingsets[]) { CFPropertyListRef unique; uint32_t refnum; - CFTypeID type = CFGetTypeID(plist); - CFIndex idx, before, after; + CFTypeID type = __CFGenericTypeID_genericobj_inline(plist); + CFIndex idx; 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); + // Do not unique dictionaries or arrays, because: they + // are slow to compare, and have poor hash codes. + // Uniquing bools is unnecessary. + int which = -1; + if (stringtype == type) { + which = 0; + } else if (numbertype == type) { + which = 1; + } else if (datatype == type) { + which = 2; + } else if (datetype == type) { + which = 3; + } + if (1 && -1 != which) { + CFMutableSetRef uniquingset = uniquingsets[which]; + CFIndex before = CFSetGetCount(uniquingset); CFSetAddValue(uniquingset, plist); - after = CFSetGetCount(uniquingset); + CFIndex after = CFSetGetCount(uniquingset); if (after == before) { // already in set unique = CFSetGetValue(uniquingset, plist); if (unique != plist) { @@ -279,7 +288,7 @@ static void _flattenPlist(CFPropertyListRef plist, CFMutableArrayRef objlist, CF 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); + _flattenPlist(list[idx], objlist, objtable, uniquingsets); } if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list); } else if (arraytype == type) { @@ -287,16 +296,15 @@ static void _flattenPlist(CFPropertyListRef plist, CFMutableArrayRef objlist, CF 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); + _flattenPlist(list[idx], objlist, objtable, uniquingsets); } if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list); } } -// stream must be a CFMutableDataRef +// stream can be a CFWriteStreamRef or a CFMutableDataRef CFIndex __CFBinaryPlistWriteToStream(CFPropertyListRef plist, CFTypeRef stream) { CFMutableDictionaryRef objtable; - CFMutableSetRef uniquingset; CFMutableArrayRef objlist; CFBinaryPlistTrailer trailer; uint64_t *offsets, length_so_far; @@ -315,16 +323,26 @@ CFIndex __CFBinaryPlistWriteToStream(CFPropertyListRef plist, CFTypeRef stream) arraytype = CFArrayGetTypeID(); } objtable = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, NULL); - _CFDictionarySetCapacity(objtable, 320); + _CFDictionarySetCapacity(objtable, 640); objlist = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, NULL); - _CFArraySetCapacity(objlist, 320); + _CFArraySetCapacity(objlist, 640); cb.equal = __plistUniquingEqual; - uniquingset = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &cb); - _CFSetSetCapacity(uniquingset, 320); - - _flattenPlist(plist, objlist, objtable, uniquingset); + CFMutableSetRef uniquingsets[4]; + uniquingsets[0] = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &cb); + _CFSetSetCapacity(uniquingsets[0], 1000); + uniquingsets[1] = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &cb); + _CFSetSetCapacity(uniquingsets[1], 500); + uniquingsets[2] = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &cb); + _CFSetSetCapacity(uniquingsets[2], 250); + uniquingsets[3] = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &cb); + _CFSetSetCapacity(uniquingsets[3], 250); + + _flattenPlist(plist, objlist, objtable, uniquingsets); - CFRelease(uniquingset); + CFRelease(uniquingsets[0]); + CFRelease(uniquingsets[1]); + CFRelease(uniquingsets[2]); + CFRelease(uniquingsets[3]); cnt = CFArrayGetCount(objlist); offsets = CFAllocatorAllocate(kCFAllocatorSystemDefault, cnt * sizeof(*offsets), 0); @@ -469,6 +487,7 @@ CFIndex __CFBinaryPlistWriteToStream(CFPropertyListRef plist, CFTypeRef stream) } else { CFRelease(objtable); CFRelease(objlist); + CFAllocatorDeallocate(kCFAllocatorSystemDefault, buf); CFAllocatorDeallocate(kCFAllocatorSystemDefault, offsets); return 0; } @@ -500,9 +519,8 @@ CFIndex __CFBinaryPlistWriteToStream(CFPropertyListRef plist, CFTypeRef stream) 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; +bool __CFBinaryPlistGetTopLevelInfo(const uint8_t *databytes, uint64_t datalen, uint8_t *marker, uint64_t *offset, CFBinaryPlistTrailer *trailer) { + const uint8_t *bytesptr; CFBinaryPlistTrailer trail; uint64_t off; CFIndex idx; @@ -516,8 +534,6 @@ bool __CFBinaryPlistGetTopLevelInfo(CFDataRef data, uint8_t *marker, uint64_t *o 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)); @@ -565,12 +581,12 @@ static uint64_t _getOffsetOfRefAt(const uint8_t *databytes, const uint8_t *bytes return off; } -bool __CFBinaryPlistGetOffsetForValueFromArray(CFDataRef data, uint64_t startOffset, const CFBinaryPlistTrailer *trailer, CFIndex idx, uint64_t *offset) { - const uint8_t *databytes, *bytesptr; +bool __CFBinaryPlistGetOffsetForValueFromArray(const uint8_t *databytes, uint64_t datalen, uint64_t startOffset, const CFBinaryPlistTrailer *trailer, CFIndex idx, uint64_t *offset) { + const uint8_t *bytesptr; uint8_t marker; CFIndex cnt; uint64_t off; - databytes = CFDataGetBytePtr(data); + marker = *(databytes + startOffset); if ((marker & 0xf0) != kCFBinaryPlistMarkerArray) return false; cnt = (marker & 0x0f); @@ -584,19 +600,18 @@ bool __CFBinaryPlistGetOffsetForValueFromArray(CFDataRef data, uint64_t startOff } if (cnt <= idx) return false; off = _getOffsetOfRefAt(databytes, bytesptr + idx * trailer->_objectRefSize, trailer); - if ((uint64_t)CFDataGetLength(data) <= off) return false; + if (datalen <= 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; +bool __CFBinaryPlistGetOffsetForValueFromDictionary(const uint8_t *databytes, uint64_t datalen, uint64_t startOffset, const CFBinaryPlistTrailer *trailer, CFTypeRef key, uint64_t *koffset, uint64_t *voffset) { + const uint8_t *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); @@ -609,7 +624,7 @@ bool __CFBinaryPlistGetOffsetForValueFromDictionary(CFDataRef data, uint64_t sta } for (keyn = 0; keyn < cnt; keyn++) { off = _getOffsetOfRefAt(databytes, refsptr, trailer); - if ((uint64_t)CFDataGetLength(data) <= off) return false; + if (datalen <= off) return false; refsptr += trailer->_objectRefSize; bytesptr = databytes + off; marker = *bytesptr & 0xf0; @@ -638,7 +653,7 @@ bool __CFBinaryPlistGetOffsetForValueFromDictionary(CFDataRef data, uint64_t sta } if (koffset) *koffset = off; off = _getOffsetOfRefAt(databytes, refsptr + (cnt - 1) * trailer->_objectRefSize, trailer); - if ((uint64_t)CFDataGetLength(data) <= off) return false; + if (datalen <= off) return false; if (voffset) *voffset = off; return true; } else { @@ -689,14 +704,15 @@ static bool _getDoubleFromData(const uint8_t *datap, double *vp) { 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; +bool __CFBinaryPlistCreateObject(const uint8_t *databytes, uint64_t datalen, uint64_t startOffset, const CFBinaryPlistTrailer *trailer, CFAllocatorRef allocator, CFOptionFlags mutabilityOption, CFMutableDictionaryRef objects, CFPropertyListRef *plist) { + const uint8_t *bytesptr; uint64_t off; uint8_t marker; CFIndex idx, cnt; uint64_t bigint; UniChar *chars; CFPropertyListRef *list, buffer[256]; + CFAllocatorRef listAllocator; if (objects) { *plist = CFDictionaryGetValue(objects, (const void *)(intptr_t)startOffset); @@ -706,7 +722,6 @@ bool __CFBinaryPlistCreateObject(CFDataRef data, uint64_t startOffset, const CFB } } - databytes = CFDataGetBytePtr(data); marker = *(databytes + startOffset); switch (marker & 0xf0) { case kCFBinaryPlistMarkerNull: @@ -829,18 +844,25 @@ bool __CFBinaryPlistCreateObject(CFDataRef data, uint64_t startOffset, const CFB cnt = (CFIndex)bigint; } list = (cnt <= 256) ? buffer : CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(CFPropertyListRef) * cnt, 0); + listAllocator = (list == buffer ? kCFAllocatorNull : kCFAllocatorSystemDefault); 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 (datalen <= off) return false; + if (!__CFBinaryPlistCreateObject(databytes, datalen, off, trailer, allocator, mutabilityOption, objects, &pl)) { + if (!CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { + while (idx--) { + CFRelease(list[idx]); + } } if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list); return false; } - list[idx] = pl; + if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { + CF_WRITE_BARRIER_BASE_ASSIGN(listAllocator, list, list[idx], CFMakeCollectable(pl)); + } else { + list[idx] = pl; + } bytesptr += trailer->_objectRefSize; } *plist = _CFArrayCreate_ex(allocator, (mutabilityOption != kCFPropertyListImmutable), list, cnt); @@ -857,18 +879,25 @@ bool __CFBinaryPlistCreateObject(CFDataRef data, uint64_t startOffset, const CFB } cnt *= 2; list = (cnt <= 256) ? buffer : CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(CFPropertyListRef) * cnt, 0); + listAllocator = (list == buffer ? kCFAllocatorNull : kCFAllocatorSystemDefault); 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 (datalen <= off) return false; + if (!__CFBinaryPlistCreateObject(databytes, datalen, off, trailer, allocator, mutabilityOption, objects, &pl)) { + if (!CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { + while (idx--) { + CFRelease(list[idx]); + } } if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list); return false; } - list[idx] = pl; + if (CF_IS_COLLECTABLE_ALLOCATOR(allocator)) { + CF_WRITE_BARRIER_BASE_ASSIGN(listAllocator, list, list[idx], CFMakeCollectable(pl)); + } else { + list[idx] = pl; + } bytesptr += trailer->_objectRefSize; } *plist = _CFDictionaryCreate_ex(allocator, (mutabilityOption != kCFPropertyListImmutable), list, list + cnt / 2, cnt / 2); @@ -884,9 +913,11 @@ __private_extern__ bool __CFTryParseBinaryPlist(CFAllocatorRef allocator, CFData CFBinaryPlistTrailer trailer; uint64_t offset; CFPropertyListRef pl; + const uint8_t *databytes = CFDataGetBytePtr(data); + uint64_t datalen = CFDataGetLength(data); - if (8 <= CFDataGetLength(data) && __CFBinaryPlistGetTopLevelInfo(data, &marker, &offset, &trailer)) { - if (__CFBinaryPlistCreateObject(data, offset, &trailer, allocator, option, NULL, &pl)) { + if (8 <= datalen && __CFBinaryPlistGetTopLevelInfo(databytes, datalen, &marker, &offset, &trailer)) { + if (__CFBinaryPlistCreateObject(databytes, datalen, offset, &trailer, allocator, option, NULL, &pl)) { if (plist) *plist = pl; } else { if (plist) *plist = NULL; diff --git a/Parsing.subproj/CFPropertyList.c b/Parsing.subproj/CFPropertyList.c index 37f01c1..e173b1d 100644 --- a/Parsing.subproj/CFPropertyList.c +++ b/Parsing.subproj/CFPropertyList.c @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -31,21 +29,19 @@ #include #include #include -#include "CFUtilities.h" +#include "CFUtilitiesPriv.h" #include "CFStringEncodingConverter.h" #include "CFInternal.h" +#include +#if defined(__MACH__) +#include +#endif // __MACH__ #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; @@ -423,20 +419,30 @@ static void _CFAppendXML0(CFTypeRef object, UInt32 indentation, CFMutableDataRef _plistAppendCharacters(xmlString, CFXMLPlistTags[STRING_IX], STRING_TAG_LENGTH); _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); + _appendIndents(indentation+1, xmlString); + _plistAppendUTF8CString(xmlString, "<"); + _plistAppendCharacters(xmlString, CFXMLPlistTags[KEY_IX], KEY_TAG_LENGTH); + _plistAppendUTF8CString(xmlString, ">"); + _appendEscapedString(CFSTR("CF$UID"), xmlString); + _plistAppendUTF8CString(xmlString, "\n"); + _appendIndents(indentation + 1, xmlString); _plistAppendUTF8CString(xmlString, "<"); - _plistAppendCharacters(xmlString, CFXMLPlistTags[KEY_IX], KEY_TAG_LENGTH); + _plistAppendCharacters(xmlString, CFXMLPlistTags[INTEGER_IX], INTEGER_TAG_LENGTH); _plistAppendUTF8CString(xmlString, ">"); - _appendEscapedString(CFSTR("CF$UID"), xmlString); + + uint64_t v = _CFKeyedArchiverUIDGetValue(object); + CFNumberRef num = CFNumberCreate(kCFAllocatorSystemDefault, kCFNumberSInt64Type, &v); + _plistAppendFormat(xmlString, CFSTR("%@"), num); + CFRelease(num); + _plistAppendUTF8CString(xmlString, "\n"); - _CFAppendXML0(num, indentation+1, xmlString); _appendIndents(indentation, xmlString); _plistAppendUTF8CString(xmlString, "allocator, tmpbuflen, 0); + // GrP GC: collector shouldn't scan this raw data + tmpbuf = CFAllocatorAllocate(pInfo->allocator, tmpbuflen, AUTO_MEMORY_UNSCANNED); for (; pInfo->curr < pInfo->end; pInfo->curr++) { UniChar c = *(pInfo->curr); if (c == '<') { @@ -852,7 +859,7 @@ static CFDataRef __CFPLDataDecode(_CFXMLPlistParseInfo *pInfo, Boolean mutable) if (0 == (cntr & 0x3)) { if (tmpbuflen <= tmpbufpos + 2) { tmpbuflen <<= 2; - tmpbuf = CFAllocatorReallocate(pInfo->allocator, tmpbuf, tmpbuflen, 0); + tmpbuf = CFAllocatorReallocate(pInfo->allocator, tmpbuf, tmpbuflen, AUTO_MEMORY_UNSCANNED); } tmpbuf[tmpbufpos++] = (acc >> 16) & 0xff; if (numeq < 2) @@ -1110,6 +1117,7 @@ static CFStringRef getString(_CFXMLPlistParseInfo *pInfo) { while (!pInfo->errorString && pInfo->curr < pInfo->end) { UniChar ch = *(pInfo->curr); if (ch == '<') { + if (pInfo->curr + 1 >= pInfo->end) break; // Could be a CDSect; could be the end of the string if (*(pInfo->curr+1) != '!') break; // End of the string _catFromMarkToBuf(mark, pInfo->curr, &string, pInfo->allocator); @@ -1834,14 +1842,15 @@ void __CFSetNastyFile(CFTypeRef cf) { } extern bool __CFTryParseBinaryPlist(CFAllocatorRef allocator, CFDataRef data, CFOptionFlags option, CFPropertyListRef *plist, CFStringRef *errorString); -int _CFPropertyListAllowNonUTF8 = 1; +int32_t _CFPropertyListAllowNonUTF8 = 0; -static CFTypeRef _CFPropertyListCreateFromXMLData(CFAllocatorRef allocator, CFDataRef xmlData, CFOptionFlags option, CFStringRef *errorString, Boolean allowNewTypes, CFPropertyListFormat *format) { +CFTypeRef _CFPropertyListCreateFromXMLData(CFAllocatorRef allocator, CFDataRef xmlData, CFOptionFlags option, CFStringRef *errorString, Boolean allowNewTypes, CFPropertyListFormat *format) { CFStringEncoding encoding; CFStringRef xmlString; UInt32 length; CFPropertyListRef plist; + if (errorString) *errorString = NULL; if (!xmlData || CFDataGetLength(xmlData) == 0) { if (errorString) { *errorString = CFSTR("Cannot parse a NULL or zero-length data"); @@ -1858,7 +1867,6 @@ static CFTypeRef _CFPropertyListCreateFromXMLData(CFAllocatorRef allocator, CFDa allocator = allocator ? allocator : __CFGetDefaultAllocator(); CFRetain(allocator); - if (errorString) *errorString = NULL; encoding = encodingForXMLData(xmlData, errorString); // 0 is an error return, NOT MacRoman. if (encoding == 0) { @@ -1869,10 +1877,7 @@ static CFTypeRef _CFPropertyListCreateFromXMLData(CFAllocatorRef allocator, CFDa } xmlString = CFStringCreateWithBytes(allocator, CFDataGetBytePtr(xmlData), CFDataGetLength(xmlData), encoding, true); - if (NULL == xmlString && !_CFExecutableLinkedOnOrAfter(CFSystemVersionMerlot) && _CFPropertyListAllowNonUTF8) { // conversion failed, probably because not in proper encoding - static int yanmode = -1; - if (-1 == yanmode) yanmode = (getenv("YanMode") != NULL); - if (1 != yanmode && _CFExecutableLinkedOnOrAfter(CFSystemVersionJaguar)) { + if (NULL == xmlString && (!_CFExecutableLinkedOnOrAfter(CFSystemVersionChablis) || _CFPropertyListAllowNonUTF8)) { // conversion failed, probably because not in proper encoding CFTypeRef f = (__CFNastyFile__) ? (__CFNastyFile__) : CFSTR("(UNKNOWN)"); if (encoding == kCFStringEncodingUTF8) { CFLog(0, CFSTR("\n\tCFPropertyListCreateFromXMLData(): plist parse failed; the data is not proper UTF-8. The file name for this data could be:\n\t%@\n\tThe parser will retry as in 10.2, but the problem should be corrected in the plist."), f); @@ -1881,7 +1886,6 @@ static CFTypeRef _CFPropertyListCreateFromXMLData(CFAllocatorRef allocator, CFDa CFLog(0, CFSTR("\n\tCFPropertyListCreateFromXMLData(): conversion of data failed.\n\tThe file is not in the encoding specified in XML header if XML.\n\tThe file name for this data could be:\n\t\t%@\n."), f); #endif } - } // Call __CFStringCreateImmutableFunnel3() the same way CFStringCreateWithBytes() does, except with the addt'l flag if (encoding == kCFStringEncodingUTF8) xmlString = __CFStringCreateImmutableFunnel3(allocator, CFDataGetBytePtr(xmlData), CFDataGetLength(xmlData), kCFStringEncodingUTF8, true, true, false, false, false, (void *)-1 /* ALLOCATORSFREEFUNC */, kCFStringEncodingLenientUTF8Conversion); } @@ -1949,6 +1953,84 @@ CFTypeRef CFPropertyListCreateFromXMLData(CFAllocatorRef allocator, CFDataRef xm return _CFPropertyListCreateFromXMLData(allocator, xmlData, option, errorString, true, NULL); } +CFIndex CFPropertyListWriteToStream(CFPropertyListRef propertyList, CFWriteStreamRef stream, CFPropertyListFormat format, CFStringRef *errorString) { + CFAssert1(stream != NULL, __kCFLogAssertion, "%s(): NULL stream not allowed", __PRETTY_FUNCTION__); + CFAssert2(format == kCFPropertyListOpenStepFormat || format == kCFPropertyListXMLFormat_v1_0 || format == kCFPropertyListBinaryFormat_v1_0, __kCFLogAssertion, "%s(): Unrecognized option %d", __PRETTY_FUNCTION__, format); + CFAssert1(propertyList != NULL, __kCFLogAssertion, "%s(): Cannot be called with a NULL property list", __PRETTY_FUNCTION__); + __CFAssertIsPList(propertyList); + CFAssert1(CFWriteStreamGetTypeID() == CFGetTypeID(stream), __kCFLogAssertion, "%s(): stream argument is not a write stream", __PRETTY_FUNCTION__); + CFAssert1(kCFStreamStatusOpen == CFWriteStreamGetStatus(stream) || kCFStreamStatusWriting == CFWriteStreamGetStatus(stream), __kCFLogAssertion, "%s(): stream is not open", __PRETTY_FUNCTION__); + + if (errorString) *errorString = NULL; + if (!CFPropertyListIsValid(propertyList, format)) { + if (errorString) *errorString = CFRetain(CFSTR("Property list invalid for format")); + return 0; + } + if (format == kCFPropertyListOpenStepFormat) { + if (errorString) *errorString = CFRetain(CFSTR("Property list format kCFPropertyListOpenStepFormat not supported for writing")); + return 0; + } + if (format == kCFPropertyListXMLFormat_v1_0) { + CFDataRef data = CFPropertyListCreateXMLData(kCFAllocatorSystemDefault, propertyList); + CFIndex len = data ? CFDataGetLength(data) : 0; + CFIndex ret = CFWriteStreamWrite(stream, CFDataGetBytePtr(data), len); + CFRelease(data); + if (len != ret) { + } + return len; + } + if (format == kCFPropertyListBinaryFormat_v1_0) { + CFIndex len = __CFBinaryPlistWriteToStream(propertyList, stream); + return len; + } + if (errorString) *errorString = CFRetain(CFSTR("Unknown format option")); + return 0; +} + +static void __CFConvertReadStreamToBytes(CFReadStreamRef stream, CFIndex max, uint8_t **buffer, CFIndex *length) { + int32_t buflen = 0, bufsize = 0, retlen; + uint8_t *buf = NULL, sbuf[8192]; + for (;;) { + retlen = CFReadStreamRead(stream, sbuf, __CFMin(8192, max)); + max -= retlen; + if (retlen <= 0 || max <= 0) { + *buffer = buf; + *length = buflen; + return; + } + if (bufsize < buflen + retlen) { + bufsize = 2 * bufsize; + if (bufsize < buflen + retlen) bufsize = buflen + retlen; + buf = CFAllocatorReallocate(kCFAllocatorSystemDefault, buf, bufsize, 0); + } + memmove(buf + buflen, sbuf, retlen); + buflen += retlen; + } +} + +CFPropertyListRef CFPropertyListCreateFromStream(CFAllocatorRef allocator, CFReadStreamRef stream, CFIndex length, CFOptionFlags mutabilityOption, CFPropertyListFormat *format, CFStringRef *errorString) { + CFPropertyListRef pl; + CFDataRef data; + CFIndex buflen = 0; + uint8_t *buffer = NULL; + CFAssert1(stream != NULL, __kCFLogAssertion, "%s(): NULL stream not allowed", __PRETTY_FUNCTION__); + CFAssert1(CFReadStreamGetTypeID() == CFGetTypeID(stream), __kCFLogAssertion, "%s(): stream argument is not a read stream", __PRETTY_FUNCTION__); + CFAssert1(kCFStreamStatusOpen == CFReadStreamGetStatus(stream) || kCFStreamStatusReading == CFReadStreamGetStatus(stream), __kCFLogAssertion, "%s(): stream is not open", __PRETTY_FUNCTION__); + CFAssert2(mutabilityOption == kCFPropertyListImmutable || mutabilityOption == kCFPropertyListMutableContainers || mutabilityOption == kCFPropertyListMutableContainersAndLeaves, __kCFLogAssertion, "%s(): Unrecognized option %d", __PRETTY_FUNCTION__, mutabilityOption); + + if (errorString) *errorString = NULL; + if (0 == length) length = INT_MAX; + __CFConvertReadStreamToBytes(stream, length, &buffer, &buflen); + if (!buffer || buflen < 6) { + if (buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, buffer); + if (errorString) *errorString = CFRetain(CFSTR("stream had too few bytes")); + return NULL; + } + data = CFDataCreateWithBytesNoCopy(kCFAllocatorSystemDefault, buffer, buflen, kCFAllocatorSystemDefault); + pl = _CFPropertyListCreateFromXMLData(allocator, data, mutabilityOption, errorString, true, format); + CFRelease(data); + return pl; +} // ======================================================================== @@ -2259,51 +2341,62 @@ static CFTypeRef parsePlistDict(_CFXMLPlistParseInfo *pInfo) { return dict; } -static unsigned char fromHexDigit(unsigned char ch) { +CF_INLINE unsigned char fromHexDigit(unsigned char ch) { if (isdigit(ch)) return ch - '0'; if ((ch >= 'a') && (ch <= 'f')) return ch - 'a' + 10; if ((ch >= 'A') && (ch <= 'F')) return ch - 'A' + 10; return 0xff; // Just choose a large number for the error code } +/* Gets up to bytesSize bytes from a plist data. Returns number of bytes actually read. Leaves cursor at first non-space, non-hex character. + -1 is returned for unexpected char, -2 for uneven number of hex digits +*/ +static int getDataBytes(_CFXMLPlistParseInfo *pInfo, unsigned char *bytes, int bytesSize) { + int numBytesRead = 0; + while ((pInfo->curr < pInfo->end) && (numBytesRead < bytesSize)) { + int first, second; + UniChar ch1 = *pInfo->curr; + if (ch1 == '>') return numBytesRead; // Meaning we're done + first = fromHexDigit(ch1); + if (first != 0xff) { // If the first char is a hex, then try to read a second hex + pInfo->curr++; + if (pInfo->curr >= pInfo->end) return -2; // Error: uneven number of hex digits + UniChar ch2 = *pInfo->curr; + second = fromHexDigit(ch2); + if (second == 0xff) return -2; // Error: uneven number of hex digits + bytes[numBytesRead++] = (first << 4) + second; + pInfo->curr++; + } else if (ch1 == ' ' || ch1 == '\n' || ch1 == '\t' || ch1 == '\r' || ch1 == 0x2028 || ch1 == 0x2029) { + pInfo->curr++; + } else { + return -1; // Error: unexpected character + } + } + return numBytesRead; // This does likely mean we didn't encounter a '>', but we'll let the caller deal with that +} + static CFTypeRef parsePlistData(_CFXMLPlistParseInfo *pInfo) { - CFStringRef token; - unsigned length = 0; CFMutableDataRef result = CFDataCreateMutable(pInfo->allocator, 0); - advanceToNonSpace(pInfo); - while ( (token = parseUnquotedPlistString(pInfo)) ) { - unsigned tlength = CFStringGetLength(token); - unsigned char *bytes; - unsigned idx; - if (tlength & 1) { // Token must have an even number of characters - CFRelease(token); - CFRelease(result); - if (_CFExecutableLinkedOnOrAfter(CFSystemVersionJaguar)) { - pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Malformed data byte group at line %d; uneven length"), lineNumber(pInfo)); - } - return NULL; - } - CFDataSetLength(result, length + tlength/2); - bytes = (unsigned char *) CFDataGetMutableBytePtr(result) + length; - length += tlength / 2; - for (idx = 0; idx < tlength; idx += 2) { - unsigned char hi = fromHexDigit(CFStringGetCharacterAtIndex(token, idx)), lo = fromHexDigit(CFStringGetCharacterAtIndex(token, idx+1)); - if (hi == 0xff || lo == 0xff) { - CFRelease(token); - CFRelease(result); - if (_CFExecutableLinkedOnOrAfter(CFSystemVersionJaguar)) { - pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Malformed data byte group at line %d; invalid hex"), lineNumber(pInfo)); + // Read hex bytes and append them to result + while (1) { + #define numBytes 400 + unsigned char bytes[numBytes]; + int numBytesRead = getDataBytes(pInfo, bytes, numBytes); + if (numBytesRead < 0) { + CFRelease(result); + if (_CFExecutableLinkedOnOrAfter(CFSystemVersionJaguar)) { + switch (numBytesRead) { + case -2: pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Malformed data byte group at line %d; uneven length"), lineNumber(pInfo)); break; + default: pInfo->errorString = CFStringCreateWithFormat(pInfo->allocator, NULL, CFSTR("Malformed data byte group at line %d; invalid hex"), lineNumber(pInfo)); break; } - return NULL; - } - *bytes = (hi << 4) + lo; - bytes++; - } - CFRelease(token); - token = NULL; - advanceToNonSpace(pInfo); + } + return NULL; + } + if (numBytesRead == 0) break; + CFDataAppendBytes(result, bytes, numBytesRead); } + if (pInfo->errorString) { CFRelease(pInfo->errorString); pInfo->errorString = NULL; diff --git a/Parsing.subproj/CFPropertyList.h b/Parsing.subproj/CFPropertyList.h index 22caedd..8e53f23 100644 --- a/Parsing.subproj/CFPropertyList.h +++ b/Parsing.subproj/CFPropertyList.h @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -23,7 +21,7 @@ * @APPLE_LICENSE_HEADER_END@ */ /* CFPropertyList.h - Copyright (c) 1998-2003, Apple, Inc. All rights reserved. + Copyright (c) 1998-2005, Apple, Inc. All rights reserved. */ #if !defined(__COREFOUNDATION_CFPROPERTYLIST__) @@ -32,6 +30,7 @@ #include #include #include +#include #if defined(__cplusplus) extern "C" { @@ -93,6 +92,31 @@ Boolean CFPropertyListIsValid(CFPropertyListRef plist, CFPropertyListFormat form * to be helpful. The plist structure which is to be allowed is given by * the format parameter. */ +CF_EXPORT +CFIndex CFPropertyListWriteToStream(CFPropertyListRef propertyList, CFWriteStreamRef stream, CFPropertyListFormat format, CFStringRef *errorString); + +/* Writes the bytes of a plist serialization out to the stream. The + * stream must be opened and configured -- the function simply writes + * a bunch of bytes to the stream. The output plist format can be chosen. + * Leaves the stream open, but note that reading a plist expects the + * reading stream to end wherever the writing ended, so that the + * end of the plist data can be identified. Returns the number of bytes + * written, or 0 on error. Error messages are not currently localized, but + * may be in the future, so they are not suitable for comparison. */ + +CF_EXPORT +CFPropertyListRef CFPropertyListCreateFromStream(CFAllocatorRef allocator, CFReadStreamRef stream, CFIndex streamLength, CFOptionFlags mutabilityOption, CFPropertyListFormat *format, CFStringRef *errorString); + +/* Same as current function CFPropertyListCreateFromXMLData() + * but takes a stream instead of data, and works on any plist file format. + * CFPropertyListCreateFromXMLData() also works on any plist file format. + * The stream must be open and configured -- the function simply reads a bunch + * of bytes from it starting at the current location in the stream, to the END + * of the stream, which is expected to be the end of the plist, or up to the + * number of bytes given by the length parameter if it is not 0. Error messages + * are not currently localized, but may be in the future, so they are not + * suitable for comparison. */ + #endif #if defined(__cplusplus) diff --git a/Parsing.subproj/CFXMLInputStream.c b/Parsing.subproj/CFXMLInputStream.c index 8a64a4e..1234ae7 100644 --- a/Parsing.subproj/CFXMLInputStream.c +++ b/Parsing.subproj/CFXMLInputStream.c @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -28,6 +26,7 @@ */ #include +#include #include #include "CFStringEncodingConverter.h" #include "CFUniChar.h" @@ -139,6 +138,12 @@ CF_INLINE void _fillStringWithCharacters(CFMutableStringRef string, UniChar *cha } __private_extern__ Boolean _openInputStream(_CFXMLInputStream *stream) { + if (NULL == stream->data && NULL != stream->url) { + CFDataRef data = NULL; + if (CFURLCreateDataAndPropertiesFromResource(stream->allocator, stream->url, &data, NULL, NULL, NULL)) { + stream->data = data; + } + } if (NULL == stream->data) { return false; } else { diff --git a/Parsing.subproj/CFXMLInputStream.h b/Parsing.subproj/CFXMLInputStream.h index 1ec0440..e38e21a 100644 --- a/Parsing.subproj/CFXMLInputStream.h +++ b/Parsing.subproj/CFXMLInputStream.h @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -23,7 +21,7 @@ * @APPLE_LICENSE_HEADER_END@ */ /* CFXMLInputStream.h - Copyright (c) 2000-2003, Apple, Inc. All rights reserved. + Copyright (c) 2000-2005, Apple, Inc. All rights reserved. */ #if !defined(__COREFOUNDATION_CFXMLINPUTSTREAM__) diff --git a/Parsing.subproj/CFXMLNode.c b/Parsing.subproj/CFXMLNode.c index 125acef..91e7b4c 100644 --- a/Parsing.subproj/CFXMLNode.c +++ b/Parsing.subproj/CFXMLNode.c @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 diff --git a/Parsing.subproj/CFXMLNode.h b/Parsing.subproj/CFXMLNode.h index b3b6466..7bb0b62 100644 --- a/Parsing.subproj/CFXMLNode.h +++ b/Parsing.subproj/CFXMLNode.h @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -23,7 +21,7 @@ * @APPLE_LICENSE_HEADER_END@ */ /* CFXMLNode.h - Copyright (c) 1998-2003, Apple, Inc. All rights reserved. + Copyright (c) 1998-2005, Apple, Inc. All rights reserved. */ #if !defined(__COREFOUNDATION_CFXMLNODE__) @@ -156,7 +154,7 @@ typedef struct { kCFXMLNodeTypeDocument CFXMLDocumentInfo * kCFXMLNodeTypeElement tag name CFXMLElementInfo * kCFXMLNodeTypeAttribute - kCFXMLNodeTypeProcessInstruction name of the target CFXMLProcessingInstructionInfo * + kCFXMLNodeTypeProcessingInstruction name of the target CFXMLProcessingInstructionInfo * kCFXMLNodeTypeComment text of the comment NULL kCFXMLNodeTypeText the text's contents NULL kCFXMLNodeTypeCDATASection text of the CDATA NULL diff --git a/Parsing.subproj/CFXMLParser.c b/Parsing.subproj/CFXMLParser.c index 009deab..9caaf5b 100644 --- a/Parsing.subproj/CFXMLParser.c +++ b/Parsing.subproj/CFXMLParser.c @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -28,8 +26,8 @@ */ #include -#include #include "CFXMLInputStream.h" +#include #include "CFUniChar.h" #include "CFInternal.h" @@ -260,6 +258,14 @@ CFXMLParserRef CFXMLParserCreate(CFAllocatorRef allocator, CFDataRef xmlData, CF return __CFXMLParserInit(allocator, dataSource, parseOptions, xmlData, versionOfNodes, callBacks, context); } +CFXMLParserRef CFXMLParserCreateWithDataFromURL(CFAllocatorRef allocator, CFURLRef dataSource, CFOptionFlags parseOptions, CFIndex versionOfNodes, CFXMLParserCallBacks *callBacks, CFXMLParserContext *context) { + CFAssert1(dataSource == NULL || CFGetTypeID(dataSource) == CFURLGetTypeID(), __kCFLogAssertion, "%s(): dataSource is not a valid CFURL", __PRETTY_FUNCTION__); + CFAssert1(callBacks != NULL && callBacks->createXMLStructure != NULL && callBacks->addChild != NULL && callBacks->endXMLStructure != NULL, __kCFLogAssertion, "%s(): callbacks createXMLStructure, addChild, and endXMLStructure must all be non-NULL", __PRETTY_FUNCTION__); + CFAssert2(versionOfNodes <= 1, __kCFLogAssertion, "%s(): version number %d is higher than supported by CFXMLParser", __PRETTY_FUNCTION__, versionOfNodes); + CFAssert1(versionOfNodes != 0, __kCFLogAssertion, "%s(): version number 0 is no longer supported by CFXMLParser", __PRETTY_FUNCTION__); + + return __CFXMLParserInit(allocator, dataSource, parseOptions, NULL, versionOfNodes, callBacks, context); +} Boolean CFXMLParserParse(CFXMLParserRef parser) { CFXMLDocumentInfo docData; @@ -1792,8 +1798,32 @@ static void _XMLTreeEndXMLStructure(CFXMLParserRef parser, void *xmlType, void * CFRelease((CFXMLTreeRef)xmlType); } +CFXMLTreeRef CFXMLTreeCreateWithDataFromURL(CFAllocatorRef allocator, CFURLRef dataSource, CFOptionFlags parseOptions, CFIndex version) { + CFXMLParserRef parser; + CFXMLParserCallBacks callbacks; + CFXMLTreeRef result; + + CFAssert1(dataSource == NULL || CFGetTypeID(dataSource) == CFURLGetTypeID(), __kCFLogAssertion, "%s(): dataSource is not a valid CFURL", __PRETTY_FUNCTION__); -CFXMLTreeRef CFXMLTreeCreateFromData(CFAllocatorRef allocator, CFDataRef xmlData, CFURLRef dataSource, UInt32 parseOptions, CFIndex parserVersion) { + callbacks.createXMLStructure = _XMLTreeCreateXMLStructure; + callbacks.addChild = _XMLTreeAddChild; + callbacks.endXMLStructure = _XMLTreeEndXMLStructure; + callbacks.resolveExternalEntity = NULL; + callbacks.handleError = NULL; + parser = CFXMLParserCreateWithDataFromURL(allocator, dataSource, parseOptions, version, &callbacks, NULL); + + if (CFXMLParserParse(parser)) { + result = (CFXMLTreeRef)CFXMLParserGetDocument(parser); + } else { + result = (CFXMLTreeRef)CFXMLParserGetDocument(parser); + if (result) CFRelease(result); + result = NULL; + } + CFRelease(parser); + return result; +} + +CFXMLTreeRef CFXMLTreeCreateFromData(CFAllocatorRef allocator, CFDataRef xmlData, CFURLRef dataSource, CFOptionFlags parseOptions, CFIndex parserVersion) { return CFXMLTreeCreateFromDataWithError(allocator, xmlData, dataSource, parseOptions, parserVersion, NULL); } @@ -1909,6 +1939,13 @@ CFStringRef CFXMLCreateStringByEscapingEntities(CFAllocatorRef allocator, CFStri mark = idx + 1; } } + // Copy the remainder to the output string before returning. + CFStringRef remainder = CFStringCreateWithSubstring(allocator, string, CFRangeMake(mark, idx - mark)); + if (NULL != remainder) { + CFStringAppend(newString, remainder); + CFRelease(remainder); + } + CFRelease(startChars); return newString; } diff --git a/Parsing.subproj/CFXMLParser.h b/Parsing.subproj/CFXMLParser.h index d9ecd84..d035979 100644 --- a/Parsing.subproj/CFXMLParser.h +++ b/Parsing.subproj/CFXMLParser.h @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -23,7 +21,7 @@ * @APPLE_LICENSE_HEADER_END@ */ /* CFXMLParser.h - Copyright (c) 1998-2003, Apple, Inc. All rights reserved. + Copyright (c) 1998-2005, Apple, Inc. All rights reserved. */ #if !defined(__COREFOUNDATION_CFXMLPARSER__) @@ -186,6 +184,10 @@ CFTypeID CFXMLParserGetTypeID(void); CF_EXPORT CFXMLParserRef CFXMLParserCreate(CFAllocatorRef allocator, CFDataRef xmlData, CFURLRef dataSource, CFOptionFlags parseOptions, CFIndex versionOfNodes, CFXMLParserCallBacks *callBacks, CFXMLParserContext *context); +/* Arguments as above, except that the data to be parsed is loaded directly + from dataSource. dataSource may not be NULL. */ +CF_EXPORT +CFXMLParserRef CFXMLParserCreateWithDataFromURL(CFAllocatorRef allocator, CFURLRef dataSource, CFOptionFlags parseOptions, CFIndex versionOfNodes, CFXMLParserCallBacks *callBacks, CFXMLParserContext *context); CF_EXPORT void CFXMLParserGetContext(CFXMLParserRef parser, CFXMLParserContext *context); @@ -246,6 +248,9 @@ CFXMLTreeRef CFXMLTreeCreateFromData(CFAllocatorRef allocator, CFDataRef xmlData CF_EXPORT CFXMLTreeRef CFXMLTreeCreateFromDataWithError(CFAllocatorRef allocator, CFDataRef xmlData, CFURLRef dataSource, CFOptionFlags parseOptions, CFIndex versionOfNodes, CFDictionaryRef *errorDict) AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; +/* Loads the data to be parsed directly from dataSource. Arguments as above. */ +CF_EXPORT +CFXMLTreeRef CFXMLTreeCreateWithDataFromURL(CFAllocatorRef allocator, CFURLRef dataSource, CFOptionFlags parseOptions, CFIndex versionOfNodes); /* Generate the XMLData (ready to be written to whatever permanent storage is to be used) from an CFXMLTree. Will NOT regenerate entity references (except those diff --git a/Parsing.subproj/CFXMLTree.c b/Parsing.subproj/CFXMLTree.c index d517431..267eafc 100644 --- a/Parsing.subproj/CFXMLTree.c +++ b/Parsing.subproj/CFXMLTree.c @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 diff --git a/PlugIn.subproj/CFBundle.c b/PlugIn.subproj/CFBundle.c index f6da950..b46201d 100644 --- a/PlugIn.subproj/CFBundle.c +++ b/PlugIn.subproj/CFBundle.c @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -37,6 +35,7 @@ #include "CFPriv.h" #include #include "CFBundle_BinaryTypes.h" +#include #if defined(BINARY_SUPPORT_DYLD) // Import the mach-o headers that define the macho magic numbers @@ -49,7 +48,6 @@ #include #include -#include #include #endif /* BINARY_SUPPORT_DYLD */ @@ -68,9 +66,16 @@ #endif #if defined(__WIN32__) +#if !defined(__MINGW32__) && !defined(__CYGWIN__) +// With the MS headers, turning off Standard-C gets you macros for stat vs_stat. +// Strictly speaking, this is supposed to control traditional vs ANSI C features. #undef __STDC__ +#endif #include #include +#if !defined(__MINGW32__) && !defined(__CYGWIN__) +#define __STDC__ +#endif #endif // Public CFBundle Info plist keys @@ -154,8 +159,8 @@ struct __CFBundle { void *_connectionCookie; /* DYLD goop */ - void *_imageCookie; - void *_moduleCookie; + const void *_imageCookie; + const void *_moduleCookie; /* CFM<->DYLD glue */ CFMutableDictionaryRef _glueDict; @@ -191,12 +196,12 @@ static CFStringRef _defaultLocalization = NULL; static CFBundleRef _CFBundleCreate(CFAllocatorRef allocator, CFURLRef bundleURL, Boolean alreadyLocked, Boolean doFinalProcessing); static CFStringRef _CFBundleCopyExecutableName(CFAllocatorRef alloc, CFBundleRef bundle, CFURLRef url, CFDictionaryRef infoDict); static CFURLRef _CFBundleCopyExecutableURLIgnoringCache(CFBundleRef bundle); -static void _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 void _CFBundleEnsureBundleExistsForImagePath(CFStringRef imagePath); +static void _CFBundleEnsureBundlesExistForImagePaths(CFArrayRef imagePaths); static CFDictionaryRef _CFBundleGrokInfoDictFromMainExecutable(void); static CFStringRef _CFBundleDYLDCopyLoadedImagePathForPointer(void *p); static void *_CFBundleDYLDGetSymbolByNameWithSearch(CFBundleRef bundle, CFStringRef symbolName, Boolean globalSearch); @@ -477,6 +482,27 @@ CFBundleRef _CFBundleCreateWithExecutableURLIfLooksLikeBundle(CFAllocatorRef all return bundle; } +CFURLRef _CFBundleCopyMainBundleExecutableURL(Boolean *looksLikeBundle) { + // This function is for internal use only; _mainBundle is deliberately accessed outside of the lock to get around a reentrancy issue + const char *processPath; + CFStringRef str = NULL; + CFURLRef executableURL = NULL; + processPath = _CFProcessPath(); + if (processPath) { + str = CFStringCreateWithCString(NULL, processPath, CFStringFileSystemEncoding()); + if (str) { + executableURL = CFURLCreateWithFileSystemPath(NULL, str, PLATFORM_PATH_STYLE, false); + CFRelease(str); + } + } + if (looksLikeBundle) { + CFBundleRef mainBundle = _mainBundle; + if (mainBundle && (3 == mainBundle->_version || 4 == mainBundle->_version)) mainBundle = NULL; + *looksLikeBundle = (mainBundle ? YES : NO); + } + return executableURL; +} + static CFBundleRef _CFBundleGetMainBundleAlreadyLocked(void) { if (!_initedMainBundle) { const char *processPath; @@ -549,6 +575,18 @@ static CFBundleRef _CFBundleGetMainBundleAlreadyLocked(void) { if (_mainBundle->_binaryType == __CFBundleCFMBinary || _mainBundle->_binaryType == __CFBundleUnreadableBinary) { // if type 0 bundle and CFM binary and no Info.plist, treat as unbundled, since this also gives too many false positives if (_mainBundle->_version == 0) _mainBundle->_version = 4; + if (_mainBundle->_version != 4) { + // if CFM binary and no Info.plist and not main executable for bundle, treat as unbundled, since this also gives too many false positives + // except for Macromedia Director MX, which is unbundled but wants to be treated as bundled + CFStringRef executableName = _CFBundleCopyExecutableName(NULL, _mainBundle, NULL, NULL); + Boolean treatAsBundled = false; + if (str) { + CFIndex strLength = CFStringGetLength(str); + if (strLength > 10) treatAsBundled = CFStringFindWithOptions(str, CFSTR(" MX"), CFRangeMake(strLength - 10, 10), 0, NULL); + } + if (!treatAsBundled && (!executableName || !CFStringHasSuffix(str, executableName))) _mainBundle->_version = 4; + if (executableName) CFRelease(executableName); + } 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; @@ -579,8 +617,12 @@ static CFBundleRef _CFBundleGetMainBundleAlreadyLocked(void) { #endif /* BINARY_SUPPORT_CFM */ // Perform delayed final processing steps. // This must be done after _isLoaded has been set, for security reasons (3624341). - _CFBundleCheckWorkarounds(_mainBundle); - _CFBundleInitPlugIn(_mainBundle); + _CFBundleCheckWorkarounds(_mainBundle); + if (_CFBundleNeedsInitPlugIn(_mainBundle)) { + __CFSpinUnlock(&CFBundleGlobalDataLock); + _CFBundleInitPlugIn(_mainBundle); + __CFSpinLock(&CFBundleGlobalDataLock); + } } } if (bundleURL) CFRelease(bundleURL); @@ -724,6 +766,7 @@ static void __CFBundleDeallocate(CFTypeRef cf) { _CFBundleRemoveFromTables(bundle); if (bundle->_url != NULL) { + _CFBundleFlushCachesForURL(bundle->_url); CFRelease(bundle->_url); } if (bundle->_infoDict != NULL) { @@ -836,6 +879,7 @@ static CFBundleRef _CFBundleCreate(CFAllocatorRef allocator, CFURLRef bundleURL, bundle->_sharesStringsFiles = false; /* ??? For testing purposes? Or for good? */ +#warning Ali or Doug: Decide how to finalize strings sharing if (!getenv("CFBundleDisableStringsSharing") && (strncmp(buff, "/System/Library/Frameworks", 26) == 0) && (strncmp(buff + strlen(buff) - 10, ".framework", 10) == 0)) bundle->_sharesStringsFiles = true; @@ -866,9 +910,13 @@ static CFBundleRef _CFBundleCreate(CFAllocatorRef allocator, CFURLRef bundleURL, if (doFinalProcessing) { _CFBundleCheckWorkarounds(bundle); - _CFBundleInitPlugIn(bundle); + if (_CFBundleNeedsInitPlugIn(bundle)) { + if (alreadyLocked) __CFSpinUnlock(&CFBundleGlobalDataLock); + _CFBundleInitPlugIn(bundle); + if (alreadyLocked) __CFSpinLock(&CFBundleGlobalDataLock); + } } - + return bundle; } @@ -908,7 +956,7 @@ void _CFBundleSetDefaultLocalization(CFStringRef localizationName) { _defaultLocalization = newLocalization; } -__private_extern__ CFArrayRef _CFBundleGetLanguageSearchList(CFBundleRef bundle) { +CFArrayRef _CFBundleGetLanguageSearchList(CFBundleRef bundle) { if (bundle->_searchLanguages == NULL) { CFMutableArrayRef langs = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); CFStringRef devLang = CFBundleGetDevelopmentRegion(bundle); @@ -986,6 +1034,10 @@ CFDictionaryRef CFBundleGetLocalInfoDictionary(CFBundleRef bundle) { if (errStr) { CFRelease(errStr); } + if (bundle->_localInfoDict && CFDictionaryGetTypeID() != CFGetTypeID(bundle->_localInfoDict)) { + CFRelease(bundle->_localInfoDict); + bundle->_localInfoDict = NULL; + } CFRelease(data); } CFRelease(url); @@ -1463,7 +1515,6 @@ static CFURLRef _CFBundleCopyExecutableURLInDirectoryWithAllocator(CFAllocatorRe CFDictionaryRef infoDict = NULL; CFStringRef executablePath = NULL; CFURLRef executableURL = NULL; - Boolean isDir = false; Boolean foundIt = false; Boolean lookupMainExe = ((executableName == NULL) ? true : false); @@ -1584,12 +1635,42 @@ CFURLRef CFBundleCopyAuxiliaryExecutableURL(CFBundleRef bundle, CFStringRef exec Boolean CFBundleIsExecutableLoaded(CFBundleRef bundle) {return bundle->_isLoaded;} +CFBundleExecutableType CFBundleGetExecutableType(CFBundleRef bundle) { + CFBundleExecutableType result = kCFBundleOtherExecutableType; + CFURLRef executableURL = CFBundleCopyExecutableURL(bundle); + + if (!executableURL) bundle->_binaryType = __CFBundleNoBinary; +#if defined(BINARY_SUPPORT_DYLD) + if (bundle->_binaryType == __CFBundleUnknownBinary) { + bundle->_binaryType = _CFBundleGrokBinaryType(executableURL); +#if defined(BINARY_SUPPORT_CFM) + if (bundle->_binaryType != __CFBundleCFMBinary && bundle->_binaryType != __CFBundleUnreadableBinary) { + bundle->_resourceData._executableLacksResourceFork = true; + } +#endif /* BINARY_SUPPORT_CFM */ + } +#endif /* BINARY_SUPPORT_DYLD */ + if (executableURL) CFRelease(executableURL); + + if (bundle->_binaryType == __CFBundleCFMBinary) { + result = kCFBundlePEFExecutableType; + } else if (bundle->_binaryType == __CFBundleDYLDExecutableBinary || bundle->_binaryType == __CFBundleDYLDBundleBinary || bundle->_binaryType == __CFBundleDYLDFrameworkBinary) { + result = kCFBundleMachOExecutableType; + } else if (bundle->_binaryType == __CFBundleDLLBinary) { + result = kCFBundleDLLExecutableType; + } else if (bundle->_binaryType == __CFBundleELFBinary) { + result = kCFBundleELFExecutableType; + } + return result; +} + #define UNKNOWN_FILETYPE 0x0 #define PEF_FILETYPE 0x1000 #define XLS_FILETYPE 0x10001 #define DOC_FILETYPE 0x10002 #define PPT_FILETYPE 0x10003 #define XLS_NAME "Workbook" +#define XLS_NAME2 "Book" #define DOC_NAME "WordDocument" #define PPT_NAME "PowerPoint Document" #define PEF_MAGIC 0x4a6f7921 @@ -1599,30 +1680,35 @@ Boolean CFBundleIsExecutableLoaded(CFBundleRef bundle) {return bundle->_isLoaded #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 + 0xcafebabe, 0xbebafeca, 0xfeedface, 0xcefaedfe, 0x4a6f7921, 0x21796f4a, 0x7f454c46, 0xffd8ffe0, + 0x4d4d002a, 0x49492a00, 0x47494638, 0x89504e47, 0x69636e73, 0x00000100, 0x7b5c7274, 0x25504446, + 0x2e7261fd, 0x2e524d46, 0x2e736e64, 0x2e736400, 0x464f524d, 0x52494646, 0x38425053, 0x000001b3, + 0x000001ba, 0x4d546864, 0x504b0304, 0x53495421, 0x53495432, 0x53495435, 0x53495444, 0x53747566, + 0x30373037, 0x3c212d2d, 0x25215053, 0xd0cf11e0, 0x62656769, 0x6b6f6c79, 0x3026b275, 0x0000000c, + 0xfe370023, 0x09020600, 0x09040600, 0x4f676753, 0x664c6143, 0x00010000, 0x74727565, 0x4f54544f, + 0x41433130, 0xc809fe02, 0x0809fe02, 0x2356524d, 0x67696d70, 0x3c435058, 0x28445746, 0x424f4d53 }; // 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 + "mach\0" "mach\0" "mach\0" "mach\0" "pef\0\0" "pef\0\0" "elf\0\0" "jpeg\0" + "tiff\0" "tiff\0" "gif\0\0" "png\0\0" "icns\0" "ico\0\0" "rtf\0\0" "pdf\0\0" + "ra\0\0\0""rm\0\0\0""au\0\0\0""au\0\0\0""iff\0\0" "riff\0" "psd\0\0" "mpeg\0" + "mpeg\0" "mid\0\0" "zip\0\0" "sit\0\0" "sit\0\0" "sit\0\0" "sit\0\0" "sit\0\0" + "cpio\0" "html\0" "ps\0\0\0""ole\0\0" "uu\0\0\0""dmg\0\0" "wmv\0\0" "jp2\0\0" + "doc\0\0" "xls\0\0" "xls\0\0" "ogg\0\0" "flac\0" "ttf\0\0" "ttf\0\0" "otf\0\0" + "dwg\0\0" "dgn\0\0" "dgn\0\0" "wrl\0\0" "xcf\0\0" "cpx\0\0" "dwf\0\0" "bom\0\0"; + +#define NUM_EXTENSIONS 56 #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;} +CF_INLINE uint32_t _CFBundleSwapInt64Conditional(uint64_t arg, Boolean swap) {return swap ? CFSwapInt64(arg) : arg;} -static CFDictionaryRef _CFBundleGrokInfoDictFromData(char *bytes, unsigned long length) { +static CFDictionaryRef _CFBundleGrokInfoDictFromData(const char *bytes, uint32_t length) { CFMutableDictionaryRef result = NULL; CFDataRef infoData = NULL; if (NULL != bytes && 0 < length) { @@ -1651,70 +1737,132 @@ static CFDictionaryRef _CFBundleGrokInfoDictFromMainExecutable() { return _CFBundleGrokInfoDictFromData(bytes, length); } -static CFDictionaryRef _CFBundleGrokInfoDictFromFile(int fd, unsigned long offset, Boolean swapped) { +static CFDictionaryRef _CFBundleGrokInfoDictFromFile(int fd, const void *bytes, CFIndex length, uint32_t offset, Boolean swapped, Boolean sixtyFour) { struct stat statBuf; - char *maploc; + off_t fileLength = 0; + char *maploc = NULL; + const char *loc; 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; + if (fd >= 0 && fstat(fd, &statBuf) == 0 && (maploc = mmap(0, statBuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) != (void *)-1) { + loc = maploc; + fileLength = statBuf.st_size; + } else { + loc = bytes; + fileLength = length; + } + if (fileLength > offset + sizeof(struct mach_header_64)) { + if (sixtyFour) { + uint32_t ncmds = _CFBundleSwapInt32Conditional(((struct mach_header_64 *)(loc + offset))->ncmds, swapped); + uint32_t sizeofcmds = _CFBundleSwapInt32Conditional(((struct mach_header_64 *)(loc + offset))->sizeofcmds, swapped); + const char *startofcmds = loc + offset + sizeof(struct mach_header_64); + const char *endofcmds = startofcmds + sizeofcmds; + struct segment_command_64 *sgp = (struct segment_command_64 *)startofcmds; + if (endofcmds > loc + fileLength) endofcmds = loc + fileLength; + for (i = 0; !foundit && i < ncmds && startofcmds <= (char *)sgp && (char *)sgp < endofcmds; i++) { + if (LC_SEGMENT == _CFBundleSwapInt32Conditional(sgp->cmd, swapped)) { + struct section_64 *sp = (struct section_64 *)((char *)sgp + sizeof(struct segment_command_64)); + uint32_t nsects = _CFBundleSwapInt32Conditional(sgp->nsects, swapped); + for (j = 0; !foundit && j < nsects && startofcmds <= (char *)sp && (char *)sp < endofcmds; j++) { + if (0 == strncmp(sp->sectname, PLIST_SECTION, sizeof(sp->sectname)) && 0 == strncmp(sp->segname, PLIST_SEGMENT, sizeof(sp->segname))) { + uint64_t sectlength64 = _CFBundleSwapInt64Conditional(sp->size, swapped); + uint32_t sectlength = (uint32_t)(sectlength64 & 0xffffffff); + uint32_t sectoffset = _CFBundleSwapInt32Conditional(sp->offset, swapped); + const char *sectbytes = loc + offset + sectoffset; + // we don't support huge-sized plists + if (sectlength64 <= 0xffffffff && loc <= sectbytes && sectbytes + sectlength <= loc + fileLength) result = _CFBundleGrokInfoDictFromData(sectbytes, sectlength); + foundit = true; + } + sp = (struct section_64 *)((char *)sp + sizeof(struct section_64)); } - sp = (struct section *)((char *)sp + sizeof(struct section)); } + sgp = (struct segment_command_64 *)((char *)sgp + _CFBundleSwapInt32Conditional(sgp->cmdsize, swapped)); + } + } else { + uint32_t ncmds = _CFBundleSwapInt32Conditional(((struct mach_header *)(loc + offset))->ncmds, swapped); + uint32_t sizeofcmds = _CFBundleSwapInt32Conditional(((struct mach_header *)(loc + offset))->sizeofcmds, swapped); + const char *startofcmds = loc + offset + sizeof(struct mach_header); + const char *endofcmds = startofcmds + sizeofcmds; + struct segment_command *sgp = (struct segment_command *)startofcmds; + if (endofcmds > loc + fileLength) endofcmds = loc + fileLength; + for (i = 0; !foundit && i < ncmds && startofcmds <= (char *)sgp && (char *)sgp < endofcmds; i++) { + if (LC_SEGMENT == _CFBundleSwapInt32Conditional(sgp->cmd, swapped)) { + struct section *sp = (struct section *)((char *)sgp + sizeof(struct segment_command)); + uint32_t nsects = _CFBundleSwapInt32Conditional(sgp->nsects, swapped); + for (j = 0; !foundit && j < nsects && startofcmds <= (char *)sp && (char *)sp < endofcmds; j++) { + if (0 == strncmp(sp->sectname, PLIST_SECTION, sizeof(sp->sectname)) && 0 == strncmp(sp->segname, PLIST_SEGMENT, sizeof(sp->segname))) { + uint32_t sectlength = _CFBundleSwapInt32Conditional(sp->size, swapped); + uint32_t sectoffset = _CFBundleSwapInt32Conditional(sp->offset, swapped); + const char *sectbytes = loc + offset + sectoffset; + if (loc <= sectbytes && sectbytes + sectlength <= loc + fileLength) result = _CFBundleGrokInfoDictFromData(sectbytes, sectlength); + foundit = true; + } + sp = (struct section *)((char *)sp + sizeof(struct section)); + } + } + sgp = (struct segment_command *)((char *)sgp + _CFBundleSwapInt32Conditional(sgp->cmdsize, swapped)); } - sgp = (struct segment_command *)((char *)sgp + _CFBundleSwapInt32Conditional(sgp->cmdsize, swapped)); } - munmap(maploc, statBuf.st_size); } + if (maploc) munmap(maploc, statBuf.st_size); return result; } -static Boolean _CFBundleGrokX11(int fd, unsigned long offset, Boolean swapped) { +static Boolean _CFBundleGrokX11(int fd, const void *bytes, CFIndex length, uint32_t offset, Boolean swapped, Boolean sixtyFour) { static const char libX11name[] = LIB_X11; struct stat statBuf; - char *maploc; + off_t fileLength = 0; + char *maploc = NULL; + const char *loc; 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; + if (fd >= 0 && fstat(fd, &statBuf) == 0 && (maploc = mmap(0, statBuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) != (void *)-1) { + loc = maploc; + fileLength = statBuf.st_size; + } else { + loc = bytes; + fileLength = length; + } + if (fileLength > offset + sizeof(struct mach_header_64)) { + if (sixtyFour) { + uint32_t ncmds = _CFBundleSwapInt32Conditional(((struct mach_header_64 *)(loc + offset))->ncmds, swapped); + uint32_t sizeofcmds = _CFBundleSwapInt32Conditional(((struct mach_header_64 *)(loc + offset))->sizeofcmds, swapped); + const char *startofcmds = loc + offset + sizeof(struct mach_header_64); + const char *endofcmds = startofcmds + sizeofcmds; + struct dylib_command *dlp = (struct dylib_command *)startofcmds; + if (endofcmds > loc + fileLength) endofcmds = loc + fileLength; + for (i = 0; !result && i < ncmds && startofcmds <= (char *)dlp && (char *)dlp < endofcmds; i++) { + if (LC_LOAD_DYLIB == _CFBundleSwapInt32Conditional(dlp->cmd, swapped)) { + uint32_t nameoffset = _CFBundleSwapInt32Conditional(dlp->dylib.name.offset, swapped); + if (0 == strncmp((char *)dlp + nameoffset, libX11name, sizeof(libX11name) - 1)) result = true; + } + dlp = (struct dylib_command *)((char *)dlp + _CFBundleSwapInt32Conditional(dlp->cmdsize, swapped)); + } + } else { + uint32_t ncmds = _CFBundleSwapInt32Conditional(((struct mach_header *)(loc + offset))->ncmds, swapped); + uint32_t sizeofcmds = _CFBundleSwapInt32Conditional(((struct mach_header *)(loc + offset))->sizeofcmds, swapped); + const char *startofcmds = loc + offset + sizeof(struct mach_header); + const char *endofcmds = startofcmds + sizeofcmds; + struct dylib_command *dlp = (struct dylib_command *)startofcmds; + if (endofcmds > loc + fileLength) endofcmds = loc + fileLength; + for (i = 0; !result && i < ncmds && startofcmds <= (char *)dlp && (char *)dlp < endofcmds; i++) { + if (LC_LOAD_DYLIB == _CFBundleSwapInt32Conditional(dlp->cmd, swapped)) { + uint32_t nameoffset = _CFBundleSwapInt32Conditional(dlp->dylib.name.offset, swapped); + if (0 == strncmp((char *)dlp + nameoffset, libX11name, sizeof(libX11name) - 1)) result = true; + } + dlp = (struct dylib_command *)((char *)dlp + _CFBundleSwapInt32Conditional(dlp->cmdsize, swapped)); } - dlp = (struct dylib_command *)((char *)dlp + _CFBundleSwapInt32Conditional(dlp->cmdsize, swapped)); } - munmap(maploc, statBuf.st_size); } + if (maploc) munmap(maploc, statBuf.st_size); return result; } - -static UInt32 _CFBundleGrokMachTypeForFatFile(int fd, void *bytes, CFIndex length, Boolean *isX11, CFDictionaryRef *infodict) { +static UInt32 _CFBundleGrokMachTypeForFatFile(int fd, const 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)]; + unsigned char buffer[sizeof(struct mach_header_64)]; + const unsigned char *moreBytes = NULL; const NXArchInfo *archInfo = NXGetLocalArchInfo(); struct fat_arch *fat = NULL; @@ -1726,23 +1874,36 @@ static UInt32 _CFBundleGrokMachTypeForFatFile(int fd, void *bytes, CFIndex lengt 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)) { + if (fd >= 0 && lseek(fd, fat->offset, SEEK_SET) == (off_t)fat->offset && read(fd, buffer, sizeof(buffer)) >= (int)sizeof(buffer)) { + moreBytes = buffer; + } else if (bytes && (uint32_t)length >= fat->offset + 512) { + moreBytes = bytes + fat->offset; + } + if (moreBytes) { 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); + if (infodict) *infodict = _CFBundleGrokInfoDictFromFile(fd, bytes, length, fat->offset, false, false); + if (isX11) *isX11 = _CFBundleGrokX11(fd, bytes, length, fat->offset, false, 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); + if (infodict) *infodict = _CFBundleGrokInfoDictFromFile(fd, bytes, length, fat->offset, true, false); + if (isX11) *isX11 = _CFBundleGrokX11(fd, bytes, length, fat->offset, true, false); + } else if (MH_MAGIC_64 == magic) { + machtype = ((struct mach_header_64 *)moreBytes)->filetype; + if (infodict) *infodict = _CFBundleGrokInfoDictFromFile(fd, bytes, length, fat->offset, false, true); + if (isX11) *isX11 = _CFBundleGrokX11(fd, bytes, length, fat->offset, false, true); + } else if (MH_CIGAM_64 == magic) { + machtype = CFSwapInt32(((struct mach_header_64 *)moreBytes)->filetype); + if (infodict) *infodict = _CFBundleGrokInfoDictFromFile(fd, bytes, length, fat->offset, true, true); + if (isX11) *isX11 = _CFBundleGrokX11(fd, bytes, length, fat->offset, true, true); } } } return machtype; } -static UInt32 _CFBundleGrokMachType(int fd, void *bytes, CFIndex length, Boolean *isX11, CFDictionaryRef *infodict) { +static UInt32 _CFBundleGrokMachType(int fd, const void *bytes, CFIndex length, Boolean *isX11, CFDictionaryRef *infodict) { unsigned int magic = *((UInt32 *)bytes), machtype = UNKNOWN_FILETYPE; CFIndex i; @@ -1750,13 +1911,22 @@ static UInt32 _CFBundleGrokMachType(int fd, void *bytes, CFIndex length, Boolean 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); + if (infodict) *infodict = _CFBundleGrokInfoDictFromFile(fd, bytes, length, 0, false, false); + if (isX11) *isX11 = _CFBundleGrokX11(fd, bytes, length, 0, false, 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); + if (infodict) *infodict = _CFBundleGrokInfoDictFromFile(fd, bytes, length, 0, true, false); + if (isX11) *isX11 = _CFBundleGrokX11(fd, bytes, length, 0, true, false); + } else if (MH_MAGIC_64 == magic) { + machtype = ((struct mach_header_64 *)bytes)->filetype; + if (infodict) *infodict = _CFBundleGrokInfoDictFromFile(fd, bytes, length, 0, false, true); + if (isX11) *isX11 = _CFBundleGrokX11(fd, bytes, length, 0, false, true); + } else if (MH_CIGAM_64 == magic) { + for (i = 0; i < length; i += 4) *(UInt32 *)(bytes + i) = CFSwapInt32(*(UInt32 *)(bytes + i)); + machtype = ((struct mach_header_64 *)bytes)->filetype; + if (infodict) *infodict = _CFBundleGrokInfoDictFromFile(fd, bytes, length, 0, true, true); + if (isX11) *isX11 = _CFBundleGrokX11(fd, bytes, length, 0, true, true); } else if (FAT_MAGIC == magic) { machtype = _CFBundleGrokMachTypeForFatFile(fd, bytes, length, isX11, infodict); } else if (FAT_CIGAM == magic) { @@ -1770,12 +1940,18 @@ static UInt32 _CFBundleGrokMachType(int fd, void *bytes, CFIndex length, Boolean #endif /* BINARY_SUPPORT_DYLD */ -static UInt32 _CFBundleGrokFileTypeForOLEFile(int fd, unsigned long offset) { +static UInt32 _CFBundleGrokFileTypeForOLEFile(int fd, const void *bytes, CFIndex length, off_t offset) { UInt32 filetype = UNKNOWN_FILETYPE; - static const unsigned char xlsname[] = XLS_NAME, docname[] = DOC_NAME, pptname[] = PPT_NAME; - unsigned char moreBytes[512]; + static const unsigned char xlsname[] = XLS_NAME, xlsname2[] = XLS_NAME2, docname[] = DOC_NAME, pptname[] = PPT_NAME; + const unsigned char *moreBytes = NULL; + unsigned char buffer[512]; - if (lseek(fd, offset, SEEK_SET) == (off_t)offset && read(fd, moreBytes, sizeof(moreBytes)) >= (int)sizeof(moreBytes)) { + if (fd >= 0 && lseek(fd, offset, SEEK_SET) == (off_t)offset && read(fd, buffer, sizeof(buffer)) >= (int)sizeof(buffer)) { + moreBytes = buffer; + } else if (bytes && length >= offset + 512) { + moreBytes = bytes + offset; + } + if (moreBytes) { CFIndex i, j; Boolean foundit = false; for (i = 0; !foundit && i < 4; i++) { @@ -1783,6 +1959,9 @@ static UInt32 _CFBundleGrokFileTypeForOLEFile(int fd, unsigned long offset) { 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(xlsname2) == namelength) { + for (j = 0, foundit = true; j + 1 < namelength; j++) if (moreBytes[128 * i + 2 * j] != xlsname2[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; @@ -1795,94 +1974,97 @@ static UInt32 _CFBundleGrokFileTypeForOLEFile(int fd, unsigned long offset) { return filetype; } -static Boolean _CFBundleGrokFileType(CFURLRef url, CFStringRef *extension, UInt32 *machtype, CFDictionaryRef *infodict) { +static Boolean _CFBundleGrokFileType(CFURLRef url, CFDataRef data, CFStringRef *extension, UInt32 *machtype, CFDictionaryRef *infodict) { struct stat statBuf; int fd = -1; char path[CFMaxPathSize]; - unsigned char bytes[MAGIC_BYTES_TO_READ]; + const unsigned char *bytes = NULL; + unsigned char buffer[MAGIC_BYTES_TO_READ]; CFIndex i, length = 0; + off_t fileLength = 0; const char *ext = NULL; UInt32 mt = UNKNOWN_FILETYPE; - 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) { +#if defined(BINARY_SUPPORT_DYLD) + Boolean isX11 = false; +#endif /* BINARY_SUPPORT_DYLD */ + Boolean isFile = false, isPlain = true, isZero = true, isHTML = false; + // extensions returned: o, tool, x11app, pef, core, dylib, bundle, elf, jpeg, jp2, tiff, gif, png, pict, icns, ico, rtf, pdf, ra, rm, au, aiff, aifc, wav, avi, wmv, ogg, flac, psd, mpeg, mid, zip, jar, sit, cpio, html, ps, mov, qtif, ttf, otf, sfont, bmp, hqx, bin, class, tar, txt, gz, Z, uu, bz, bz2, sh, pl, py, rb, dvi, sgi, tga, mp3, xml, plist, xls, doc, ppt, mp4, m4a, m4b, m4p, dmg, cwk, webarchive, dwg, dgn, pfa, pfb, afm, tfm, xcf, cpx, dwf, swf, swc, abw, bom + // ??? we do not distinguish between different wm types, returning wmv for any of wmv, wma, or asf + if (url && CFURLGetFileSystemRepresentation(url, true, path, CFMaxPathSize) && stat(path, &statBuf) == 0 && (statBuf.st_mode & S_IFMT) == S_IFREG && (fd = open(path, O_RDONLY, 0777)) >= 0) { + // cjk: It is not at clear that not caching would be a win here, since + // in most cases the sniffing of the executable is only done lazily, + // the executable is likely to be immediately used again; say, the + // bundle executable loaded. CFBundle does not need the data again, + // but for the system as a whole not caching could be a net win or lose. + // So, this is where the cache disablement would go, but I am not going + // to turn it on at this point. + // fcntl(fd, F_NOCACHE, 1); + length = read(fd, buffer, MAGIC_BYTES_TO_READ); + fileLength = statBuf.st_size; + bytes = buffer; + isFile = true; + } else if (data) { + length = CFDataGetLength(data); + fileLength = (off_t)length; + bytes = CFDataGetBytePtr(data); + if (length == 0) ext = "txt"; + } + if (bytes) { + if (length >= 4) { UInt32 magic = CFSwapInt32HostToBig(*((UInt32 *)bytes)); for (i = 0; !ext && i < NUM_EXTENSIONS; i++) { if (__CFBundleMagicNumbersArray[i] == magic) ext = __CFBundleExtensionsArray + i * EXTENSION_LENGTH; } if (ext) { - if (0xcafebabe == magic && 8 <= length && 0 != *((UInt16 *)(bytes + 4))) { - ext = "class"; + if (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); + else if ((int)sizeof(struct mach_header_64) <= length) mt = _CFBundleGrokMachType(fd, bytes, length, extension ? &isX11 : NULL, infodict); + + if (MH_OBJECT == mt) ext = "o"; + else if (MH_EXECUTE == mt) ext = isX11 ? "x11app" : "tool"; + else if (PEF_FILETYPE == mt) ext = "pef"; + else if (MH_CORE == mt) ext = "core"; + else if (MH_DYLIB == mt) ext = "dylib"; + else if (MH_BUNDLE == mt) ext = "bundle"; #endif /* BINARY_SUPPORT_DYLD */ - } -#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) { + else if (0x7b5c7274 == magic && (6 > length || 'f' != bytes[4])) ext = NULL; + else if (0x00010000 == magic && (6 > length || 0 != bytes[4])) ext = NULL; + else if (0x47494638 == magic && (6 > length || (0x3761 != CFSwapInt16HostToBig(*((UInt16 *)(bytes + 4))) && 0x3961 != CFSwapInt16HostToBig(*((UInt16 *)(bytes + 4)))))) ext = NULL; + else if (0x0000000c == magic && (6 > length || 0x6a50 != CFSwapInt16HostToBig(*((UInt16 *)(bytes + 4))))) ext = NULL; + else if (0x2356524d == magic && (6 > length || 0x4c20 != CFSwapInt16HostToBig(*((UInt16 *)(bytes + 4))))) ext = NULL; + else if (0x28445746 == magic && (6 > length || 0x2056 != CFSwapInt16HostToBig(*((UInt16 *)(bytes + 4))))) ext = NULL; + else if (0x30373037 == magic && (6 > length || 0x30 != bytes[4] || !isdigit(bytes[5]))) ext = NULL; + else if (0x41433130 == magic && (6 > length || 0x31 != bytes[4] || !isdigit(bytes[5]))) ext = NULL; + else if (0x89504e47 == magic && (8 > length || 0x0d0a1a0a != CFSwapInt32HostToBig(*((UInt32 *)(bytes + 4))))) ext = NULL; + else if (0x53747566 == magic && (8 > length || 0x66497420 != CFSwapInt32HostToBig(*((UInt32 *)(bytes + 4))))) ext = NULL; + else if (0x3026b275 == magic && (8 > length || 0x8e66cf11 != CFSwapInt32HostToBig(*((UInt32 *)(bytes + 4))))) ext = NULL; + else if (0x67696d70 == magic && (8 > length || 0x20786366 != CFSwapInt32HostToBig(*((UInt32 *)(bytes + 4))))) ext = NULL; + else if (0x424f4d53 == magic && (8 > length || 0x746f7265 != CFSwapInt32HostToBig(*((UInt32 *)(bytes + 4))))) ext = NULL; + else if (0x25215053 == magic && 14 <= length && 0 == strncmp(bytes + 4, "-AdobeFont", 10)) ext = "pfa"; + 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"; - } + 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"; - } + 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"; - } + UInt32 ft = _CFBundleGrokFileTypeForOLEFile(fd, bytes, length, 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 @@ -1899,119 +2081,128 @@ static Boolean _CFBundleGrokFileType(CFURLRef url, CFStringRef *extension, UInt3 } 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"; + if (5 <= length && 0 == bytes[3] && 0 == bytes[4] && ((1 == bytes[1] && 1 == (0xf7 & bytes[2])) || (0 == bytes[1] && (2 == (0xf7 & bytes[2]) || (3 == (0xf7 & bytes[2])))))) ext = "tga"; + else if (8 <= length && (0x6d6f6f76 == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 4))) || 0x6d646174 == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 4))) || 0x77696465 == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 4))))) ext = "mov"; + else if (8 <= length && (0x69647363 == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 4))) || 0x69646174 == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 4))))) ext = "qtif"; + else if (8 <= length && 0x424f424f == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 4)))) ext = "cwk"; + else if (8 <= length && 0x62706c69 == magic && 0x7374 == CFSwapInt16HostToBig(*((UInt16 *)(bytes + 4))) && isdigit(bytes[6]) && isdigit(bytes[7])) { + for (i = 8; !ext && i < 128 && i + 16 <= length; i++) { + if (0 == strncmp(bytes + i, "WebMainResource", 15)) ext = "webarchive"; + } + if (!ext) ext = "plist"; } else if (12 <= length && 0x66747970 == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 4)))) { // ??? list of ftyp values needs to be checked - if (0x6d703432 == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 8)))) { - ext = "mp4"; - } else if (0x4d344120 == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 8)))) { - ext = "m4a"; - } else if (0x4d344220 == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 8)))) { - ext = "m4b"; - } else if (0x4d345020 == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 8)))) { - ext = "m4p"; - } - } else if (0x424d == shortMagic && 18 <= length && 40 == CFSwapInt32HostToLittle(*((UInt32 *)(bytes + 14)))) { - ext = "bmp"; - } else if (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)) { + if (0x6d703432 == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 8)))) ext = "mp4"; + else if (0x4d344120 == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 8)))) ext = "m4a"; + else if (0x4d344220 == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 8)))) ext = "m4b"; + else if (0x4d345020 == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 8)))) ext = "m4p"; + } else if (0x424d == shortMagic && 18 <= length && 40 == CFSwapInt32HostToLittle(*((UInt32 *)(bytes + 14)))) ext = "bmp"; + else if (20 <= length && 0 == strncmp(bytes + 6, "%!PS-AdobeFont", 14)) ext = "pfb"; + else if (40 <= length && 0x42696e48 == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 34))) && 0x6578 == CFSwapInt16HostToBig(*((UInt16 *)(bytes + 38)))) ext = "hqx"; + else if (128 <= length && 0x6d42494e == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 102)))) ext = "bin"; + else if (128 <= length && 0 == bytes[0] && 0 < bytes[1] && bytes[1] < 64 && 0 == bytes[74] && 0 == bytes[82] && 0 == (fileLength % 128)) { unsigned df = CFSwapInt32HostToBig(*((UInt32 *)(bytes + 83))), rf = CFSwapInt32HostToBig(*((UInt32 *)(bytes + 87))), blocks = 1 + (df + 127) / 128 + (rf + 127) / 128; - if (df < 0x00800000 && rf < 0x00800000 && 1 < blocks && (off_t)(128 * blocks) == 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) { + if (df < 0x00800000 && rf < 0x00800000 && 1 < blocks && (off_t)(128 * blocks) == fileLength) ext = "bin"; + } else if (265 <= length && 0x75737461 == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 257))) && (0x72202000 == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 261))) || 0x7200 == CFSwapInt16HostToBig(*((UInt16 *)(bytes + 261))))) ext = "tar"; + else if (0xfeff == shortMagic || 0xfffe == shortMagic) ext = "txt"; + else if (0x1f9d == shortMagic) ext = "Z"; + else if (0x1f8b == shortMagic) ext = "gz"; + else if (0x71c7 == shortMagic || 0xc771 == shortMagic) ext = "cpio"; + else if (0xf702 == shortMagic) ext = "dvi"; + else if (0x01da == shortMagic && (0 == bytes[2] || 1 == bytes[2]) && (0 < bytes[3] && 16 > bytes[3])) ext = "sgi"; + else if (0x2321 == shortMagic) { CFIndex endOfLine = 0, lastSlash = 0; for (i = 2; 0 == endOfLine && i < length; i++) if ('\n' == bytes[i]) endOfLine = i; if (endOfLine > 3) { for (i = endOfLine - 1; 0 == lastSlash && i > 1; i--) if ('/' == bytes[i]) lastSlash = i; if (lastSlash > 0) { - if (0 == 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"; - } + 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) { + } else if (0xffd8 == shortMagic && 0xff == bytes[2]) ext = "jpeg"; + else if (0x4657 == shortMagic && 0x53 == bytes[2]) ext = "swf"; + else if (0x4357 == shortMagic && 0x53 == bytes[2]) ext = "swc"; + else if (0x4944 == shortMagic && '3' == bytes[2] && 0x20 > bytes[3]) ext = "mp3"; + else if (0x425a == shortMagic && isdigit(bytes[2]) && isdigit(bytes[3])) ext = "bz"; + else if (0x425a == shortMagic && 'h' == bytes[2] && isdigit(bytes[3]) && 8 <= length && (0x31415926 == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 4))) || 0x17724538 == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 4))))) ext = "bz2"; + else if (0x0011 == CFSwapInt16HostToBig(*((UInt16 *)(bytes + 2))) || 0x0012 == CFSwapInt16HostToBig(*((UInt16 *)(bytes + 2)))) ext = "tfm"; + else if ('<' == bytes[0] && 14 <= length) { if (0 == 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"; + for (i = 4; !ext && i < 128 && i + 20 <= length; i++) { + if ('<' == bytes[i]) { + if (0 == strncasecmp(bytes + i + 1, "abiword", 7)) ext = "abw"; + else if (0 == strncasecmp(bytes + i + 1, "!doctype svg", 12)) ext = "svg"; + else if (0 == strncasecmp(bytes + i + 1, "!doctype x3d", 12)) ext = "x3d"; + else if (0 == strncasecmp(bytes + i + 1, "!doctype html", 13)) ext = "html"; + else if (0 == strncasecmp(bytes + i + 1, "!doctype plist", 14)) ext = "plist"; + else if (0 == strncasecmp(bytes + i + 1, "!doctype posingfont", 19)) ext = "sfont"; + } } + if (!ext) ext = "xml"; } } } } if (extension && !ext) { //??? what about MacOSRoman? - for (i = 0; (isPlain || isZero) && i < length && i < 512; i++) { + for (i = 0; (isPlain || isZero) && !isHTML && i < length && i < 512; i++) { char c = bytes[i]; if (0x7f <= c || (0x20 > c && !isspace(c))) isPlain = false; if (0 != c) isZero = false; + if (isPlain && '<' == c && i + 14 <= length && 0 == strncasecmp(bytes + i + 1, "!doctype html", 13)) isHTML = true; } - 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 (isHTML) { + ext = "html"; + } else if (isPlain) { + if (16 <= length && 0 == strncmp(bytes, "StartFontMetrics", 16)) ext = "afm"; + else ext = "txt"; + } else if (isZero && length >= MAGIC_BYTES_TO_READ && fileLength >= 526) { + if (isFile) { + if (lseek(fd, 512, SEEK_SET) == 512 && read(fd, buffer, MAGIC_BYTES_TO_READ) >= 14) { + if (0x001102ff == CFSwapInt32HostToBig(*((UInt32 *)(buffer + 10)))) ext = "pict"; } + } else { + if (526 <= length && 0x001102ff == CFSwapInt32HostToBig(*((UInt32 *)(bytes + 522)))) ext = "pict"; } } } - if (extension && !ext && !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 && !ext && !isZero && length >= MAGIC_BYTES_TO_READ && fileLength >= 1024) { + if (isFile) { + off_t offset = fileLength - 512; + if (lseek(fd, offset, SEEK_SET) == offset && read(fd, buffer, 512) >= 512) { + if (0x6b6f6c79 == CFSwapInt32HostToBig(*((UInt32 *)buffer)) || (0x63647361 == CFSwapInt32HostToBig(*((UInt32 *)(buffer + 504))) && 0x656e6372 == CFSwapInt32HostToBig(*((UInt32 *)(buffer + 508))))) ext = "dmg"; } + } else { + if (512 <= length && (0x6b6f6c79 == CFSwapInt32HostToBig(*((UInt32 *)(bytes + length - 512))) || (0x63647361 == CFSwapInt32HostToBig(*((UInt32 *)(bytes + length - 8))) && 0x656e6372 == CFSwapInt32HostToBig(*((UInt32 *)(bytes + length - 4)))))) ext = "dmg"; } } } if (extension) *extension = ext ? CFStringCreateWithCStringNoCopy(NULL, ext, kCFStringEncodingASCII, kCFAllocatorNull) : NULL; if (machtype) *machtype = mt; - close(fd); + if (fd >= 0) close(fd); return (ext != NULL); } CFStringRef _CFBundleCopyFileTypeForFileURL(CFURLRef url) { CFStringRef extension = NULL; - (void)_CFBundleGrokFileType(url, &extension, NULL, NULL); + (void)_CFBundleGrokFileType(url, NULL, &extension, NULL, NULL); + return extension; +} + +CFStringRef _CFBundleCopyFileTypeForFileData(CFDataRef data) { + CFStringRef extension = NULL; + (void)_CFBundleGrokFileType(NULL, data, &extension, NULL, NULL); return extension; } __private_extern__ CFDictionaryRef _CFBundleCopyInfoDictionaryInExecutable(CFURLRef url) { CFDictionaryRef result = NULL; - (void)_CFBundleGrokFileType(url, NULL, NULL, &result); + (void)_CFBundleGrokFileType(url, NULL, NULL, NULL, &result); return result; } @@ -2021,7 +2212,7 @@ __private_extern__ __CFPBinaryType _CFBundleGrokBinaryType(CFURLRef executableUR // 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)) { + if (_CFBundleGrokFileType(executableURL, NULL, NULL, &machtype, NULL)) { switch (machtype) { case MH_EXECUTE: result = __CFBundleDYLDExecutableBinary; @@ -2425,23 +2616,7 @@ __private_extern__ CFURLRef _CFBundleCopyFrameworkURLForExecutablePath(CFAllocat #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); - + if (_CFAppendPathComponent(pathBuff, &length, CFMaxPathSize, executablesToFrameworksPathBuff, 16) && _CFAppendPathComponent(pathBuff, &length, CFMaxPathSize, nameBuff, nameLength) && _CFAppendPathExtension(pathBuff, &length, CFMaxPathSize, frameworksExtension, 9)) { CFStringSetExternalCharactersNoCopy(cheapStr, pathBuff, length, CFMaxPathSize); bundleURL = CFURLCreateWithFileSystemPath(alloc, cheapStr, PLATFORM_PATH_STYLE, true); if (!_CFBundleCouldBeBundle(bundleURL)) { @@ -2449,6 +2624,18 @@ __private_extern__ CFURLRef _CFBundleCopyFrameworkURLForExecutablePath(CFAllocat bundleURL = NULL; } } + // * (Windows-only) Next check the "Executables" directory parallel to the "PrivateFrameworks" directory case. + if (bundleURL == NULL) { + length = savedLength; + if (_CFAppendPathComponent(pathBuff, &length, CFMaxPathSize, executablesToPrivateFrameworksPathBuff, 23) && _CFAppendPathComponent(pathBuff, &length, CFMaxPathSize, nameBuff, nameLength) && _CFAppendPathExtension(pathBuff, &length, CFMaxPathSize, frameworksExtension, 9)) { + CFStringSetExternalCharactersNoCopy(cheapStr, pathBuff, length, CFMaxPathSize); + bundleURL = CFURLCreateWithFileSystemPath(alloc, cheapStr, PLATFORM_PATH_STYLE, true); + if (!_CFBundleCouldBeBundle(bundleURL)) { + CFRelease(bundleURL); + bundleURL = NULL; + } + } + } #endif // * Finally check the executable inside the framework case. if (bundleURL == NULL) { @@ -2492,6 +2679,7 @@ __private_extern__ CFURLRef _CFBundleCopyFrameworkURLForExecutablePath(CFAllocat return bundleURL; } +#if defined(BINARY_SUPPORT_DYLD) 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. @@ -2529,13 +2717,13 @@ static void _CFBundleEnsureBundlesExistForImagePaths(CFArrayRef imagePaths) { _CFBundleEnsureBundleExistsForImagePath(CFArrayGetValueAtIndex(imagePaths, i)); } } +#endif /* BINARY_SUPPORT_DYLD */ static void _CFBundleEnsureBundlesUpToDateWithHintAlreadyLocked(CFStringRef hint) { - CFArrayRef imagePaths; // Tickle the main bundle into existence (void)_CFBundleGetMainBundleAlreadyLocked(); #if defined(BINARY_SUPPORT_DYLD) - imagePaths = _CFBundleDYLDCopyLoadedImagePathsForHint(hint); + CFArrayRef imagePaths = _CFBundleDYLDCopyLoadedImagePathsForHint(hint); if (imagePaths != NULL) { _CFBundleEnsureBundlesExistForImagePaths(imagePaths); CFRelease(imagePaths); @@ -2545,8 +2733,6 @@ static void _CFBundleEnsureBundlesUpToDateWithHintAlreadyLocked(CFStringRef hint static void _CFBundleEnsureAllBundlesUpToDateAlreadyLocked(void) { // This method returns all the statically linked bundles. This includes the main bundle as well as any frameworks that the process was linked against at launch time. It does not include frameworks or opther bundles that were loaded dynamically. - - CFArrayRef imagePaths; // Tickle the main bundle into existence (void)_CFBundleGetMainBundleAlreadyLocked(); @@ -2560,7 +2746,7 @@ static void _CFBundleEnsureAllBundlesUpToDateAlreadyLocked(void) { #endif #if defined(BINARY_SUPPORT_DYLD) - imagePaths = _CFBundleDYLDCopyLoadedImagePathsIfChanged(); + CFArrayRef imagePaths = _CFBundleDYLDCopyLoadedImagePathsIfChanged(); if (imagePaths != NULL) { _CFBundleEnsureBundlesExistForImagePaths(imagePaths); CFRelease(imagePaths); @@ -2664,10 +2850,10 @@ CF_EXPORT CFURLRef CFBundleCopyBuiltInPlugInsURL(CFBundleRef bundle) { #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; +static const void *__CFBundleDYLDFindImage(char *buff) { + const void *header = NULL; + uint32_t i, numImages = _dyld_image_count(), numMatches = 0; + const char *curName, *p, *q; for (i = 0; !header && i < numImages; i++) { curName = _dyld_get_image_name(i); @@ -2703,7 +2889,7 @@ __private_extern__ Boolean _CFBundleDYLDCheckLoaded(CFBundleRef bundle) { char buff[CFMaxPathSize]; if (CFURLGetFileSystemRepresentation(executableURL, true, buff, CFMaxPathSize)) { - void *header = __CFBundleDYLDFindImage(buff); + const void *header = __CFBundleDYLDFindImage(buff); if (header) { if (bundle->_binaryType == __CFBundleUnknownBinary) { bundle->_binaryType = __CFBundleDYLDFrameworkBinary; @@ -2791,13 +2977,14 @@ __private_extern__ Boolean _CFBundleDYLDLoadFramework(CFBundleRef bundle) { __private_extern__ void _CFBundleDYLDUnloadBundle(CFBundleRef bundle) { if (bundle->_isLoaded) { - if (bundle->_moduleCookie && !NSUnLinkModule(bundle->_moduleCookie, NSUNLINKMODULE_OPTION_NONE)) { + if (bundle->_moduleCookie && !NSUnLinkModule((NSModule)(bundle->_moduleCookie), NSUNLINKMODULE_OPTION_NONE)) { CFLog(__kCFLogBundle, CFSTR("Internal error unloading bundle %@"), bundle); } else { - if (bundle->_moduleCookie && bundle->_imageCookie && !NSDestroyObjectFileImage(bundle->_imageCookie)) { + if (bundle->_moduleCookie && bundle->_imageCookie && !NSDestroyObjectFileImage((NSObjectFileImage)(bundle->_imageCookie))) { /* MF:!!! Error destroying object file image */ } - bundle->_connectionCookie = bundle->_imageCookie = bundle->_moduleCookie = NULL; + bundle->_connectionCookie = NULL; + bundle->_imageCookie = bundle->_moduleCookie = NULL; bundle->_isLoaded = false; } } @@ -2816,7 +3003,7 @@ static void *_CFBundleDYLDGetSymbolByNameWithSearch(CFBundleRef bundle, CFString /* MF:??? ASCII appropriate here? */ if (CFStringGetCString(symbolName, &(buff[1]), 1024, kCFStringEncodingASCII)) { if (bundle->_moduleCookie) { - symbol = NSLookupSymbolInModule(bundle->_moduleCookie, buff); + symbol = NSLookupSymbolInModule((NSModule)(bundle->_moduleCookie), buff); } else if (bundle->_imageCookie) { symbol = NSLookupSymbolInImage(bundle->_imageCookie, buff, NSLOOKUPSYMBOLINIMAGE_OPTION_BIND|NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR); } @@ -2844,13 +3031,14 @@ static void *_CFBundleDYLDGetSymbolByNameWithSearch(CFBundleRef bundle, CFString } static CFStringRef _CFBundleDYLDCopyLoadedImagePathForPointer(void *p) { - unsigned long i, j, n = _dyld_image_count(); + uint32_t i, j, n = _dyld_image_count(); Boolean foundit = false; - char *name; + const 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); + // will need modification for 64-bit + const struct mach_header *mh = _dyld_get_image_header(i); + uint32_t addr = (uint32_t)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)) { @@ -2868,12 +3056,12 @@ static CFStringRef _CFBundleDYLDCopyLoadedImagePathForPointer(void *p) { } __private_extern__ CFArrayRef _CFBundleDYLDCopyLoadedImagePathsForHint(CFStringRef hint) { - unsigned long i, numImages = _dyld_image_count(); + uint32_t i, numImages = _dyld_image_count(); CFMutableArrayRef result = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); CFRange range = CFRangeMake(0, CFStringGetLength(hint)); for (i=0; i #else -#include #endif extern Boolean _CFBundleCFMLoad(CFBundleRef bundle); extern void _CFBundleCFMConnect(CFBundleRef bundle); @@ -139,6 +136,7 @@ extern void *_CFBundleDLLGetSymbolByName(CFBundleRef bundle, CFStringRef symbolN /* Private PlugIn-related CFBundle API */ +extern Boolean _CFBundleNeedsInitPlugIn(CFBundleRef bundle); extern void _CFBundleInitPlugIn(CFBundleRef bundle); extern void _CFBundleDeallocatePlugIn(CFBundleRef bundle); @@ -196,6 +194,7 @@ extern void _CFPlugInRemoveFactory(CFPlugInRef plugIn, _CFPFactory *factory); #define _CFBundleAlternateBuiltInPlugInsURLFromBase2 CFSTR("Contents/Plug-ins/") #define _CFBundleLprojExtension CFSTR("lproj") +#define _CFBundleLprojExtensionWithDot CFSTR(".lproj") #define _CFBundleMacOSXPlatformName CFSTR("macos") #define _CFBundleAlternateMacOSXPlatformName CFSTR("macosx") diff --git a/PlugIn.subproj/CFBundle_Resources.c b/PlugIn.subproj/CFBundle_Resources.c index cd7b195..c2bce54 100644 --- a/PlugIn.subproj/CFBundle_Resources.c +++ b/PlugIn.subproj/CFBundle_Resources.c @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -32,7 +30,7 @@ #else #define USE_GETDIRENTRIES 1 #endif -#define GETDIRENTRIES_CACHE_CAPACITY 100 +#define GETDIRENTRIES_CACHE_CAPACITY 128 #include "CFBundle_Internal.h" #include @@ -43,16 +41,6 @@ #include "CFInternal.h" #include "CFPriv.h" -#if defined(__MACOS8__) -/* MacOS8 Headers */ -#include -#include -#include -#include -#include -#include -#include -#else /* Unixy & Windows Headers */ #include #include @@ -61,8 +49,6 @@ #if USE_GETDIRENTRIES #include #endif -#endif - // All new-style bundles will have these extensions. @@ -183,12 +169,21 @@ CF_INLINE void _CFEnsureStaticBuffersInited(void) { static CFMutableDictionaryRef contentsCache = NULL; static CFMutableDictionaryRef directoryContentsCache = NULL; +static CFMutableDictionaryRef unknownContentsCache = NULL; + +typedef enum { + _CFBundleAllContents = 0, + _CFBundleDirectoryContents = 1, + _CFBundleUnknownContents = 2 +} _CFBundleDirectoryContentsType; -static CFArrayRef _CFBundleCopyDirectoryContentsAtPath(CFStringRef path, Boolean directoriesOnly) { +static CFArrayRef _CFBundleCopyDirectoryContentsAtPath(CFStringRef path, _CFBundleDirectoryContentsType contentsType) { CFArrayRef result = NULL; __CFSpinLock(&CFBundleResourceGlobalDataLock); - if (directoriesOnly) { + if (contentsType == _CFBundleUnknownContents) { + if (unknownContentsCache) result = (CFMutableArrayRef)CFDictionaryGetValue(unknownContentsCache, path); + } else if (contentsType == _CFBundleDirectoryContents) { if (directoryContentsCache) result = (CFMutableArrayRef)CFDictionaryGetValue(directoryContentsCache, path); } else { if (contentsCache) result = (CFMutableArrayRef)CFDictionaryGetValue(contentsCache, path); @@ -197,18 +192,51 @@ static CFArrayRef _CFBundleCopyDirectoryContentsAtPath(CFStringRef path, Boolean __CFSpinUnlock(&CFBundleResourceGlobalDataLock); if (!result) { + Boolean tryToOpen = true, allDots = true; char cpathBuff[CFMaxPathSize], dirge[8192]; - CFIndex cpathLen = 0; + CFIndex cpathLen = 0, idx, lastSlashIdx = 0; int fd = -1, numread; long basep = 0; - CFMutableArrayRef contents = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks), directoryContents = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); - CFStringRef name; + CFMutableArrayRef contents = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks), directoryContents = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks), unknownContents = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + CFStringRef dirName, name; + struct stat statBuf; if (_CFStringGetFileSystemRepresentation(path, cpathBuff, CFMaxPathSize)) { cpathLen = strlen(cpathBuff); - fd = open(cpathBuff, O_RDONLY, 0777); + + // First see whether we already know that the directory doesn't exist + for (idx = cpathLen; lastSlashIdx == 0 && idx-- > 0;) { + if (cpathBuff[idx] == '/') lastSlashIdx = idx; + else if (cpathBuff[idx] != '.') allDots = false; + } + if (lastSlashIdx > 0 && lastSlashIdx + 1 < cpathLen && !allDots) { + cpathBuff[lastSlashIdx] = '\0'; + dirName = CFStringCreateWithCString(NULL, cpathBuff, CFStringFileSystemEncoding()); + if (dirName) { + name = CFStringCreateWithCString(NULL, cpathBuff + lastSlashIdx + 1, CFStringFileSystemEncoding()); + if (name) { + // ??? we might like to use directoryContentsCache rather than contentsCache here, but we cannot unless we resolve DT_LNKs below + CFArrayRef dirDirContents = NULL; + + __CFSpinLock(&CFBundleResourceGlobalDataLock); + if (contentsCache) dirDirContents = (CFArrayRef)CFDictionaryGetValue(contentsCache, dirName); + if (dirDirContents) { + Boolean foundIt = false; + CFIndex dirDirIdx, dirDirLength = CFArrayGetCount(dirDirContents); + for (dirDirIdx = 0; !foundIt && dirDirIdx < dirDirLength; dirDirIdx++) if (kCFCompareEqualTo == CFStringCompare(name, CFArrayGetValueAtIndex(dirDirContents, dirDirIdx), kCFCompareCaseInsensitive)) foundIt = true; + if (!foundIt) tryToOpen = false; + } + __CFSpinUnlock(&CFBundleResourceGlobalDataLock); + + CFRelease(name); + } + CFRelease(dirName); + } + cpathBuff[lastSlashIdx] = '/'; + } + if (tryToOpen) fd = open(cpathBuff, O_RDONLY, 0777); } - if (fd >= 0) { + if (fd >= 0 && fstat(fd, &statBuf) == 0 && (statBuf.st_mode & S_IFMT) == S_IFDIR) { 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)) { @@ -216,39 +244,48 @@ static CFArrayRef _CFBundleCopyDirectoryContentsAtPath(CFStringRef path, Boolean 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) { + // ??? should we follow links for DT_LNK? unless we do, results are approximate, but for performance reasons we do not + // ??? likewise for DT_UNKNOWN + // ??? the utility of distinguishing directories from other contents is somewhat doubtful anyway CFArrayAppendValue(contents, name); if (dent->d_type == DT_DIR) { CFArrayAppendValue(directoryContents, name); } else if (dent->d_type == DT_UNKNOWN) { - 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'; + CFArrayAppendValue(unknownContents, name); } CFRelease(name); } } } - close(fd); } + if (fd >= 0) close(fd); __CFSpinLock(&CFBundleResourceGlobalDataLock); if (!contentsCache) contentsCache = CFDictionaryCreateMutable(NULL, GETDIRENTRIES_CACHE_CAPACITY, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - if (GETDIRENTRIES_CACHE_CAPACITY == CFDictionaryGetCount(contentsCache)) CFDictionaryRemoveAllValues(contentsCache); + 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); + if (GETDIRENTRIES_CACHE_CAPACITY <= CFDictionaryGetCount(directoryContentsCache)) CFDictionaryRemoveAllValues(directoryContentsCache); CFDictionaryAddValue(directoryContentsCache, path, directoryContents); - result = CFRetain(directoriesOnly ? directoryContents : contents); + if (!unknownContentsCache) unknownContentsCache = CFDictionaryCreateMutable(NULL, GETDIRENTRIES_CACHE_CAPACITY, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + if (GETDIRENTRIES_CACHE_CAPACITY <= CFDictionaryGetCount(unknownContentsCache)) CFDictionaryRemoveAllValues(unknownContentsCache); + CFDictionaryAddValue(unknownContentsCache, path, unknownContents); + + if (contentsType == _CFBundleUnknownContents) { + result = CFRetain(unknownContents); + } else if (contentsType == _CFBundleDirectoryContents) { + result = CFRetain(directoryContents); + } else { + result = CFRetain(contents); + } + CFRelease(contents); CFRelease(directoryContents); + CFRelease(unknownContents); __CFSpinUnlock(&CFBundleResourceGlobalDataLock); } - return result; } @@ -256,11 +293,43 @@ static void _CFBundleFlushContentsCaches(void) { __CFSpinLock(&CFBundleResourceGlobalDataLock); if (contentsCache) CFDictionaryRemoveAllValues(contentsCache); if (directoryContentsCache) CFDictionaryRemoveAllValues(directoryContentsCache); + if (unknownContentsCache) CFDictionaryRemoveAllValues(unknownContentsCache); + __CFSpinUnlock(&CFBundleResourceGlobalDataLock); +} + +static void _CFBundleFlushContentsCacheForPath(CFMutableDictionaryRef cache, CFStringRef path) { + CFStringRef keys[GETDIRENTRIES_CACHE_CAPACITY]; + unsigned i, count = CFDictionaryGetCount(cache); + if (count <= GETDIRENTRIES_CACHE_CAPACITY) { + CFDictionaryGetKeysAndValues(cache, (const void **)keys, NULL); + for (i = 0; i < count; i++) { + if (CFStringFindWithOptions(keys[i], path, CFRangeMake(0, CFStringGetLength(keys[i])), kCFCompareAnchored|kCFCompareCaseInsensitive, NULL)) { + CFDictionaryRemoveValue(cache, keys[i]); + } + } + } +} + +static void _CFBundleFlushContentsCachesForPath(CFStringRef path) { + __CFSpinLock(&CFBundleResourceGlobalDataLock); + if (contentsCache) _CFBundleFlushContentsCacheForPath(contentsCache, path); + if (directoryContentsCache) _CFBundleFlushContentsCacheForPath(directoryContentsCache, path); + if (unknownContentsCache) _CFBundleFlushContentsCacheForPath(unknownContentsCache, path); __CFSpinUnlock(&CFBundleResourceGlobalDataLock); } #endif /* USE_GETDIRENTRIES */ +CF_EXPORT void _CFBundleFlushCachesForURL(CFURLRef url) { +#if USE_GETDIRENTRIES + CFURLRef absoluteURL = CFURLCopyAbsoluteURL(url); + CFStringRef path = CFURLCopyFileSystemPath(absoluteURL, PLATFORM_PATH_STYLE); + _CFBundleFlushContentsCachesForPath(path); + CFRelease(path); + CFRelease(absoluteURL); +#endif /* USE_GETDIRENTRIES */ +} + CF_EXPORT void _CFBundleFlushCaches(void) { #if USE_GETDIRENTRIES _CFBundleFlushContentsCaches(); @@ -302,47 +371,48 @@ static void _CFSearchBundleDirectory(CFAllocatorRef alloc, CFMutableArrayRef res // 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; + Boolean appendSucceeded = true, platformGenericFound = false, platformSpecificFound = false, platformGenericIsDir = false, platformSpecificIsDir = false, platformGenericIsUnknown = false, platformSpecificIsUnknown = false; CFStringRef platformGenericStr = NULL; #if USE_GETDIRENTRIES CFIndex dirPathLen = pathLen; - CFArrayRef contents, directoryContents; - CFRange contentsRange, directoryContentsRange; - + CFArrayRef contents, directoryContents, unknownContents; + CFRange contentsRange, directoryContentsRange, unknownContentsRange; + CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, dirPathLen, dirPathLen); CFStringReplaceAll(cheapStr, tmpString); //fprintf(stderr, "looking in ");CFShow(cheapStr); - contents = _CFBundleCopyDirectoryContentsAtPath(cheapStr, false); + contents = _CFBundleCopyDirectoryContentsAtPath(cheapStr, _CFBundleAllContents); contentsRange = CFRangeMake(0, CFArrayGetCount(contents)); - directoryContents = _CFBundleCopyDirectoryContentsAtPath(cheapStr, true); + directoryContents = _CFBundleCopyDirectoryContentsAtPath(cheapStr, _CFBundleDirectoryContents); directoryContentsRange = CFRangeMake(0, CFArrayGetCount(directoryContents)); + unknownContents = _CFBundleCopyDirectoryContentsAtPath(cheapStr, _CFBundleUnknownContents); + unknownContentsRange = CFRangeMake(0, CFArrayGetCount(unknownContents)); #endif - if (nameLen > 0) { - _CFAppendPathComponent(pathUniChars, &pathLen, CFMaxPathSize, nameUniChars, nameLen); - } - // Save length with just name appended. + if (nameLen > 0) appendSucceeded = _CFAppendPathComponent(pathUniChars, &pathLen, CFMaxPathSize, nameUniChars, nameLen); savedPathLen = pathLen; - - // Check platform generic - if (typeLen > 0) { - _CFAppendPathExtension(pathUniChars, &pathLen, CFMaxPathSize, typeUniChars, typeLen); - } + if (appendSucceeded && typeLen > 0) appendSucceeded = _CFAppendPathExtension(pathUniChars, &pathLen, CFMaxPathSize, typeUniChars, typeLen); + if (appendSucceeded) { #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); + CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars + dirPathLen + 1, pathLen - dirPathLen - 1, pathLen - dirPathLen - 1); + CFStringReplaceAll(cheapStr, tmpString); + platformGenericFound = CFArrayContainsValue(contents, contentsRange, cheapStr); + platformGenericIsDir = CFArrayContainsValue(directoryContents, directoryContentsRange, cheapStr); + platformGenericIsUnknown = CFArrayContainsValue(unknownContents, unknownContentsRange, cheapStr); + //fprintf(stderr, "looking for ");CFShow(cheapStr);if (platformGenericFound) fprintf(stderr, "found it\n"); if (platformGenericIsDir) fprintf(stderr, "a directory\n"); + CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, pathLen, pathLen); + CFStringReplaceAll(cheapStr, tmpString); + if (platformGenericFound && platformGenericIsUnknown) { + (void)_CFIsResourceAtPath(cheapStr, &platformGenericIsDir); + //if (platformGenericIsDir) fprintf(stderr, "a directory after all\n"); else fprintf(stderr, "not a directory after all\n"); + } #else - CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, pathLen, pathLen); - CFStringReplaceAll(cheapStr, tmpString); - platformGenericFound = _CFIsResourceAtPath(cheapStr, &platformGenericIsDir); + CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, pathLen, pathLen); + CFStringReplaceAll(cheapStr, tmpString); + platformGenericFound = _CFIsResourceAtPath(cheapStr, &platformGenericIsDir); #endif + } // Check for platform specific. if (platformGenericFound) { @@ -352,22 +422,27 @@ static void _CFSearchBundleDirectory(CFAllocatorRef alloc, CFMutableArrayRef res pathUniChars[pathLen++] = (UniChar)'-'; memmove(pathUniChars + pathLen, _PlatformUniChars, _PlatformLen * sizeof(UniChar)); pathLen += _PlatformLen; - if (typeLen > 0) { - _CFAppendPathExtension(pathUniChars, &pathLen, CFMaxPathSize, typeUniChars, typeLen); - } + if (appendSucceeded && typeLen > 0) appendSucceeded = _CFAppendPathExtension(pathUniChars, &pathLen, CFMaxPathSize, typeUniChars, typeLen); + if (appendSucceeded) { #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); + CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars + dirPathLen + 1, pathLen - dirPathLen - 1, pathLen - dirPathLen - 1); + CFStringReplaceAll(cheapStr, tmpString); + platformSpecificFound = CFArrayContainsValue(contents, contentsRange, cheapStr); + platformSpecificIsDir = CFArrayContainsValue(directoryContents, directoryContentsRange, cheapStr); + platformSpecificIsUnknown = CFArrayContainsValue(unknownContents, unknownContentsRange, cheapStr); + //fprintf(stderr, "looking for ");CFShow(cheapStr);if (platformSpecificFound) fprintf(stderr, "found it\n"); if (platformSpecificIsDir) fprintf(stderr, "a directory\n"); + CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, pathLen, pathLen); + CFStringReplaceAll(cheapStr, tmpString); + if (platformSpecificFound && platformSpecificIsUnknown) { + (void)_CFIsResourceAtPath(cheapStr, &platformSpecificIsDir); + //if (platformSpecificIsDir) fprintf(stderr, "a directory after all\n"); else fprintf(stderr, "not a directory after all\n"); + } #else - CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, pathLen, pathLen); - CFStringReplaceAll(cheapStr, tmpString); - platformSpecificFound = _CFIsResourceAtPath(cheapStr, &platformSpecificIsDir); + CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, pathLen, pathLen); + CFStringReplaceAll(cheapStr, tmpString); + platformSpecificFound = _CFIsResourceAtPath(cheapStr, &platformSpecificIsDir); #endif + } } } if (platformSpecificFound) { @@ -385,6 +460,7 @@ static void _CFSearchBundleDirectory(CFAllocatorRef alloc, CFMutableArrayRef res #if USE_GETDIRENTRIES CFRelease(contents); CFRelease(directoryContents); + CFRelease(unknownContents); #endif } @@ -427,6 +503,7 @@ static void _CFFindBundleResourcesInRawDir(CFAllocatorRef alloc, UniChar *workin cpathLen = strlen(cpathBuff); if (!resTypes) { + // ??? should this use _CFBundleCopyDirectoryContentsAtPath? children = _CFContentsOfDirectory(alloc, cpathBuff, NULL, NULL, NULL); if (children) { CFIndex childIndex, childCount = CFArrayGetCount(children); @@ -440,6 +517,7 @@ static void _CFFindBundleResourcesInRawDir(CFAllocatorRef alloc, UniChar *workin for (i=0; i 0) { - _CFAppendPathComponent(workingUniChars, &workingLen, CFMaxPathSize, subDirUniChars, subDirLen); - } - _CFFindBundleResourcesInRawDir(alloc, workingUniChars, workingLen, nameUniChars, nameLen, resTypes, limit, version, cheapStr, tmpString, result); - + Boolean appendSucceeded = _CFAppendPathComponent(workingUniChars, &workingLen, CFMaxPathSize, _GlobalResourcesUniChars, _GlobalResourcesLen); + if (appendSucceeded && subDirLen > 0) appendSucceeded = _CFAppendPathComponent(workingUniChars, &workingLen, CFMaxPathSize, subDirUniChars, subDirLen); + if (appendSucceeded) _CFFindBundleResourcesInRawDir(alloc, workingUniChars, workingLen, nameUniChars, nameLen, resTypes, limit, version, cheapStr, tmpString, result); // Strip the non-localized resource directory. workingLen = savedWorkingLen; } if (CFArrayGetCount(result) < limit) { - if (subDirLen > 0) { - _CFAppendPathComponent(workingUniChars, &workingLen, CFMaxPathSize, subDirUniChars, subDirLen); - } - _CFFindBundleResourcesInRawDir(alloc, workingUniChars, workingLen, nameUniChars, nameLen, resTypes, limit, version, cheapStr, tmpString, result); + Boolean appendSucceeded = true; + if (subDirLen > 0) appendSucceeded = _CFAppendPathComponent(workingUniChars, &workingLen, CFMaxPathSize, subDirUniChars, subDirLen); + if (appendSucceeded) _CFFindBundleResourcesInRawDir(alloc, workingUniChars, workingLen, nameUniChars, nameLen, resTypes, limit, version, cheapStr, tmpString, result); } // Now search the local resources. @@ -492,6 +566,7 @@ static void _CFFindBundleResourcesInResourcesDir(CFAllocatorRef alloc, UniChar * for (langIndex = 0; langIndex < langCount; langIndex++) { curLangStr = CFArrayGetValueAtIndex(searchLanguages, langIndex); curLangLen = CFStringGetLength(curLangStr); + if (curLangLen > 255) curLangLen = 255; CFStringGetCharacters(curLangStr, CFRangeMake(0, curLangLen), curLangUniChars); savedWorkingLen = workingLen; if (!_CFAppendPathComponent(workingUniChars, &workingLen, CFMaxPathSize, curLangUniChars, curLangLen)) { @@ -598,46 +673,28 @@ CFArrayRef _CFFindBundleResources(CFBundleRef bundle, CFURLRef bundleURL, CFStri 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); - } + CFArrayRef languages = _CFBundleGetLanguageSearchList(bundle), types = NULL, array; + if (resourceType) types = CFArrayCreate(CFGetAllocator(bundle), (const void **)&resourceType, 1, &kCFTypeArrayCallBacks); array = _CFFindBundleResources(bundle, NULL, subDirName, languages, resourceName, types, 1, _CFBundleLayoutVersion(bundle)); - if (types) { - CFRelease(types); - } + if (types) CFRelease(types); if (array) { - if (CFArrayGetCount(array) > 0) { - result = CFRetain(CFArrayGetValueAtIndex(array, 0)); - } + 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; + CFArrayRef languages = _CFBundleGetLanguageSearchList(bundle), types = NULL, array; + if (resourceType) types = CFArrayCreate(CFGetAllocator(bundle), (const void **)&resourceType, 1, &kCFTypeArrayCallBacks); - 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); - } + if (types) CFRelease(types); return array; } @@ -646,31 +703,19 @@ CF_EXPORT CFURLRef _CFBundleCopyResourceURLForLanguage(CFBundleRef bundle, CFStr 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); + CFArrayRef languages = NULL, types = NULL, array; - if (resourceType) { - types = CFArrayCreateMutable(CFGetAllocator(bundle), 1, &kCFTypeArrayCallBacks); - CFArrayAppendValue(types, resourceType); - } + if (localizationName) languages = CFArrayCreate(CFGetAllocator(bundle), (const void **)&localizationName, 1, &kCFTypeArrayCallBacks); + if (resourceType) types = CFArrayCreate(CFGetAllocator(bundle), (const void **)&resourceType, 1, &kCFTypeArrayCallBacks); array = _CFFindBundleResources(bundle, NULL, subDirName, languages, resourceName, types, 1, _CFBundleLayoutVersion(bundle)); - - if (types) { - CFRelease(types); - } - if (array) { - if (CFArrayGetCount(array) > 0) { - result = CFRetain(CFArrayGetValueAtIndex(array, 0)); - } + if (CFArrayGetCount(array) > 0) result = CFRetain(CFArrayGetValueAtIndex(array, 0)); CFRelease(array); } - CFRelease(languages); + if (types) CFRelease(types); + if (languages) CFRelease(languages); return result; } @@ -678,25 +723,16 @@ CF_EXPORT CFURLRef CFBundleCopyResourceURLForLocalization(CFBundleRef bundle, CF 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; + CFArrayRef languages = NULL, types = NULL, array; - if (localizationName) CFArrayAppendValue(languages, localizationName); - - if (resourceType) { - types = CFArrayCreateMutable(CFGetAllocator(bundle), 1, &kCFTypeArrayCallBacks); - CFArrayAppendValue(types, resourceType); - } + if (localizationName) languages = CFArrayCreate(CFGetAllocator(bundle), (const void **)&localizationName, 1, &kCFTypeArrayCallBacks); + if (resourceType) types = CFArrayCreate(CFGetAllocator(bundle), (const void **)&resourceType, 1, &kCFTypeArrayCallBacks); // MF:!!! Better "limit" than 1,000,000? array = _CFFindBundleResources(bundle, NULL, subDirName, languages, NULL, types, 1000000, _CFBundleLayoutVersion(bundle)); - if (types) { - CFRelease(types); - } - - CFRelease(languages); + if (types) CFRelease(types); + if (languages) CFRelease(languages); return array; } @@ -729,6 +765,10 @@ CF_EXPORT CFStringRef CFBundleCopyLocalizedString(CFBundleRef bundle, CFStringRe CFRelease(errStr); errStr = NULL; } + if (stringTable && CFDictionaryGetTypeID() != CFGetTypeID(stringTable)) { + CFRelease(stringTable); + stringTable = NULL; + } CFRelease(tableData); } } @@ -747,6 +787,7 @@ CF_EXPORT CFStringRef CFBundleCopyLocalizedString(CFBundleRef bundle, CFStringRe result = CFDictionaryGetValue(stringTable, key); if (result == NULL) { + static int capitalize = -1; if (value == NULL) { result = CFRetain(key); } else if (CFEqual(value, CFSTR(""))) { @@ -754,6 +795,15 @@ CF_EXPORT CFStringRef CFBundleCopyLocalizedString(CFBundleRef bundle, CFStringRe } else { result = CFRetain(value); } + if (capitalize != 0) { + if (capitalize != 0) { + CFMutableStringRef capitalizedResult = CFStringCreateMutableCopy(CFGetAllocator(bundle), 0, result); + CFLog(__kCFLogBundle, CFSTR("Localizable string \"%@\" not found in strings table \"%@\" of bundle %@."), key, tableName, bundle); + CFStringUppercase(capitalizedResult, NULL); + CFRelease(result); + result = capitalizedResult; + } + } } else { CFRetain(result); } @@ -774,27 +824,16 @@ CF_EXPORT CFURLRef CFBundleCopyResourceURLInDirectory(CFURLRef bundleURL, CFStri } 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); - } + CFArrayRef languages = _CFBundleCopyLanguageSearchListInDirectory(NULL, newURL, &version), types = NULL, array; + if (resourceType) types = CFArrayCreate(NULL, (const void **)&resourceType, 1, &kCFTypeArrayCallBacks); array = _CFFindBundleResources(NULL, newURL, subDirName, languages, resourceName, types, 1, version); - if (types) { - CFRelease(types); - } - - CFRelease(languages); + if (types) CFRelease(types); + if (languages) CFRelease(languages); if (array) { - if (CFArrayGetCount(array) > 0) { - result = CFRetain(CFArrayGetValueAtIndex(array, 0)); - } + if (CFArrayGetCount(array) > 0) result = CFRetain(CFArrayGetValueAtIndex(array, 0)); CFRelease(array); } } @@ -815,22 +854,14 @@ CF_EXPORT CFArrayRef CFBundleCopyResourceURLsOfTypeInDirectory(CFURLRef bundleUR } 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); - } + CFArrayRef languages = _CFBundleCopyLanguageSearchListInDirectory(NULL, newURL, &version), types = NULL; + if (resourceType) types = CFArrayCreate(NULL, (const void **)&resourceType, 1, &kCFTypeArrayCallBacks); // MF:!!! Better "limit" than 1,000,000? array = _CFFindBundleResources(NULL, newURL, subDirName, languages, NULL, types, 1000000, version); - if (types) { - CFRelease(types); - } - - CFRelease(languages); + if (types) CFRelease(types); + if (languages) CFRelease(languages); } if (newURL) CFRelease(newURL); return array; @@ -839,9 +870,9 @@ CF_EXPORT CFArrayRef CFBundleCopyResourceURLsOfTypeInDirectory(CFURLRef bundleUR // 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" + "es_ES\0" "da_DK\0" "pt_PT\0" "fr_CA\0" "nb_NO\0" "he_IL\0" "ja_JP\0" "en_AU\0" "ar\0\0\0\0" "fi_FI\0" "fr_CH\0" "de_CH\0" "el_GR\0" "is_IS\0" "mt_MT\0" "\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" + "tr_TR\0" "hr_HR\0" "nl_NL\0" "nl_BE\0" "en_CA\0" "en_CA\0" "pt_PT\0" "nb_NO\0" "da_DK\0" "hi_IN\0" "ur_PK\0" "tr_TR\0" "it_CH\0" "en\0\0\0\0" "\0\0\0\0\0\0" "ro_RO\0" "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" @@ -850,10 +881,10 @@ const char * __CFBundleLocaleAbbreviationsArray = "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"; + "ur_IN\0" "vi_VN\0" "fr_BE\0" "uz_UZ\0" "en_SG\0" "nn_NO\0" "af_ZA\0" "eo\0\0\0\0" + "mr_IN\0" "bo\0\0\0\0" "ne_NP\0" "kl\0\0\0\0" "en_IE\0"; -#define NUM_LOCALE_ABBREVIATIONS 108 +#define NUM_LOCALE_ABBREVIATIONS 109 #define LOCALE_ABBREVIATION_LENGTH 6 static const char * const __CFBundleLanguageNamesArray[] = { @@ -875,16 +906,16 @@ static const char * const __CFBundleLanguageNamesArray[] = { "", "", "", "", "", "", "", "", "Welsh", "Basque", "Catalan", "Latin", "Quechua", "Guarani", "Aymara", "Tatar", "Uighur", "Dzongkha", "Javanese", "Sundanese", "Galician", "Afrikaans", "Breton", "Inuktitut", - "Scottish", "Manx", "Irish", "Tongan", "Greek", "Greenlandic", "Azerbaijani" + "Scottish", "Manx", "Irish", "Tongan", "Greek", "Greenlandic", "Azerbaijani", "Nynorsk" }; -#define NUM_LANGUAGE_NAMES 151 +#define NUM_LANGUAGE_NAMES 152 #define LANGUAGE_NAME_LENGTH 13 // string, with groups of 3 characters being 1 element in the array of abbreviations const char * __CFBundleLanguageAbbreviationsArray = "en\0" "fr\0" "de\0" "it\0" "nl\0" "sv\0" "es\0" "da\0" - "pt\0" "no\0" "he\0" "ja\0" "ar\0" "fi\0" "el\0" "is\0" + "pt\0" "nb\0" "he\0" "ja\0" "ar\0" "fi\0" "el\0" "is\0" "mt\0" "tr\0" "hr\0" "zh\0" "ur\0" "hi\0" "th\0" "ko\0" "lt\0" "pl\0" "hu\0" "et\0" "lv\0" "se\0" "fo\0" "fa\0" "ru\0" "zh\0" "nl\0" "ga\0" "sq\0" "ro\0" "cs\0" "sk\0" @@ -901,11 +932,22 @@ const char * __CFBundleLanguageAbbreviationsArray = "\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"; + "gd\0" "gv\0" "ga\0" "to\0" "el\0" "kl\0" "az\0" "nn\0"; -#define NUM_LANGUAGE_ABBREVIATIONS 151 +#define NUM_LANGUAGE_ABBREVIATIONS 152 #define LANGUAGE_ABBREVIATION_LENGTH 3 +#ifdef __CONSTANT_CFSTRINGS__ + +// These are not necessarily common localizations per se, but localizations for which the full language name is still in common use. +// These are used to provide a fast path for it (other localizations usually use the abbreviation, which is even faster). +static CFStringRef const __CFBundleCommonLanguageNamesArray[] = {CFSTR("English"), CFSTR("French"), CFSTR("German"), CFSTR("Italian"), CFSTR("Dutch"), CFSTR("Spanish"), CFSTR("Japanese")}; +static CFStringRef const __CFBundleCommonLanguageAbbreviationsArray[] = {CFSTR("en"), CFSTR("fr"), CFSTR("de"), CFSTR("it"), CFSTR("nl"), CFSTR("es"), CFSTR("ja")}; + +#define NUM_COMMON_LANGUAGE_NAMES 7 + +#endif // __CONSTANT_CFSTRINGS__ + static const SInt32 __CFBundleScriptCodesArray[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 1, 4, 0, 0, 0, 0, 0, 0, 2, 4, 9, 21, 3, 29, 29, 29, 29, 29, 0, 0, 4, @@ -916,7 +958,7 @@ static const SInt32 __CFBundleScriptCodesArray[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 4, 26, 0, 0, 0, 0, 0, 28, - 0, 0, 0, 0, 6, 0, 0 + 0, 0, 0, 0, 6, 0, 0, 0 }; static const CFStringEncoding __CFBundleStringEncodingsArray[] = { @@ -929,7 +971,7 @@ static const CFStringEncoding __CFBundleStringEncodingsArray[] = { 0, 0, 0, 0, 0, 0, 0, 0, 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 + 39, 39, 40, 0, 6, 0, 0, 0 }; static SInt32 _CFBundleGetLanguageCodeForLocalization(CFStringRef localizationName) { @@ -941,7 +983,9 @@ static SInt32 _CFBundleGetLanguageCodeForLocalization(CFStringRef localizationNa 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 + if ('n' == buff[0] && 'o' == buff[1]) result = 9; // hack for Norwegian + else if (0 == strcmp(buff, "zh_TW") || 0 == strcmp(buff, "zh-Hant")) result = 19; // hack for mixed-up Chinese language codes + else if (0 == strcmp(buff, "zh_CN") || 0 == strcmp(buff, "zh-Hans")) result = 33; 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; @@ -961,7 +1005,7 @@ static CFStringRef _CFBundleCopyLanguageAbbreviationForLanguageCode(SInt32 langu return result; } -static CFStringRef _CFBundleCopyLanguageNameForLanguageCode(SInt32 languageCode) { +static inline CFStringRef _CFBundleCopyLanguageNameForLanguageCode(SInt32 languageCode) { CFStringRef result = NULL; if (0 <= languageCode && languageCode < NUM_LANGUAGE_NAMES) { const char *languageName = __CFBundleLanguageNamesArray[languageCode]; @@ -972,7 +1016,7 @@ static CFStringRef _CFBundleCopyLanguageNameForLanguageCode(SInt32 languageCode) return result; } -static CFStringRef _CFBundleCopyLanguageAbbreviationForLocalization(CFStringRef localizationName) { +static inline CFStringRef _CFBundleCopyLanguageAbbreviationForLocalization(CFStringRef localizationName) { CFStringRef result = NULL; SInt32 languageCode = _CFBundleGetLanguageCodeForLocalization(localizationName); if (languageCode >= 0) { @@ -986,7 +1030,20 @@ static CFStringRef _CFBundleCopyLanguageAbbreviationForLocalization(CFStringRef return result; } -static CFStringRef _CFBundleCopyLanguageNameForLocalization(CFStringRef localizationName) { +static inline CFStringRef _CFBundleCopyModifiedLocalization(CFStringRef localizationName) { + CFMutableStringRef result = NULL; + CFIndex length = CFStringGetLength(localizationName); + if (length >= 4) { + UniChar c = CFStringGetCharacterAtIndex(localizationName, 2); + if ('-' == c || '_' == c) { + result = CFStringCreateMutableCopy(NULL, length, localizationName); + CFStringReplace(result, CFRangeMake(2, 1), ('-' == c) ? CFSTR("_") : CFSTR("-")); + } + } + return result; +} + +static inline CFStringRef _CFBundleCopyLanguageNameForLocalization(CFStringRef localizationName) { CFStringRef result = NULL; SInt32 languageCode = _CFBundleGetLanguageCodeForLocalization(localizationName); if (languageCode >= 0) { @@ -1129,9 +1186,11 @@ __CFSetNastyFile(CFSTR("")); } } 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); + Boolean useEnglishAsBackstop = true; + // could perhaps read out of LANG environment variable + if (useEnglishAsBackstop && !userLanguages) { + CFStringRef english = CFSTR("English"); + userLanguages = CFArrayCreate(kCFAllocatorDefault, (const void **)&english, 1, &kCFTypeArrayCallBacks); } if (userLanguages && CFGetTypeID(userLanguages) != CFArrayGetTypeID()) { CFRelease(userLanguages); @@ -1141,6 +1200,22 @@ __CFSetNastyFile(CFSTR("")); } __CFSpinUnlock(&CFBundleResourceGlobalDataLock); if (preferencesArray) CFRelease(preferencesArray); +#if defined(__MACOS8__) + if (useBackstops && (NULL == userLanguages || 0 == CFArrayGetCount(userLanguages)) { + // use the system region and language as a backstop on 8 + CFMutableArrayRef mutableUserLanguages = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + CFStringRef localeAbbreviation = _CFBundleCopyLocaleAbbreviationForRegionCode(GetScriptManagerVariable(smRegionCode)), languageAbbreviation = _CFBundleCopyLanguageAbbreviationForLanguageCode(GetScriptVariable(smSystemScript, smScriptLang)); + if (localeAbbreviation) { + CFArrayAppendValue(mutableUserLanguages, localeAbbreviation); + CFRelease(localeAbbreviation); + } + if (languageAbbreviation) { + CFArrayAppendValue(mutableUserLanguages, languageAbbreviation); + CFRelease(languageAbbreviation); + } + result = (CFArrayRef)mutableUserLanguages; + } +#endif /* __MACOS8__ */ if (!result && userLanguages) result = CFRetain(userLanguages); return result; } @@ -1162,8 +1237,13 @@ CF_EXPORT void _CFBundleGetLanguageAndRegionCodes(SInt32 *languageCode, SInt32 * language = _CFBundleGetLanguageCodeForLocalization(localizationName); region = _CFBundleGetRegionCodeForLocalization(localizationName); } else { +#if defined(__MACOS8__) + language = GetScriptVariable(smSystemScript, smScriptLang); + region = GetScriptManagerVariable(smRegionCode); +#else language = 0; region = 0; +#endif /* __MACOS8__ */ } if (language == -1 && region != -1) language = _CFBundleGetLanguageCodeForRegionCode(region); if (region == -1 && language != -1) region = _CFBundleGetRegionCodeForLanguageCode(language); @@ -1172,18 +1252,19 @@ CF_EXPORT void _CFBundleGetLanguageAndRegionCodes(SInt32 *languageCode, SInt32 * 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); + CFIndex curLangLen = CFStringGetLength(curLangStr), savedPathLen, idx; UniChar curLangUniChars[255]; - CFIndex savedPathLen; - CFStringRef languageAbbreviation = _CFBundleCopyLanguageAbbreviationForLocalization(curLangStr), languageName = _CFBundleCopyLanguageNameForLocalization(curLangStr); + CFStringRef altLangStr = NULL, modifiedLangStr = NULL, languageAbbreviation = NULL, languageName = NULL, canonicalLanguageIdentifier = NULL; + CFMutableDictionaryRef canonicalLanguageIdentifiers = NULL, predefinedCanonicalLanguageIdentifiers = NULL; Boolean foundOne = false; CFArrayRef predefinedLocalizations = NULL; CFRange predefinedLocalizationsRange; CFMutableStringRef cheapStr, tmpString; #if USE_GETDIRENTRIES - CFArrayRef directoryContents; - CFRange directoryContentsRange; + CFArrayRef contents; + CFRange contentsRange; #else Boolean isDir = false; #endif @@ -1192,13 +1273,13 @@ static Boolean _CFBundleTryOnePreferredLprojNameInDirectory(CFAllocatorRef alloc // different purposes, where each type is appropriate cheapStr = CFStringCreateMutable(alloc, 0); _CFStrSetDesiredCapacity(cheapStr, CFMaxPathSize); - tmpString = CFStringCreateMutableWithExternalCharactersNoCopy(kCFAllocatorSystemDefault, NULL, 0, 0, kCFAllocatorNull); - + 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)); + contents = _CFBundleCopyDirectoryContentsAtPath(cheapStr, _CFBundleAllContents); + contentsRange = CFRangeMake(0, CFArrayGetCount(contents)); #endif if (infoDict) { @@ -1210,71 +1291,156 @@ static Boolean _CFBundleTryOnePreferredLprojNameInDirectory(CFAllocatorRef alloc } predefinedLocalizationsRange = CFRangeMake(0, predefinedLocalizations ? CFArrayGetCount(predefinedLocalizations) : 0); + if (curLangLen > 255) curLangLen = 255; CFStringGetCharacters(curLangStr, CFRangeMake(0, curLangLen), curLangUniChars); savedPathLen = pathLen; - _CFAppendPathComponent(pathUniChars, &pathLen, CFMaxPathSize, curLangUniChars, curLangLen); - _CFAppendPathExtension(pathUniChars, &pathLen, CFMaxPathSize, _LprojUniChars, _LprojLen); + if (_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))) { + CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars + savedPathLen + 1, pathLen - savedPathLen - 1, pathLen - savedPathLen - 1); + CFStringReplaceAll(cheapStr, tmpString); + if ((predefinedLocalizations && CFArrayContainsValue(predefinedLocalizations, predefinedLocalizationsRange, curLangStr)) || (version != 4 && CFArrayContainsValue(contents, contentsRange, cheapStr))) { #else - CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, pathLen, pathLen); - CFStringReplaceAll(cheapStr, tmpString); - if ((predefinedLocalizations && CFArrayContainsValue(predefinedLocalizations, predefinedLocalizationsRange, curLangStr)) || (version != 4 && _CFIsResourceAtPath(cheapStr, &isDir) && isDir)) { + 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; + if (!CFArrayContainsValue(lprojNames, CFRangeMake(0, CFArrayGetCount(lprojNames)), curLangStr)) CFArrayAppendValue(lprojNames, curLangStr); + foundOne = true; + if (CFStringGetLength(curLangStr) <= 2) { + CFRelease(cheapStr); + CFRelease(tmpString); +#if USE_GETDIRENTRIES + CFRelease(contents); +#endif + return foundOne; + } + } + } +#ifdef __CONSTANT_CFSTRINGS__ + for (idx = 0; !altLangStr && idx < NUM_COMMON_LANGUAGE_NAMES; idx++) { + if (CFEqual(curLangStr, __CFBundleCommonLanguageAbbreviationsArray[idx])) altLangStr = __CFBundleCommonLanguageNamesArray[idx]; + else if (CFEqual(curLangStr, __CFBundleCommonLanguageNamesArray[idx])) altLangStr = __CFBundleCommonLanguageAbbreviationsArray[idx]; + } +#endif // __CONSTANT_CFSTRINGS__ + if (foundOne && altLangStr) { + CFRelease(cheapStr); + CFRelease(tmpString); +#if USE_GETDIRENTRIES + CFRelease(contents); +#endif + return foundOne; + } + if (altLangStr) { + curLangLen = CFStringGetLength(altLangStr); + if (curLangLen > 255) curLangLen = 255; + CFStringGetCharacters(altLangStr, CFRangeMake(0, curLangLen), curLangUniChars); + pathLen = savedPathLen; + if (_CFAppendPathComponent(pathUniChars, &pathLen, CFMaxPathSize, curLangUniChars, curLangLen) && _CFAppendPathExtension(pathUniChars, &pathLen, CFMaxPathSize, _LprojUniChars, _LprojLen)) { +#if USE_GETDIRENTRIES + CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars + savedPathLen + 1, pathLen - savedPathLen - 1, pathLen - savedPathLen - 1); + CFStringReplaceAll(cheapStr, tmpString); + if ((predefinedLocalizations && CFArrayContainsValue(predefinedLocalizations, predefinedLocalizationsRange, altLangStr)) || (version != 4 && CFArrayContainsValue(contents, contentsRange, cheapStr))) { +#else + CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, pathLen, pathLen); + CFStringReplaceAll(cheapStr, tmpString); + if ((predefinedLocalizations && CFArrayContainsValue(predefinedLocalizations, predefinedLocalizationsRange, altLangStr)) || (version != 4 && _CFIsResourceAtPath(cheapStr, &isDir) && isDir)) { +#endif + if (!CFArrayContainsValue(lprojNames, CFRangeMake(0, CFArrayGetCount(lprojNames)), altLangStr)) CFArrayAppendValue(lprojNames, altLangStr); + foundOne = true; + CFRelease(cheapStr); + CFRelease(tmpString); +#if USE_GETDIRENTRIES + CFRelease(contents); +#endif + return foundOne; + } + } + } +#if USE_GETDIRENTRIES + if (!foundOne) { + Boolean hasLocalizations = false; + for (idx = 0; !hasLocalizations && idx < contentsRange.length; idx++) { + CFStringRef name = CFArrayGetValueAtIndex(contents, idx); + if (CFStringHasSuffix(name, _CFBundleLprojExtensionWithDot)) hasLocalizations = true; + } + if (!hasLocalizations) { + CFRelease(cheapStr); + CFRelease(tmpString); + CFRelease(contents); + return foundOne; + } + } +#endif + if (!altLangStr && (modifiedLangStr = _CFBundleCopyModifiedLocalization(curLangStr))) { + curLangLen = CFStringGetLength(modifiedLangStr); + if (curLangLen > 255) curLangLen = 255; + CFStringGetCharacters(modifiedLangStr, CFRangeMake(0, curLangLen), curLangUniChars); + pathLen = savedPathLen; + if (_CFAppendPathComponent(pathUniChars, &pathLen, CFMaxPathSize, curLangUniChars, curLangLen) && _CFAppendPathExtension(pathUniChars, &pathLen, CFMaxPathSize, _LprojUniChars, _LprojLen)) { +#if USE_GETDIRENTRIES + CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars + savedPathLen + 1, pathLen - savedPathLen - 1, pathLen - savedPathLen - 1); + CFStringReplaceAll(cheapStr, tmpString); + if ((predefinedLocalizations && CFArrayContainsValue(predefinedLocalizations, predefinedLocalizationsRange, modifiedLangStr)) || (version != 4 && CFArrayContainsValue(contents, contentsRange, cheapStr))) { +#else + CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, pathLen, pathLen); + CFStringReplaceAll(cheapStr, tmpString); + if ((predefinedLocalizations && CFArrayContainsValue(predefinedLocalizations, predefinedLocalizationsRange, modifiedLangStr)) || (version != 4 && _CFIsResourceAtPath(cheapStr, &isDir) && isDir)) { +#endif + if (!CFArrayContainsValue(lprojNames, CFRangeMake(0, CFArrayGetCount(lprojNames)), modifiedLangStr)) CFArrayAppendValue(lprojNames, modifiedLangStr); + 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)) { + if (!altLangStr && (languageAbbreviation = _CFBundleCopyLanguageAbbreviationForLocalization(curLangStr)) && !CFEqual(curLangStr, languageAbbreviation)) { curLangLen = CFStringGetLength(languageAbbreviation); + if (curLangLen > 255) curLangLen = 255; CFStringGetCharacters(languageAbbreviation, CFRangeMake(0, curLangLen), curLangUniChars); pathLen = savedPathLen; - _CFAppendPathComponent(pathUniChars, &pathLen, CFMaxPathSize, curLangUniChars, curLangLen); - _CFAppendPathExtension(pathUniChars, &pathLen, CFMaxPathSize, _LprojUniChars, _LprojLen); + if (_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))) { + CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars + savedPathLen + 1, pathLen - savedPathLen - 1, pathLen - savedPathLen - 1); + CFStringReplaceAll(cheapStr, tmpString); + if ((predefinedLocalizations && CFArrayContainsValue(predefinedLocalizations, predefinedLocalizationsRange, languageAbbreviation)) || (version != 4 && CFArrayContainsValue(contents, contentsRange, cheapStr))) { #else - CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, pathLen, pathLen); - CFStringReplaceAll(cheapStr, tmpString); - if ((predefinedLocalizations && CFArrayContainsValue(predefinedLocalizations, predefinedLocalizationsRange, languageAbbreviation)) || (version != 4 && _CFIsResourceAtPath(cheapStr, &isDir) && isDir)) { + 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 (!CFArrayContainsValue(lprojNames, CFRangeMake(0, CFArrayGetCount(lprojNames)), languageAbbreviation)) CFArrayAppendValue(lprojNames, languageAbbreviation); + foundOne = true; + } } } - if (languageName && !CFEqual(curLangStr, languageName)) { + if (!altLangStr && (languageName = _CFBundleCopyLanguageNameForLocalization(curLangStr)) && !CFEqual(curLangStr, languageName)) { curLangLen = CFStringGetLength(languageName); + if (curLangLen > 255) curLangLen = 255; CFStringGetCharacters(languageName, CFRangeMake(0, curLangLen), curLangUniChars); pathLen = savedPathLen; - _CFAppendPathComponent(pathUniChars, &pathLen, CFMaxPathSize, curLangUniChars, curLangLen); - _CFAppendPathExtension(pathUniChars, &pathLen, CFMaxPathSize, _LprojUniChars, _LprojLen); + if (_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))) { + CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars + savedPathLen + 1, pathLen - savedPathLen - 1, pathLen - savedPathLen - 1); + CFStringReplaceAll(cheapStr, tmpString); + if ((predefinedLocalizations && CFArrayContainsValue(predefinedLocalizations, predefinedLocalizationsRange, languageName)) || (version != 4 && CFArrayContainsValue(contents, contentsRange, cheapStr))) { #else - CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, pathLen, pathLen); - CFStringReplaceAll(cheapStr, tmpString); - if ((predefinedLocalizations && CFArrayContainsValue(predefinedLocalizations, predefinedLocalizationsRange, languageName)) || (version != 4 && _CFIsResourceAtPath(cheapStr, &isDir) && isDir)) { + 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; + if (!CFArrayContainsValue(lprojNames, CFRangeMake(0, CFArrayGetCount(lprojNames)), languageName)) CFArrayAppendValue(lprojNames, languageName); + foundOne = true; + } } } - - CFRelease(cheapStr); - CFRelease(tmpString); + if (modifiedLangStr) CFRelease(modifiedLangStr); if (languageAbbreviation) CFRelease(languageAbbreviation); if (languageName) CFRelease(languageName); + if (canonicalLanguageIdentifier) CFRelease(canonicalLanguageIdentifier); + if (canonicalLanguageIdentifiers) CFRelease(canonicalLanguageIdentifiers); + if (predefinedCanonicalLanguageIdentifiers) CFRelease(predefinedCanonicalLanguageIdentifiers); + CFRelease(cheapStr); + CFRelease(tmpString); #if USE_GETDIRENTRIES - CFRelease(directoryContents); + CFRelease(contents); #endif return foundOne; @@ -1326,6 +1492,7 @@ __private_extern__ void _CFBundleAddPreferredLprojNamesInDirectory(CFAllocatorRe resourcesPath = CFURLCopyFileSystemPath(absoluteURL, PLATFORM_PATH_STYLE); CFRelease(absoluteURL); pathLen = CFStringGetLength(resourcesPath); + if (pathLen > CFMaxPathSize) pathLen = CFMaxPathSize; CFStringGetCharacters(resourcesPath, CFRangeMake(0, pathLen), pathUniChars); CFRelease(resourcesURL); CFRelease(resourcesPath); @@ -1371,30 +1538,52 @@ __private_extern__ void _CFBundleAddPreferredLprojNamesInDirectory(CFAllocatorRe 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); + CFStringRef altLangStr = NULL, modifiedLangStr = NULL, languageAbbreviation = NULL, languageName = NULL, canonicalLanguageIdentifier = NULL; + CFMutableDictionaryRef canonicalLanguageIdentifiers = NULL; + CFIndex idx; + if (range.length == 0) return foundOne; if (CFArrayContainsValue(array, range, curLangStr)) { - // We found one. - CFArrayAppendValue(lprojNames, curLangStr); + if (!CFArrayContainsValue(lprojNames, CFRangeMake(0, CFArrayGetCount(lprojNames)), curLangStr)) CFArrayAppendValue(lprojNames, curLangStr); + foundOne = true; + if (range.length == 1 || CFStringGetLength(curLangStr) <= 2) return foundOne; + } + if (range.length == 1 && CFArrayContainsValue(array, range, CFSTR("default"))) return foundOne; +#ifdef __CONSTANT_CFSTRINGS__ + for (idx = 0; !altLangStr && idx < NUM_COMMON_LANGUAGE_NAMES; idx++) { + if (CFEqual(curLangStr, __CFBundleCommonLanguageAbbreviationsArray[idx])) altLangStr = __CFBundleCommonLanguageNamesArray[idx]; + else if (CFEqual(curLangStr, __CFBundleCommonLanguageNamesArray[idx])) altLangStr = __CFBundleCommonLanguageAbbreviationsArray[idx]; + } +#endif // __CONSTANT_CFSTRINGS__ + if (foundOne && altLangStr) return foundOne; + if (altLangStr && CFArrayContainsValue(array, range, altLangStr)) { + if (!CFArrayContainsValue(lprojNames, CFRangeMake(0, CFArrayGetCount(lprojNames)), altLangStr)) CFArrayAppendValue(lprojNames, altLangStr); foundOne = true; + return foundOne; } - if (languageAbbreviation && !CFEqual(curLangStr, languageAbbreviation)) { + if (!altLangStr && (modifiedLangStr = _CFBundleCopyModifiedLocalization(curLangStr))) { + if (CFArrayContainsValue(array, range, modifiedLangStr)) { + if (!CFArrayContainsValue(lprojNames, CFRangeMake(0, CFArrayGetCount(lprojNames)), modifiedLangStr)) CFArrayAppendValue(lprojNames, modifiedLangStr); + foundOne = true; + } + } + if (!altLangStr && (languageAbbreviation = _CFBundleCopyLanguageAbbreviationForLocalization(curLangStr)) && !CFEqual(curLangStr, languageAbbreviation)) { if (CFArrayContainsValue(array, range, languageAbbreviation)) { - // We found one. - CFArrayAppendValue(lprojNames, languageAbbreviation); + if (!CFArrayContainsValue(lprojNames, CFRangeMake(0, CFArrayGetCount(lprojNames)), languageAbbreviation)) CFArrayAppendValue(lprojNames, languageAbbreviation); foundOne = true; } } - if (languageName && !CFEqual(curLangStr, languageName)) { + if (!altLangStr && (languageName = _CFBundleCopyLanguageNameForLocalization(curLangStr)) && !CFEqual(curLangStr, languageName)) { if (CFArrayContainsValue(array, range, languageName)) { - // We found one. - CFArrayAppendValue(lprojNames, languageName); + if (!CFArrayContainsValue(lprojNames, CFRangeMake(0, CFArrayGetCount(lprojNames)), languageName)) CFArrayAppendValue(lprojNames, languageName); foundOne = true; } } - + if (modifiedLangStr) CFRelease(modifiedLangStr); if (languageAbbreviation) CFRelease(languageAbbreviation); if (languageName) CFRelease(languageName); + if (canonicalLanguageIdentifier) CFRelease(canonicalLanguageIdentifier); + if (canonicalLanguageIdentifiers) CFRelease(canonicalLanguageIdentifiers); return foundOne; } @@ -1500,9 +1689,6 @@ CF_INLINE Boolean _CFBundleURLHasSubDir(CFURLRef url, CFStringRef subDirName) { } __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 @@ -1510,31 +1696,38 @@ __private_extern__ Boolean _CFBundleURLLooksLikeBundleVersion(CFURLRef url, uint // version 2: modern "Contents" bundles // version 3: none of the above (see below) // version 4: not a bundle (for main bundle only) + uint8_t localVersion = 3; +#if USE_GETDIRENTRIES + CFURLRef absoluteURL = CFURLCopyAbsoluteURL(url); + CFStringRef directoryPath = CFURLCopyFileSystemPath(absoluteURL, PLATFORM_PATH_STYLE); + CFArrayRef contents = _CFBundleCopyDirectoryContentsAtPath(directoryPath, _CFBundleAllContents); + CFRange contentsRange = CFRangeMake(0, CFArrayGetCount(contents)); if (CFStringHasSuffix(CFURLGetString(url), CFSTR(".framework/"))) { - if (_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; - } + if (CFArrayContainsValue(contents, contentsRange, _CFBundleResourcesDirectoryName)) localVersion = 0; + else if (CFArrayContainsValue(contents, contentsRange, _CFBundleSupportFilesDirectoryName2)) localVersion = 2; + else if (CFArrayContainsValue(contents, contentsRange, _CFBundleSupportFilesDirectoryName1)) localVersion = 1; } else { - if (_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 (CFArrayContainsValue(contents, contentsRange, _CFBundleSupportFilesDirectoryName2)) localVersion = 2; + else if (CFArrayContainsValue(contents, contentsRange, _CFBundleResourcesDirectoryName)) localVersion = 0; + else if (CFArrayContainsValue(contents, contentsRange, _CFBundleSupportFilesDirectoryName1)) localVersion = 1; + } + CFRelease(contents); + CFRelease(directoryPath); + CFRelease(absoluteURL); +#endif + if (localVersion == 3) { + if (CFStringHasSuffix(CFURLGetString(url), CFSTR(".framework/"))) { + if (_CFBundleURLHasSubDir(url, _CFBundleResourcesURLFromBase0)) localVersion = 0; + else if (_CFBundleURLHasSubDir(url, _CFBundleSupportFilesURLFromBase2)) localVersion = 2; + else if (_CFBundleURLHasSubDir(url, _CFBundleSupportFilesURLFromBase1)) localVersion = 1; + } else { + if (_CFBundleURLHasSubDir(url, _CFBundleSupportFilesURLFromBase2)) localVersion = 2; + else if (_CFBundleURLHasSubDir(url, _CFBundleResourcesURLFromBase0)) localVersion = 0; + else if (_CFBundleURLHasSubDir(url, _CFBundleSupportFilesURLFromBase1)) localVersion = 1; } } - if (result && version) *version = localVersion; - return result; + if (version) *version = localVersion; + return !(localVersion == 3); } __private_extern__ CFDictionaryRef _CFBundleCopyInfoDictionaryInDirectory(CFAllocatorRef alloc, CFURLRef url, uint8_t *version) { @@ -1568,30 +1761,30 @@ __private_extern__ CFDictionaryRef _CFBundleCopyInfoDictionaryInDirectoryWithVer CFStringRef infoURLFromBaseNoExtension = _CFBundleInfoURLFromBaseNoExtension0, infoURLFromBase = _CFBundleInfoURLFromBase0; Boolean tryPlatformSpecific = true, tryGlobal = true; #if USE_GETDIRENTRIES - CFArrayRef directoryContents = NULL; - CFRange directoryContentsRange = CFRangeMake(0, 0); + CFURLRef directoryURL = NULL, absoluteURL; + CFStringRef directoryPath; + CFArrayRef contents = NULL; + CFRange contentsRange = 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); + directoryURL = CFURLCreateWithString(alloc, _CFBundleResourcesURLFromBase0, url); #endif infoURLFromBaseNoExtension = _CFBundleInfoURLFromBaseNoExtension0; infoURLFromBase = _CFBundleInfoURLFromBase0; } else if (1 == version) { +#if USE_GETDIRENTRIES + directoryURL = CFURLCreateWithString(alloc, _CFBundleSupportFilesURLFromBase1, url); +#endif infoURLFromBaseNoExtension = _CFBundleInfoURLFromBaseNoExtension1; infoURLFromBase = _CFBundleInfoURLFromBase1; } else if (2 == version) { +#if USE_GETDIRENTRIES + directoryURL = CFURLCreateWithString(alloc, _CFBundleSupportFilesURLFromBase2, url); +#endif infoURLFromBaseNoExtension = _CFBundleInfoURLFromBaseNoExtension2; infoURLFromBase = _CFBundleInfoURLFromBase2; } else if (3 == version) { @@ -1599,13 +1792,27 @@ __private_extern__ CFDictionaryRef _CFBundleCopyInfoDictionaryInDirectoryWithVer // this test is necessary to exclude the case where a bundle is spuriously created from the innards of another bundle if (posixPath) { if (!(CFStringHasSuffix(posixPath, _CFBundleSupportFilesDirectoryName1) || CFStringHasSuffix(posixPath, _CFBundleSupportFilesDirectoryName2) || CFStringHasSuffix(posixPath, _CFBundleResourcesDirectoryName))) { +#if USE_GETDIRENTRIES + directoryURL = CFRetain(url); +#endif infoURLFromBaseNoExtension = _CFBundleInfoURLFromBaseNoExtension3; infoURLFromBase = _CFBundleInfoURLFromBase3; } CFRelease(posixPath); } } - +#if USE_GETDIRENTRIES + if (directoryURL) { + absoluteURL = CFURLCopyAbsoluteURL(directoryURL); + directoryPath = CFURLCopyFileSystemPath(absoluteURL, PLATFORM_PATH_STYLE); + contents = _CFBundleCopyDirectoryContentsAtPath(directoryPath, _CFBundleAllContents); + contentsRange = CFRangeMake(0, CFArrayGetCount(contents)); + CFRelease(directoryPath); + CFRelease(absoluteURL); + CFRelease(directoryURL); + } +#endif + len = CFStringGetLength(infoURLFromBaseNoExtension); CFStringGetCharacters(infoURLFromBaseNoExtension, CFRangeMake(0, len), buff); buff[len++] = (UniChar)'-'; @@ -1616,25 +1823,35 @@ __private_extern__ CFDictionaryRef _CFBundleCopyInfoDictionaryInDirectoryWithVer CFStringAppendCharacters(cheapStr, buff, len); infoURL = CFURLCreateWithString(alloc, cheapStr, url); #if USE_GETDIRENTRIES - if (directoryContents) { - CFIndex resourcesLen = CFStringGetLength(_CFBundleResourcesURLFromBase0); + if (contents) { + CFIndex resourcesLen, idx; + for (resourcesLen = len; resourcesLen > 0; resourcesLen--) if (buff[resourcesLen - 1] == '/') break; CFStringDelete(cheapStr, CFRangeMake(0, CFStringGetLength(cheapStr))); CFStringAppendCharacters(cheapStr, buff + resourcesLen, len - resourcesLen); - tryPlatformSpecific = CFArrayContainsValue(directoryContents, directoryContentsRange, cheapStr); + for (tryPlatformSpecific = false, idx = 0; !tryPlatformSpecific && idx < contentsRange.length; idx++) { + // Need to do this case-insensitive to accommodate Palm + if (kCFCompareEqualTo == CFStringCompare(cheapStr, CFArrayGetValueAtIndex(contents, idx), kCFCompareCaseInsensitive)) tryPlatformSpecific = true; + } } #endif if (tryPlatformSpecific) CFURLCreateDataAndPropertiesFromResource(alloc, infoURL, &infoData, NULL, NULL, NULL); - //fprintf(stderr, "looking for ");CFShow(infoURL);if (infoData) fprintf(stderr, "found it\n"); + //fprintf(stderr, "looking for ");CFShow(infoURL);fprintf(stderr, infoData ? "found it\n" : (tryPlatformSpecific ? "missed it\n" : "skipped it\n")); CFRelease(cheapStr); if (!infoData) { // Check for global Info.plist CFRelease(infoURL); infoURL = CFURLCreateWithString(alloc, infoURLFromBase, url); #if USE_GETDIRENTRIES - if (directoryContents) tryGlobal = CFArrayContainsValue(directoryContents, directoryContentsRange, _CFBundleInfoFileName); + if (contents) { + CFIndex idx; + for (tryGlobal = false, idx = 0; !tryGlobal && idx < contentsRange.length; idx++) { + // Need to do this case-insensitive to accommodate Palm + if (kCFCompareEqualTo == CFStringCompare(_CFBundleInfoFileName, CFArrayGetValueAtIndex(contents, idx), kCFCompareCaseInsensitive)) tryGlobal = true; + } + } #endif if (tryGlobal) CFURLCreateDataAndPropertiesFromResource(alloc, infoURL, &infoData, NULL, NULL, NULL); - //fprintf(stderr, "looking for ");CFShow(infoURL);if (infoData) fprintf(stderr, "found it\n"); + //fprintf(stderr, "looking for ");CFShow(infoURL);fprintf(stderr, infoData ? "found it\n" : (tryGlobal ? "missed it\n" : "skipped it\n")); } if (infoData) { @@ -1655,7 +1872,7 @@ __private_extern__ CFDictionaryRef _CFBundleCopyInfoDictionaryInDirectoryWithVer CFRelease(infoURL); #if USE_GETDIRENTRIES - if (directoryContents) CFRelease(directoryContents); + if (contents) CFRelease(contents); #endif } return result; @@ -1739,6 +1956,7 @@ static Boolean _CFBundleGetPackageInfoInDirectoryWithInfoDictionary(CFAllocatorR urlStr = CFURLCopyFileSystemPath(absoluteURL, PLATFORM_PATH_STYLE); CFRelease(absoluteURL); strLen = CFStringGetLength(urlStr); + if (strLen > CFMaxPathSize) strLen = CFMaxPathSize; CFStringGetCharacters(urlStr, CFRangeMake(0, strLen), buff); CFRelease(urlStr); startOfExtension = _CFStartOfPathExtension(buff, strLen); @@ -1872,11 +2090,18 @@ __private_extern__ CFStringRef _CFBundleGetOtherAlternatePlatformExecutablesSubd __private_extern__ CFArrayRef _CFBundleCopyBundleRegionsArray(CFBundleRef bundle) {return CFBundleCopyBundleLocalizations(bundle);} CF_EXPORT CFArrayRef CFBundleCopyBundleLocalizations(CFBundleRef bundle) { + CFDictionaryRef infoDict = CFBundleGetInfoDictionary(bundle); CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(bundle); - uint8_t version = _CFBundleLayoutVersion(bundle); - CFArrayRef urls = ((version != 4) ? _CFContentsOfDirectory(CFGetAllocator(bundle), NULL, NULL, resourcesURL, CFSTR("lproj")) : NULL); +#if USE_GETDIRENTRIES + CFURLRef absoluteURL; + CFStringRef directoryPath; + CFArrayRef contents; + CFRange contentsRange; + CFIndex idx; +#else + CFArrayRef urls = ((_CFBundleLayoutVersion(bundle) != 4) ? _CFContentsOfDirectory(CFGetAllocator(bundle), NULL, NULL, resourcesURL, _CFBundleLprojExtension) : NULL); +#endif CFArrayRef predefinedLocalizations = NULL; - CFDictionaryRef infoDict = CFBundleGetInfoDictionary(bundle); CFMutableArrayRef result = NULL; if (infoDict) { @@ -1887,32 +2112,46 @@ CF_EXPORT CFArrayRef CFBundleCopyBundleLocalizations(CFBundleRef bundle) { } 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++) CFArrayAppendValue(result, CFArrayGetValueAtIndex(predefinedLocalizations, i)); } } +#if USE_GETDIRENTRIES + if (resourcesURL) { + absoluteURL = CFURLCopyAbsoluteURL(resourcesURL); + directoryPath = CFURLCopyFileSystemPath(absoluteURL, PLATFORM_PATH_STYLE); + contents = _CFBundleCopyDirectoryContentsAtPath(directoryPath, _CFBundleAllContents); + contentsRange = CFRangeMake(0, CFArrayGetCount(contents)); + for (idx = 0; idx < contentsRange.length; idx++) { + CFStringRef name = CFArrayGetValueAtIndex(contents, idx); + if (CFStringHasSuffix(name, _CFBundleLprojExtensionWithDot)) { + CFStringRef localization = CFStringCreateWithSubstring(NULL, name, CFRangeMake(0, CFStringGetLength(name) - 6)); + if (!result) result = CFArrayCreateMutable(CFGetAllocator(bundle), 0, &kCFTypeArrayCallBacks); + CFArrayAppendValue(result, localization); + CFRelease(localization); + } + } + CFRelease(contents); + CFRelease(directoryPath); + CFRelease(absoluteURL); + } +#else if (urls) { - CFIndex i, c; + CFIndex i, c = CFArrayGetCount(urls); CFURLRef curURL, curAbsoluteURL; CFStringRef curStr, regionStr; UniChar buff[CFMaxPathSize]; CFIndex strLen, startOfLastPathComponent, regionLen; - c = CFArrayGetCount(urls); - if (c > 0 && !result) { - result = CFArrayCreateMutable(CFGetAllocator(bundle), 0, &kCFTypeArrayCallBacks); - } + if (c > 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); + if (strLen > CFMaxPathSize) strLen = CFMaxPathSize; CFStringGetCharacters(curStr, CFRangeMake(0, strLen), buff); startOfLastPathComponent = _CFStartOfLastPathComponent(buff, strLen); @@ -1924,6 +2163,8 @@ CF_EXPORT CFArrayRef CFBundleCopyBundleLocalizations(CFBundleRef bundle) { } CFRelease(urls); } +#endif + if (!result) { CFStringRef developmentLocalization = CFBundleGetDevelopmentRegion(bundle); if (developmentLocalization) { diff --git a/PlugIn.subproj/CFPlugIn.c b/PlugIn.subproj/CFPlugIn.c index 4cbab51..46ecba7 100644 --- a/PlugIn.subproj/CFPlugIn.c +++ b/PlugIn.subproj/CFPlugIn.c @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 diff --git a/PlugIn.subproj/CFPlugIn.h b/PlugIn.subproj/CFPlugIn.h index e160d3f..82b0bbd 100644 --- a/PlugIn.subproj/CFPlugIn.h +++ b/PlugIn.subproj/CFPlugIn.h @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -23,7 +21,7 @@ * @APPLE_LICENSE_HEADER_END@ */ /* CFPlugIn.h - Copyright (c) 1999-2003, Apple, Inc. All rights reserved. + Copyright (c) 1999-2005, Apple, Inc. All rights reserved. */ #if !defined(__COREFOUNDATION_CFPLUGIN__) diff --git a/PlugIn.subproj/CFPlugInCOM.h b/PlugIn.subproj/CFPlugInCOM.h index 671de8b..21c932b 100644 --- a/PlugIn.subproj/CFPlugInCOM.h +++ b/PlugIn.subproj/CFPlugInCOM.h @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -23,7 +21,7 @@ * @APPLE_LICENSE_HEADER_END@ */ /* CFPlugInCOM.h - Copyright (c) 1999-2003, Apple, Inc. All rights reserved. + Copyright (c) 1999-2005, Apple, Inc. All rights reserved. */ #if !defined(__COREFOUNDATION_CFPLUGINCOM__) diff --git a/PlugIn.subproj/CFPlugIn_Factory.c b/PlugIn.subproj/CFPlugIn_Factory.c index 530556d..436d458 100644 --- a/PlugIn.subproj/CFPlugIn_Factory.c +++ b/PlugIn.subproj/CFPlugIn_Factory.c @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -163,7 +161,7 @@ __private_extern__ void *_CFPFactoryCreateInstance(CFAllocatorRef allocator, _CF 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); + factory->_func = (void *)((uint32_t)(factory->_func) | 0x1); } #endif } diff --git a/PlugIn.subproj/CFPlugIn_Factory.h b/PlugIn.subproj/CFPlugIn_Factory.h index 84c0d3d..51fe51f 100644 --- a/PlugIn.subproj/CFPlugIn_Factory.h +++ b/PlugIn.subproj/CFPlugIn_Factory.h @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -23,7 +21,7 @@ * @APPLE_LICENSE_HEADER_END@ */ /* CFPlugIn_Factory.h - Copyright (c) 1999-2003, Apple, Inc. All rights reserved. + Copyright (c) 1999-2005, Apple, Inc. All rights reserved. */ #if !defined(__COREFOUNDATION_CFPLUGIN_FACTORY__) diff --git a/PlugIn.subproj/CFPlugIn_Instance.c b/PlugIn.subproj/CFPlugIn_Instance.c index 89790f1..3705c6b 100644 --- a/PlugIn.subproj/CFPlugIn_Instance.c +++ b/PlugIn.subproj/CFPlugIn_Instance.c @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 diff --git a/PlugIn.subproj/CFPlugIn_PlugIn.c b/PlugIn.subproj/CFPlugIn_PlugIn.c index 6122003..4e461aa 100644 --- a/PlugIn.subproj/CFPlugIn_PlugIn.c +++ b/PlugIn.subproj/CFPlugIn_PlugIn.c @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -66,6 +64,19 @@ static void _registerType(const void *key, const void *val, void *context) { if (NULL != typeID) CFRelease(typeID); } +__private_extern__ Boolean _CFBundleNeedsInitPlugIn(CFBundleRef bundle) { + Boolean result = false; + CFDictionaryRef infoDict = CFBundleGetInfoDictionary(bundle), factoryDict; + CFStringRef tempStr; + if (infoDict) { + factoryDict = CFDictionaryGetValue(infoDict, kCFPlugInFactoriesKey); + if (factoryDict != NULL && CFGetTypeID(factoryDict) == CFDictionaryGetTypeID()) result = true; + tempStr = CFDictionaryGetValue(infoDict, kCFPlugInDynamicRegistrationKey); + if (tempStr != NULL && CFGetTypeID(tempStr) == CFStringGetTypeID() && CFStringCompare(tempStr, CFSTR("YES"), kCFCompareCaseInsensitive) == kCFCompareEqualTo) result = true; + } + return result; +} + __private_extern__ void _CFBundleInitPlugIn(CFBundleRef bundle) { CFArrayCallBacks _pluginFactoryArrayCallbacks = {0, NULL, NULL, NULL, NULL}; Boolean doDynamicReg = false; diff --git a/Preferences.subproj/CFApplicationPreferences.c b/Preferences.subproj/CFApplicationPreferences.c new file mode 100644 index 0000000..f18b93a --- /dev/null +++ b/Preferences.subproj/CFApplicationPreferences.c @@ -0,0 +1,718 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFApplicationPreferences.c + Copyright 1998-2002, Apple, Inc. All rights reserved. + Responsibility: Chris Parker +*/ + +#include +#include "CFInternal.h" +#include +#include + +static Boolean _CFApplicationPreferencesSynchronizeNoLock(_CFApplicationPreferences *self); +void _CFPreferencesDomainSetMultiple(CFPreferencesDomainRef domain, CFDictionaryRef dict); +static void updateDictRep(_CFApplicationPreferences *self); +Boolean _CFApplicationPreferencesContainsDomainNoLock(_CFApplicationPreferences *self, CFPreferencesDomainRef domain); + +static void *__CFInsertionDomain = NULL; +static CFDictionaryRef __CFInsertion = NULL; + +// Bindings internals +extern CFSpinLock_t userDefaultsLock; +extern void *userDefaults; + +// Management externals +extern Boolean _CFPreferencesManagementActive; + + +#define CF_OBJC_KVO_WILLCHANGE(obj, sel) +#define CF_OBJC_KVO_DIDCHANGE(obj, sel) + + +// Right now, nothing is getting destroyed pretty much ever. We probably don't want this to be the case, but it's very tricky - domains live in the cache as well as a given application's preferences, and since they're not CFTypes, there's no reference count. Also, it's not clear we ever want domains destroyed. When they're synchronized, they clear their internal state (to force reading from the disk again), so they're not very big.... REW, 12/17/98 + +CFPropertyListRef CFPreferencesCopyAppValue(CFStringRef key, CFStringRef appName) { + _CFApplicationPreferences *standardPrefs; + CFAssert1(appName != NULL, __kCFLogAssertion, "%s(): Cannot access application preferences with a NULL application name", __PRETTY_FUNCTION__); + CFAssert1(key != NULL, __kCFLogAssertion, "%s(): Cannot access preferences with a NULL key", __PRETTY_FUNCTION__); + + standardPrefs = _CFStandardApplicationPreferences(appName); + return standardPrefs ? _CFApplicationPreferencesCreateValueForKey(standardPrefs, key) : NULL; +} + +CF_EXPORT Boolean CFPreferencesAppBooleanValue(CFStringRef key, CFStringRef appName, Boolean *keyExistsAndHasValidFormat) { + CFPropertyListRef value; + Boolean result, valid; + CFTypeID typeID = 0; + CFAssert1(appName != NULL, __kCFLogAssertion, "%s(): Cannot access application preferences with a NULL application name", __PRETTY_FUNCTION__); + CFAssert1(key != NULL, __kCFLogAssertion, "%s(): Cannot access preferences with a NULL key", __PRETTY_FUNCTION__); + + if (!keyExistsAndHasValidFormat) { + keyExistsAndHasValidFormat = &valid; + } + value = CFPreferencesCopyAppValue(key, appName); + if (!value) { + *keyExistsAndHasValidFormat = false; + return false; + } + typeID = CFGetTypeID(value); + if (typeID == CFStringGetTypeID()) { + if (CFStringCompare(value, CFSTR("true"), kCFCompareCaseInsensitive) == kCFCompareEqualTo || CFStringCompare(value, CFSTR("YES"), kCFCompareCaseInsensitive) == kCFCompareEqualTo) { + *keyExistsAndHasValidFormat = true; + result = true; + } else if (CFStringCompare(value, CFSTR("false"), kCFCompareCaseInsensitive) == kCFCompareEqualTo || CFStringCompare(value, CFSTR("NO"), kCFCompareCaseInsensitive) == kCFCompareEqualTo) { + *keyExistsAndHasValidFormat = true; + result = false; + } else { + *keyExistsAndHasValidFormat = false; + result = false; + } + } else if (typeID == CFNumberGetTypeID()) { + if (CFNumberIsFloatType(value)) { + *keyExistsAndHasValidFormat = false; + result = false; + } else { + int i; + *keyExistsAndHasValidFormat = true; + CFNumberGetValue(value, kCFNumberIntType, &i); + result = (i == 0) ? false : true; + } + } else if (typeID == CFBooleanGetTypeID()) { + result = (value == kCFBooleanTrue); + *keyExistsAndHasValidFormat = true; + } else { + // Unknown type + result = false; + *keyExistsAndHasValidFormat = false; + } + CFRelease(value); + return result; +} + +__private_extern__ CFIndex CFPreferencesAppIntegerValue(CFStringRef key, CFStringRef appName, Boolean *keyExistsAndHasValidFormat) { + CFPropertyListRef value; + CFIndex result; + CFTypeID typeID = 0; + Boolean valid; + CFAssert1(appName != NULL, __kCFLogAssertion, "%s(): Cannot access application preferences with a NULL application name", __PRETTY_FUNCTION__); + CFAssert1(key != NULL, __kCFLogAssertion, "%s(): Cannot access preferences with a NULL key", __PRETTY_FUNCTION__); + + value = CFPreferencesCopyAppValue(key, appName); + if (!keyExistsAndHasValidFormat) { + keyExistsAndHasValidFormat = &valid; + } + if (!value) { + *keyExistsAndHasValidFormat = false; + return 0; + } + typeID = CFGetTypeID(value); + if (typeID == CFStringGetTypeID()) { + CFIndex charIndex = 0; + SInt32 intVal; + CFStringInlineBuffer buf; + Boolean success; + CFStringInitInlineBuffer(value, &buf, CFRangeMake(0, CFStringGetLength(value))); + success = __CFStringScanInteger(&buf, NULL, &charIndex, false, &intVal); + *keyExistsAndHasValidFormat = (success && charIndex == CFStringGetLength(value)); + result = (*keyExistsAndHasValidFormat) ? intVal : 0; + } else if (typeID == CFNumberGetTypeID()) { + *keyExistsAndHasValidFormat = !CFNumberIsFloatType(value); + if (*keyExistsAndHasValidFormat) { + CFNumberGetValue(value, kCFNumberCFIndexType, &result); + } else { + result = 0; + } + } else { + // Unknown type + result = 0; + *keyExistsAndHasValidFormat = false; + } + CFRelease(value); + return result; +} + +Boolean CFPreferencesGetAppBooleanValue(CFStringRef key, CFStringRef appName, Boolean *keyExistsAndHasValidFormat) { + CFAssert1(appName != NULL, __kCFLogAssertion, "%s(): Cannot access application preferences with a NULL application name", __PRETTY_FUNCTION__); + CFAssert1(key != NULL, __kCFLogAssertion, "%s(): Cannot access preferences with a NULL key", __PRETTY_FUNCTION__); + return CFPreferencesAppBooleanValue(key, appName, keyExistsAndHasValidFormat); +} + +CFIndex CFPreferencesGetAppIntegerValue(CFStringRef key, CFStringRef appName, Boolean *keyExistsAndHasValidFormat) { + CFAssert1(appName != NULL, __kCFLogAssertion, "%s(): Cannot access application preferences with a NULL application name", __PRETTY_FUNCTION__); + CFAssert1(key != NULL, __kCFLogAssertion, "%s(): Cannot access preferences with a NULL key", __PRETTY_FUNCTION__); + return CFPreferencesAppIntegerValue(key, appName, keyExistsAndHasValidFormat); +} + +void CFPreferencesSetAppValue(CFStringRef key, CFTypeRef value, CFStringRef appName) { + _CFApplicationPreferences *standardPrefs; + CFAssert1(appName != NULL, __kCFLogAssertion, "%s(): Cannot access application preferences with a NULL application name", __PRETTY_FUNCTION__); + CFAssert1(key != NULL, __kCFLogAssertion, "%s(): Cannot access preferences with a NULL key", __PRETTY_FUNCTION__); + + standardPrefs = _CFStandardApplicationPreferences(appName); + if (standardPrefs) { + if (value) _CFApplicationPreferencesSet(standardPrefs, key, value); + else _CFApplicationPreferencesRemove(standardPrefs, key); + } +} + + +static CFSpinLock_t __CFApplicationPreferencesLock = 0; // Locks access to __CFStandardUserPreferences +static CFMutableDictionaryRef __CFStandardUserPreferences = NULL; // Mutable dictionary; keys are app names, values are _CFApplicationPreferences + +Boolean CFPreferencesAppSynchronize(CFStringRef appName) { + _CFApplicationPreferences *standardPrefs; + Boolean result; + CFAssert1(appName != NULL, __kCFLogAssertion, "%s(): Cannot access application preferences with a NULL application name", __PRETTY_FUNCTION__); + __CFPreferencesCheckFormatType(); + + // Do not call _CFStandardApplicationPreferences(), as we do not want to create the preferences only to synchronize + __CFSpinLock(&__CFApplicationPreferencesLock); + if (__CFStandardUserPreferences) { + standardPrefs = (_CFApplicationPreferences *)CFDictionaryGetValue(__CFStandardUserPreferences, appName); + } else { + standardPrefs = NULL; + } + + result = standardPrefs ? _CFApplicationPreferencesSynchronizeNoLock(standardPrefs) : _CFSynchronizeDomainCache(); + __CFSpinUnlock(&__CFApplicationPreferencesLock); + return result; +} + +void CFPreferencesFlushCaches(void) { + CFAllocatorRef alloc = __CFPreferencesAllocator(); + __CFSpinLock(&__CFApplicationPreferencesLock); + if (__CFStandardUserPreferences) { + _CFApplicationPreferences **prefsArray, *prefsBuf[32]; + CFIndex idx, count = CFDictionaryGetCount(__CFStandardUserPreferences); + if (count < 32) { + prefsArray = prefsBuf; + } else { + prefsArray = _CFAllocatorAllocateGC(alloc, count * sizeof(_CFApplicationPreferences *), 0); + } + CFDictionaryGetKeysAndValues(__CFStandardUserPreferences, NULL, (const void **)prefsArray); + + __CFSpinUnlock(&__CFApplicationPreferencesLock); + // DeallocateApplicationPreferences needs the lock + for (idx = 0; idx < count; idx ++) { + _CFApplicationPreferences *appPrefs = prefsArray[idx]; + _CFApplicationPreferencesSynchronize(appPrefs); + _CFDeallocateApplicationPreferences(appPrefs); + } + __CFSpinLock(&__CFApplicationPreferencesLock); + + CFRelease(__CFStandardUserPreferences); + __CFStandardUserPreferences = NULL; + if(prefsArray != prefsBuf) _CFAllocatorDeallocateGC(alloc, prefsArray); + } + __CFSpinUnlock(&__CFApplicationPreferencesLock); + _CFPreferencesPurgeDomainCache(); +} + +// quick message to indicate that the given domain has changed, and we should go through and invalidate any dictReps that involve this domain. +void _CFApplicationPreferencesDomainHasChanged(CFPreferencesDomainRef changedDomain) { + CFAllocatorRef alloc = __CFPreferencesAllocator(); + __CFSpinLock(&__CFApplicationPreferencesLock); + if(__CFStandardUserPreferences) { // only grovel over the prefs if there's something there to grovel + _CFApplicationPreferences **prefsArray, *prefsBuf[32]; + CFIndex idx, count = CFDictionaryGetCount(__CFStandardUserPreferences); + if(count < 32) { + prefsArray = prefsBuf; + } else { + prefsArray = _CFAllocatorAllocateGC(alloc, count * sizeof(_CFApplicationPreferences *), 0); + } + CFDictionaryGetKeysAndValues(__CFStandardUserPreferences, NULL, (const void **)prefsArray); + // For this operation, giving up the lock is the last thing we want to do, so use the modified flavor of _CFApplicationPreferencesContainsDomain + for(idx = 0; idx < count; idx++) { + _CFApplicationPreferences *appPrefs = prefsArray[idx]; + if(_CFApplicationPreferencesContainsDomainNoLock(appPrefs, changedDomain)) { + updateDictRep(appPrefs); + } + } + if(prefsArray != prefsBuf) _CFAllocatorDeallocateGC(alloc, prefsArray); + } + __CFSpinUnlock(&__CFApplicationPreferencesLock); +} + + +// Begin ported code from NSUserDefaults.m + +/*************** Constants ***************/ + +// NSString * const NSUserDefaultsDidChangeNotification = @"NSUserDefaultsDidChangeNotification"; + +static void updateDictRep(_CFApplicationPreferences *self) { + if (self->_dictRep) { + CFRelease(self->_dictRep); + self->_dictRep = NULL; + } +} + +static void __addKeysAndValues(const void *key, const void *value, void *context) { + CFDictionarySetValue(context, key, value); +} + +static void computeDictRep(_CFApplicationPreferences *self) { + CFAllocatorRef alloc = __CFPreferencesAllocator(); + CFMutableArrayRef searchList = self->_search; + CFIndex idx; + CFIndex cnt = CFArrayGetCount(searchList); + CFDictionaryRef subdomainDict; + + if (self->_dictRep) { + CFRelease(self->_dictRep); + } + self->_dictRep = CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks, & kCFTypeDictionaryValueCallBacks); + _CFDictionarySetCapacity(self->_dictRep, 160); // avoid lots of rehashing + + if (!self->_dictRep) return; + if (0 == cnt) return; + + // For each subdomain triplet in the domain, iterate over dictionaries, adding them if necessary to the dictRep + for (idx = cnt; idx--;) { + CFPreferencesDomainRef domain = (CFPreferencesDomainRef)CFArrayGetValueAtIndex(searchList, idx); + + if (!domain) continue; + + subdomainDict = _CFPreferencesDomainDeepCopyDictionary(domain); + if(subdomainDict) { + CFDictionaryApplyFunction(subdomainDict, __addKeysAndValues, self->_dictRep); + CFRelease(subdomainDict); + } + + if (__CFInsertionDomain == domain && __CFInsertion) { + CFDictionaryApplyFunction(__CFInsertion, __addKeysAndValues, self->_dictRep); + } + } +} + +CFTypeRef _CFApplicationPreferencesSearchDownToDomain(_CFApplicationPreferences *self, CFPreferencesDomainRef stopper, CFStringRef key) { + CFTypeRef value = NULL; + __CFSpinLock(&__CFApplicationPreferencesLock); + CFIndex idx, cnt; + cnt = CFArrayGetCount(self->_search); + for (idx = 0; idx < cnt; idx++) { + CFPreferencesDomainRef domain = (CFPreferencesDomainRef)CFArrayGetValueAtIndex(self->_search, idx); + if (domain == stopper) break; + value = _CFPreferencesDomainCreateValueForKey(domain, key); + if (value) break; + } + __CFSpinUnlock(&__CFApplicationPreferencesLock); + return value; +} + + +void _CFApplicationPreferencesUpdate(_CFApplicationPreferences *self) { + __CFSpinLock(&__CFApplicationPreferencesLock); + updateDictRep(self); + __CFSpinUnlock(&__CFApplicationPreferencesLock); +} + +__private_extern__ CFDictionaryRef __CFApplicationPreferencesCopyCurrentState(void) { + CFDictionaryRef result; + _CFApplicationPreferences *self = _CFStandardApplicationPreferences(kCFPreferencesCurrentApplication); + __CFSpinLock(&__CFApplicationPreferencesLock); + if (!self->_dictRep) { + computeDictRep(self); + } + result = CFDictionaryCreateCopy(kCFAllocatorSystemDefault, self->_dictRep); + __CFSpinUnlock(&__CFApplicationPreferencesLock); + return result; +} + +// CACHING here - we will only return a value as current as the last time computeDictRep() was called +CFTypeRef _CFApplicationPreferencesCreateValueForKey(_CFApplicationPreferences *self, CFStringRef defaultName) { + CFTypeRef result; + __CFSpinLock(&__CFApplicationPreferencesLock); + if (!self->_dictRep) { + computeDictRep(self); + } + result = (self->_dictRep) ? (CFTypeRef )CFDictionaryGetValue(self->_dictRep, defaultName) : NULL; + if (result) { + CFRetain(result); + } + __CFSpinUnlock(&__CFApplicationPreferencesLock); + return result; +} + +void _CFApplicationPreferencesSet(_CFApplicationPreferences *self, CFStringRef defaultName, CFTypeRef value) { + CFPreferencesDomainRef applicationDomain; + void *defs = NULL; + __CFSpinLock(&userDefaultsLock); + defs = userDefaults; + __CFSpinUnlock(&userDefaultsLock); + + CF_OBJC_KVO_WILLCHANGE(defs, defaultName); + __CFSpinLock(&__CFApplicationPreferencesLock); + applicationDomain = _CFPreferencesStandardDomain(self->_appName, kCFPreferencesCurrentUser, kCFPreferencesAnyHost); + if(applicationDomain) { + _CFPreferencesDomainSet(applicationDomain, defaultName, value); + if (CFArrayContainsValue(self->_search, CFRangeMake(0, CFArrayGetCount(self->_search)), applicationDomain)) { + // Expensive; can't we just check the relevant value throughout the appropriate sets of domains? -- REW, 7/19/99 + updateDictRep(self); + } + } + __CFSpinUnlock(&__CFApplicationPreferencesLock); + CF_OBJC_KVO_DIDCHANGE(defs, defaultName); +} + +void _CFApplicationPreferencesRemove(_CFApplicationPreferences *self, CFStringRef defaultName) { + CFPreferencesDomainRef appDomain; + __CFSpinLock(&__CFApplicationPreferencesLock); + appDomain = _CFPreferencesStandardDomain(self->_appName, kCFPreferencesCurrentUser, kCFPreferencesAnyHost); + if(appDomain) { + _CFPreferencesDomainSet(appDomain, defaultName, NULL); + if (CFArrayContainsValue(self->_search, CFRangeMake(0, CFArrayGetCount(self->_search)), appDomain)) { + // If key exists, it will be in the _dictRep (but possibly overridden) + updateDictRep(self); + } + } + __CFSpinUnlock(&__CFApplicationPreferencesLock); +} + +static Boolean _CFApplicationPreferencesSynchronizeNoLock(_CFApplicationPreferences *self) { + Boolean success = _CFSynchronizeDomainCache(); + if (self->_dictRep) { + CFRelease(self->_dictRep); + self->_dictRep = NULL; + } + return success; +} + +Boolean _CFApplicationPreferencesSynchronize(_CFApplicationPreferences *self) { + Boolean result; + __CFPreferencesCheckFormatType(); + __CFSpinLock(&__CFApplicationPreferencesLock); + result = _CFApplicationPreferencesSynchronizeNoLock(self); + __CFSpinUnlock(&__CFApplicationPreferencesLock); + return result; +} + +// appName should not be kCFPreferencesCurrentApplication going in to this call +_CFApplicationPreferences *_CFApplicationPreferencesCreateWithUser(CFStringRef userName, CFStringRef appName) { + CFAllocatorRef alloc = __CFPreferencesAllocator(); + _CFApplicationPreferences *self = CFAllocatorAllocate(alloc, sizeof(_CFApplicationPreferences), 0); + if (self) { + self->_dictRep = NULL; + self->_appName = CFRetain(appName); + self->_search = CFArrayCreateMutable(alloc, 0, &kCFTypeArrayCallBacks); + if (!self->_search) { + CFAllocatorDeallocate(alloc, self); + CFRelease(appName); + self = NULL; + } + } + return self; +} + +// Do NOT release the domain after adding it to the array; domain_expression should not return a retained object -- REW, 8/26/99 +#define ADD_DOMAIN(domain_expression) domain = domain_expression; if (domain) {CFArrayAppendValue(search, domain);} +void _CFApplicationPreferencesSetStandardSearchList(_CFApplicationPreferences *appPreferences) { + /* Now we add the standard domains; they are, in order: + this user, this app, this host + this user, this app, any host + this user, any app, this host + this user, any app, any host + any user, this app, this host + any user, this app, any host + any user, any app, this host + any user, any app, any host + + note: for MacOS 8, we only add: + this user, this app, this host + this user, any app, this host + any user, this app, this host + any user, any app, this host + */ + CFPreferencesDomainRef domain; + CFMutableArrayRef search = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + if (!search) { + // couldn't allocate memory! + return; + } + + ADD_DOMAIN(_CFPreferencesStandardDomain(appPreferences->_appName, kCFPreferencesCurrentUser, kCFPreferencesCurrentHost)); + ADD_DOMAIN(_CFPreferencesStandardDomain(appPreferences->_appName, kCFPreferencesCurrentUser, kCFPreferencesAnyHost)); + __CFInsertionDomain = _CFPreferencesStandardDomain(kCFPreferencesAnyApplication, kCFPreferencesCurrentUser, kCFPreferencesCurrentHost); + ADD_DOMAIN(__CFInsertionDomain); + ADD_DOMAIN(_CFPreferencesStandardDomain(kCFPreferencesAnyApplication, kCFPreferencesCurrentUser, kCFPreferencesAnyHost)); + ADD_DOMAIN(_CFPreferencesStandardDomain(appPreferences->_appName, kCFPreferencesAnyUser, kCFPreferencesCurrentHost)); + ADD_DOMAIN(_CFPreferencesStandardDomain(appPreferences->_appName, kCFPreferencesAnyUser, kCFPreferencesAnyHost)); + ADD_DOMAIN(_CFPreferencesStandardDomain(kCFPreferencesAnyApplication, kCFPreferencesAnyUser, kCFPreferencesCurrentHost)); + ADD_DOMAIN(_CFPreferencesStandardDomain(kCFPreferencesAnyApplication, kCFPreferencesAnyUser, kCFPreferencesAnyHost)); + + _CFApplicationPreferencesSetSearchList(appPreferences, search); + CFRelease(search); +} +#undef ADD_DOMAIN + + +__private_extern__ _CFApplicationPreferences *_CFStandardApplicationPreferences(CFStringRef appName) { + _CFApplicationPreferences *appPreferences; +// CFAssert(appName != kCFPreferencesAnyApplication, __kCFLogAssertion, "Cannot use any of the CFPreferences...App... functions with an appName of kCFPreferencesAnyApplication"); + __CFSpinLock(&__CFApplicationPreferencesLock); + if (!__CFStandardUserPreferences) { + __CFStandardUserPreferences = CFDictionaryCreateMutable(NULL, 0, & kCFTypeDictionaryKeyCallBacks, NULL); + } + if (!__CFStandardUserPreferences) { + // Couldn't create + __CFSpinUnlock(&__CFApplicationPreferencesLock); + return NULL; + } + if ((appPreferences = (_CFApplicationPreferences *)CFDictionaryGetValue(__CFStandardUserPreferences, appName)) == NULL ) { + appPreferences = _CFApplicationPreferencesCreateWithUser(kCFPreferencesCurrentUser, appName); + CFDictionarySetValue(__CFStandardUserPreferences, appName, appPreferences); + __CFSpinUnlock(&__CFApplicationPreferencesLock); + _CFApplicationPreferencesSetStandardSearchList(appPreferences); + } else { + __CFSpinUnlock(&__CFApplicationPreferencesLock); + } + return appPreferences; +} + +// Exclusively for Foundation's use +void _CFApplicationPreferencesSetCacheForApp(_CFApplicationPreferences *appPrefs, CFStringRef appName) { + __CFSpinLock(&__CFApplicationPreferencesLock); + if (!__CFStandardUserPreferences) { + __CFStandardUserPreferences = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, NULL); + CFDictionarySetValue(__CFStandardUserPreferences, appName, appPrefs); + __CFSpinUnlock(&__CFApplicationPreferencesLock); + } else { + _CFApplicationPreferences *oldPrefs = (_CFApplicationPreferences *)CFDictionaryGetValue(__CFStandardUserPreferences, appName); + CFDictionarySetValue(__CFStandardUserPreferences, appName, appPrefs); + __CFSpinUnlock(&__CFApplicationPreferencesLock); + if (oldPrefs) { + _CFDeallocateApplicationPreferences(oldPrefs); + } + } +} + + +void _CFDeallocateApplicationPreferences(_CFApplicationPreferences *self) { + CFAllocatorRef alloc = __CFPreferencesAllocator(); + _CFApplicationPreferences *cachedPrefs = NULL; + __CFSpinLock(&__CFApplicationPreferencesLock); + + // Get us out of the cache before destroying! + if (__CFStandardUserPreferences) { + cachedPrefs = (_CFApplicationPreferences *)CFDictionaryGetValue(__CFStandardUserPreferences, self->_appName); + } + if (cachedPrefs == self) { + CFDictionaryRemoveValue(__CFStandardUserPreferences, self->_appName); + } + + if (self->_dictRep) CFRelease(self->_dictRep); + CFRelease(self->_search); + CFRelease(self->_appName); + CFAllocatorDeallocate(alloc, self); + __CFSpinUnlock(&__CFApplicationPreferencesLock); +} + +// For Foundation's use +CFDictionaryRef _CFApplicationPreferencesCopyRepresentationWithHint(_CFApplicationPreferences *self, CFDictionaryRef hint) { + CFDictionaryRef dict; + __CFSpinLock(&__CFApplicationPreferencesLock); + if (!self->_dictRep) { + computeDictRep(self); + } + if (self->_dictRep && (self->_dictRep != hint)) { + CFRetain(self->_dictRep); + } + dict = self->_dictRep; + __CFSpinUnlock(&__CFApplicationPreferencesLock); + return dict; +} + +// For Foundation's use; does not do what it would seem to from the name +CFDictionaryRef _CFApplicationPreferencesCopyRepresentation3(_CFApplicationPreferences *self, CFDictionaryRef hint, CFDictionaryRef insertion, CFPreferencesDomainRef afterDomain) { + __CFSpinLock(&__CFApplicationPreferencesLock); + if (0 == self && 0 == hint && 0 == afterDomain) { + // This is so so gross. + if (__CFInsertion) CFRelease(__CFInsertion); + __CFInsertion = insertion ? CFRetain(insertion) : NULL; + } + __CFSpinUnlock(&__CFApplicationPreferencesLock); + return 0; +} + +CF_EXPORT +CFDictionaryRef _CFApplicationPreferencesCopyRepresentation(_CFApplicationPreferences *self) { + return _CFApplicationPreferencesCopyRepresentationWithHint(self, NULL); +} + +__private_extern__ void _CFApplicationPreferencesSetSearchList(_CFApplicationPreferences *self, CFArrayRef newSearchList) { + CFIndex idx, count; + __CFSpinLock(&__CFApplicationPreferencesLock); + CFArrayRemoveAllValues(self->_search); + count = CFArrayGetCount(newSearchList); + for (idx = 0; idx < count; idx ++) { + CFArrayAppendValue(self->_search, CFArrayGetValueAtIndex(newSearchList, idx)); + } + updateDictRep(self); + __CFSpinUnlock(&__CFApplicationPreferencesLock); +} + +void CFPreferencesAddSuitePreferencesToApp(CFStringRef appName, CFStringRef suiteName) { + _CFApplicationPreferences *appPrefs; + + appPrefs = _CFStandardApplicationPreferences(appName); + _CFApplicationPreferencesAddSuitePreferences(appPrefs, suiteName); +} + +void _CFApplicationPreferencesAddSuitePreferences(_CFApplicationPreferences *appPrefs, CFStringRef suiteName) { + CFPreferencesDomainRef domain; + CFIndex idx; + CFRange range; + + // Find where to insert the new suite + __CFSpinLock(&__CFApplicationPreferencesLock); + domain = _CFPreferencesStandardDomain(appPrefs->_appName, kCFPreferencesCurrentUser, kCFPreferencesAnyHost); + range.location = 0; + range.length = CFArrayGetCount(appPrefs->_search); + idx = domain ? CFArrayGetFirstIndexOfValue(appPrefs->_search, range, domain) : kCFNotFound; + __CFSpinUnlock(&__CFApplicationPreferencesLock); + idx ++; // We want just below the app domain. Coincidentally, this gives us the top of the list if the app domain has been removed. + domain = _CFPreferencesStandardDomain(suiteName, kCFPreferencesCurrentUser, kCFPreferencesAnyHost); + if (domain) { + __CFSpinLock(&__CFApplicationPreferencesLock); + CFArrayInsertValueAtIndex(appPrefs->_search, idx, domain); + __CFSpinUnlock(&__CFApplicationPreferencesLock); + range.length ++; + } + domain = _CFPreferencesStandardDomain(suiteName, kCFPreferencesCurrentUser, kCFPreferencesCurrentHost); + if (domain) { + __CFSpinLock(&__CFApplicationPreferencesLock); + CFArrayInsertValueAtIndex(appPrefs->_search, idx, domain); + __CFSpinUnlock(&__CFApplicationPreferencesLock); + range.length ++; + } + + // Now the AnyUser domains + domain = _CFPreferencesStandardDomain(appPrefs->_appName, kCFPreferencesAnyUser, kCFPreferencesAnyHost); + idx = domain ? CFArrayGetFirstIndexOfValue(appPrefs->_search, range, domain) : kCFNotFound; + if (idx == kCFNotFound) { + // Someone blew away the app domain. Can only happen through -[NSUserDefaults setSearchList:]. For the any user case, we look for right below the global domain + // Can this happen anymore? -[NSUserDefaults setSearchList:] is bailing. -- ctp - - 3 Jan 2002 + domain = _CFPreferencesStandardDomain(kCFPreferencesAnyApplication, kCFPreferencesCurrentUser, kCFPreferencesAnyHost); + idx = domain ? CFArrayGetFirstIndexOfValue(appPrefs->_search, range, domain) : kCFNotFound; + if (idx == kCFNotFound) { + // Try the "any host" choice + domain = _CFPreferencesStandardDomain(kCFPreferencesAnyApplication, kCFPreferencesCurrentUser, kCFPreferencesCurrentHost); + idx = domain ? CFArrayGetFirstIndexOfValue(appPrefs->_search, range, domain) : kCFNotFound; + if (idx == kCFNotFound) { + // We give up; put the new domains at the bottom + idx = CFArrayGetCount(appPrefs->_search) - 1; + } + } + } + idx ++; + domain = _CFPreferencesStandardDomain(suiteName, kCFPreferencesAnyUser, kCFPreferencesAnyHost); + if (domain) { + __CFSpinLock(&__CFApplicationPreferencesLock); + CFArrayInsertValueAtIndex(appPrefs->_search, idx, domain); + __CFSpinUnlock(&__CFApplicationPreferencesLock); + } + domain = _CFPreferencesStandardDomain(suiteName, kCFPreferencesAnyUser, kCFPreferencesCurrentHost); + if (domain) { + __CFSpinLock(&__CFApplicationPreferencesLock); + CFArrayInsertValueAtIndex(appPrefs->_search, idx, domain); + __CFSpinUnlock(&__CFApplicationPreferencesLock); + } + __CFSpinLock(&__CFApplicationPreferencesLock); + updateDictRep(appPrefs); + __CFSpinUnlock(&__CFApplicationPreferencesLock); +} + +void CFPreferencesRemoveSuitePreferencesFromApp(CFStringRef appName, CFStringRef suiteName) { + _CFApplicationPreferences *appPrefs; + + appPrefs = _CFStandardApplicationPreferences(appName); + + _CFApplicationPreferencesRemoveSuitePreferences(appPrefs, suiteName); +} + +void _CFApplicationPreferencesRemoveSuitePreferences(_CFApplicationPreferences *appPrefs, CFStringRef suiteName) { + CFPreferencesDomainRef domain; + + __CFSpinLock(&__CFApplicationPreferencesLock); + domain = _CFPreferencesStandardDomain(suiteName, kCFPreferencesCurrentUser, kCFPreferencesAnyHost); + __CFSpinUnlock(&__CFApplicationPreferencesLock); + if (domain) _CFApplicationPreferencesRemoveDomain(appPrefs, domain); + + __CFSpinLock(&__CFApplicationPreferencesLock); + domain = _CFPreferencesStandardDomain(suiteName, kCFPreferencesCurrentUser, kCFPreferencesCurrentHost); + __CFSpinUnlock(&__CFApplicationPreferencesLock); + if (domain) _CFApplicationPreferencesRemoveDomain(appPrefs, domain); + + __CFSpinLock(&__CFApplicationPreferencesLock); + domain = _CFPreferencesStandardDomain(suiteName, kCFPreferencesAnyUser, kCFPreferencesAnyHost); + __CFSpinUnlock(&__CFApplicationPreferencesLock); + if (domain) _CFApplicationPreferencesRemoveDomain(appPrefs, domain); + + __CFSpinLock(&__CFApplicationPreferencesLock); + domain = _CFPreferencesStandardDomain(suiteName, kCFPreferencesAnyUser, kCFPreferencesCurrentHost); + __CFSpinUnlock(&__CFApplicationPreferencesLock); + if (domain) _CFApplicationPreferencesRemoveDomain(appPrefs, domain); +} + +void _CFApplicationPreferencesAddDomain(_CFApplicationPreferences *self, CFPreferencesDomainRef domain, Boolean addAtTop) { + __CFSpinLock(&__CFApplicationPreferencesLock); + if (addAtTop) { + CFArrayInsertValueAtIndex(self->_search, 0, domain); + } else { + CFArrayAppendValue(self->_search, domain); + } + updateDictRep(self); + __CFSpinUnlock(&__CFApplicationPreferencesLock); +} + +Boolean _CFApplicationPreferencesContainsDomain(_CFApplicationPreferences *self, CFPreferencesDomainRef domain) { + Boolean result; + __CFSpinLock(&__CFApplicationPreferencesLock); + result = CFArrayContainsValue(self->_search, CFRangeMake(0, CFArrayGetCount(self->_search)), domain); + __CFSpinUnlock(&__CFApplicationPreferencesLock); + return result; +} + +Boolean _CFApplicationPreferencesContainsDomainNoLock(_CFApplicationPreferences *self, CFPreferencesDomainRef domain) { + Boolean result; + result = CFArrayContainsValue(self->_search, CFRangeMake(0, CFArrayGetCount(self->_search)), domain); + return result; +} + +void _CFApplicationPreferencesRemoveDomain(_CFApplicationPreferences *self, CFPreferencesDomainRef domain) { + CFIndex idx; + CFRange range; + __CFSpinLock(&__CFApplicationPreferencesLock); + range.location = 0; + range.length = CFArrayGetCount(self->_search); + while ((idx = CFArrayGetFirstIndexOfValue(self->_search, range, domain)) != kCFNotFound) { + CFArrayRemoveValueAtIndex(self->_search, idx); + range.location = idx; + range.length = range.length - idx - 1; + } + updateDictRep(self); + __CFSpinUnlock(&__CFApplicationPreferencesLock); +} + + diff --git a/Preferences.subproj/CFPreferences.c b/Preferences.subproj/CFPreferences.c new file mode 100644 index 0000000..1d31eae --- /dev/null +++ b/Preferences.subproj/CFPreferences.c @@ -0,0 +1,843 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFPreferences.c + Copyright 1998-2002, Apple, Inc. All rights reserved. + Responsibility: Chris Parker +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "CFInternal.h" +#include + +#if defined(__WIN32__) +#include +#endif +#if DEBUG_PREFERENCES_MEMORY +#include "../Tests/CFCountingAllocator.c" +#endif + +struct __CFPreferencesDomain { + CFRuntimeBase _base; + /* WARNING - not copying the callbacks; we know they are always static structs */ + const _CFPreferencesDomainCallBacks *_callBacks; + CFTypeRef _context; + void *_domain; +}; + +CONST_STRING_DECL(kCFPreferencesAnyApplication, "kCFPreferencesAnyApplication") +CONST_STRING_DECL(kCFPreferencesAnyHost, "kCFPreferencesAnyHost") +CONST_STRING_DECL(kCFPreferencesAnyUser, "kCFPreferencesAnyUser") +CONST_STRING_DECL(kCFPreferencesCurrentApplication, "kCFPreferencesCurrentApplication") +CONST_STRING_DECL(kCFPreferencesCurrentHost, "kCFPreferencesCurrentHost") +CONST_STRING_DECL(kCFPreferencesCurrentUser, "kCFPreferencesCurrentUser") + + +static CFAllocatorRef _preferencesAllocator = NULL; +__private_extern__ CFAllocatorRef __CFPreferencesAllocator(void) { + if (!_preferencesAllocator) { +#if DEBUG_PREFERENCES_MEMORY + _preferencesAllocator = CFCountingAllocatorCreate(NULL); +#else + _preferencesAllocator = __CFGetDefaultAllocator(); + CFRetain(_preferencesAllocator); +#endif + } + return _preferencesAllocator; +} + +// declaration for telling the +void _CFApplicationPreferencesDomainHasChanged(CFPreferencesDomainRef); + +#if DEBUG_PREFERENCES_MEMORY +#warning Preferences debugging on +CF_EXPORT void CFPreferencesDumpMem(void) { + if (_preferencesAllocator) { +// CFCountingAllocatorPrintSummary(_preferencesAllocator); + CFCountingAllocatorPrintPointers(_preferencesAllocator); + } +// CFCountingAllocatorReset(_preferencesAllocator); +} +#endif + +static unsigned long __CFSafeLaunchLevel = 0; + +static CFURLRef _preferencesDirectoryForUserHostSafetyLevel(CFStringRef userName, CFStringRef hostName, unsigned long safeLevel) { + CFAllocatorRef alloc = __CFPreferencesAllocator(); + CFURLRef home = NULL; + CFURLRef url; + int levels = 0; + // if (hostName != kCFPreferencesCurrentHost && hostName != kCFPreferencesAnyHost) return NULL; // Arbitrary host access not permitted + if (userName == kCFPreferencesAnyUser) { + if (!home) home = CFURLCreateWithFileSystemPath(alloc, CFSTR("/Library/Preferences/"), kCFURLPOSIXPathStyle, true); + levels = 1; + if (hostName == kCFPreferencesCurrentHost) url = home; + else { + url = CFURLCreateWithFileSystemPathRelativeToBase(alloc, CFSTR("Network/"), kCFURLPOSIXPathStyle, true, home); + levels ++; + CFRelease(home); + } + } else { + home = CFCopyHomeDirectoryURLForUser((userName == kCFPreferencesCurrentUser) ? NULL : userName); + if (home) { + url = (safeLevel > 0) ? CFURLCreateWithFileSystemPathRelativeToBase(alloc, CFSTR("Library/Safe Preferences/"), kCFURLPOSIXPathStyle, true, home) : + CFURLCreateWithFileSystemPathRelativeToBase(alloc, CFSTR("Library/Preferences/"), kCFURLPOSIXPathStyle, true, home); + levels = 2; + CFRelease(home); + if (hostName != kCFPreferencesAnyHost) { + home = url; + url = CFURLCreateWithFileSystemPathRelativeToBase(alloc, CFSTR("ByHost/"), kCFURLPOSIXPathStyle, true, home); + levels ++; + CFRelease(home); + } + } else { + url = NULL; + } + } + return url; +} + +static CFURLRef _preferencesDirectoryForUserHost(CFStringRef userName, CFStringRef hostName) { + return _preferencesDirectoryForUserHostSafetyLevel(userName, hostName, __CFSafeLaunchLevel); +} + +// Bindings internals +__private_extern__ CFSpinLock_t userDefaultsLock = 0; +__private_extern__ void *userDefaults = NULL; + +void _CFPreferencesSetStandardUserDefaults(void *sudPtr) { + __CFSpinLock(&userDefaultsLock); + userDefaults = sudPtr; + __CFSpinUnlock(&userDefaultsLock); +} + + +#define CF_OBJC_KVO_WILLCHANGE(obj, sel) +#define CF_OBJC_KVO_DIDCHANGE(obj, sel) + + +static Boolean __CFPreferencesWritesXML = false; + +Boolean __CFPreferencesShouldWriteXML(void) { + return __CFPreferencesWritesXML; +} + +void __CFPreferencesCheckFormatType(void) { + static int checked = 0; + if (!checked) { + checked = 1; + __CFPreferencesWritesXML = CFPreferencesGetAppBooleanValue(CFSTR("CFPreferencesWritesXML"), kCFPreferencesCurrentApplication, NULL); + } +} + +static CFSpinLock_t domainCacheLock = 0; +static CFMutableDictionaryRef domainCache = NULL; // mutable + +// Public API + +CFTypeRef CFPreferencesCopyValue(CFStringRef key, CFStringRef appName, CFStringRef user, CFStringRef host) { + CFPreferencesDomainRef domain; + CFAssert1(appName != NULL && user != NULL && host != NULL, __kCFLogAssertion, "%s(): Cannot access preferences for a NULL application name, user, or host", __PRETTY_FUNCTION__); + CFAssert1(key != NULL, __kCFLogAssertion, "%s(): Cannot access preferences with a NULL key", __PRETTY_FUNCTION__); + + domain = _CFPreferencesStandardDomain(appName, user, host); + if (domain) { + return _CFPreferencesDomainCreateValueForKey(domain, key); + } else { + return NULL; + } +} + +CFDictionaryRef CFPreferencesCopyMultiple(CFArrayRef keysToFetch, CFStringRef appName, CFStringRef userName, CFStringRef hostName) { + CFPreferencesDomainRef domain; + CFMutableDictionaryRef result; + CFIndex idx, count; + + CFAssert1(appName != NULL && userName != NULL && hostName != NULL, __kCFLogAssertion, "%s(): Cannot access preferences for a NULL application name, user, or host", __PRETTY_FUNCTION__); + __CFGenericValidateType(appName, CFStringGetTypeID()); + __CFGenericValidateType(userName, CFStringGetTypeID()); + __CFGenericValidateType(hostName, CFStringGetTypeID()); + + domain = _CFPreferencesStandardDomain(appName, userName, hostName); + if (!domain) return NULL; + if (!keysToFetch) { + return _CFPreferencesDomainDeepCopyDictionary(domain); + } else { + __CFGenericValidateType(keysToFetch, CFArrayGetTypeID()); + count = CFArrayGetCount(keysToFetch); + result = CFDictionaryCreateMutable(CFGetAllocator(domain), count, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + if (!result) return NULL; + for (idx = 0; idx < count; idx ++) { + CFStringRef key = CFArrayGetValueAtIndex(keysToFetch, idx); + CFPropertyListRef value; + __CFGenericValidateType(key, CFStringGetTypeID()); + value = _CFPreferencesDomainCreateValueForKey(domain, key); + if (value) { + CFDictionarySetValue(result, key, value); + CFRelease(value); + } + } + } + return result; +} + +void CFPreferencesSetValue(CFStringRef key, CFTypeRef value, CFStringRef appName, CFStringRef user, CFStringRef host) { + CFPreferencesDomainRef domain; + CFAssert1(appName != NULL && user != NULL && host != NULL, __kCFLogAssertion, "%s(): Cannot access preferences for a NULL application name, user, or host", __PRETTY_FUNCTION__); + CFAssert1(key != NULL, __kCFLogAssertion, "%s(): Cannot access preferences with a NULL key", __PRETTY_FUNCTION__); + + domain = _CFPreferencesStandardDomain(appName, user, host); + if (domain) { + void *defs = NULL; + __CFSpinLock(&userDefaultsLock); + defs = userDefaults; + __CFSpinUnlock(&userDefaultsLock); + CF_OBJC_KVO_WILLCHANGE(defs, key); + _CFPreferencesDomainSet(domain, key, value); + _CFApplicationPreferencesDomainHasChanged(domain); + CF_OBJC_KVO_DIDCHANGE(defs, key); + } +} + + +void CFPreferencesSetMultiple(CFDictionaryRef keysToSet, CFArrayRef keysToRemove, CFStringRef appName, CFStringRef userName, CFStringRef hostName) { + CFPreferencesDomainRef domain; + CFIndex idx, count; + CFAssert1(appName != NULL && userName != NULL && hostName != NULL, __kCFLogAssertion, "%s(): Cannot access preferences for a NULL application name, user, or host", __PRETTY_FUNCTION__); + if (keysToSet) __CFGenericValidateType(keysToSet, CFDictionaryGetTypeID()); + if (keysToRemove) __CFGenericValidateType(keysToRemove, CFArrayGetTypeID()); + __CFGenericValidateType(appName, CFStringGetTypeID()); + __CFGenericValidateType(userName, CFStringGetTypeID()); + __CFGenericValidateType(hostName, CFStringGetTypeID()); + + CFTypeRef *keys = NULL; + CFTypeRef *values; + CFIndex numOfKeysToSet = 0; + + domain = _CFPreferencesStandardDomain(appName, userName, hostName); + if (!domain) return; + + CFAllocatorRef alloc = CFGetAllocator(domain); + void *defs = NULL; + + __CFSpinLock(&userDefaultsLock); + defs = userDefaults; + __CFSpinUnlock(&userDefaultsLock); + + if (keysToSet && (count = CFDictionaryGetCount(keysToSet))) { + numOfKeysToSet = count; + keys = CFAllocatorAllocate(alloc, 2*count*sizeof(CFTypeRef), 0); + if (keys) { + values = &(keys[count]); + CFDictionaryGetKeysAndValues(keysToSet, keys, values); + for (idx = 0; idx < count; idx ++) { + CF_OBJC_KVO_WILLCHANGE(defs, keys[idx]); + _CFPreferencesDomainSet(domain, keys[idx], values[idx]); + } + } + } + if (keysToRemove && (count = CFArrayGetCount(keysToRemove))) { + for (idx = 0; idx < count; idx ++) { + CFStringRef removedKey = CFArrayGetValueAtIndex(keysToRemove, idx); + CF_OBJC_KVO_WILLCHANGE(defs, removedKey); + _CFPreferencesDomainSet(domain, removedKey, NULL); + } + } + + + _CFApplicationPreferencesDomainHasChanged(domain); + + // here, we have to do things in reverse order. + if(keysToRemove) { + count = CFArrayGetCount(keysToRemove); + for(idx = count - 1; idx >= 0; idx--) { + CF_OBJC_KVO_DIDCHANGE(defs, CFArrayGetValueAtIndex(keysToRemove, idx)); + } + } + + if(numOfKeysToSet > 0) { + for(idx = numOfKeysToSet - 1; idx >= 0; idx--) { + CF_OBJC_KVO_DIDCHANGE(defs, keys[idx]); + } + } + + if(keys) CFAllocatorDeallocate(alloc, keys); +} + +Boolean CFPreferencesSynchronize(CFStringRef appName, CFStringRef user, CFStringRef host) { + CFPreferencesDomainRef domain; + CFAssert1(appName != NULL && user != NULL && host != NULL, __kCFLogAssertion, "%s(): Cannot access preferences for a NULL application name, user, or host", __PRETTY_FUNCTION__); + + __CFPreferencesCheckFormatType(); + + domain = _CFPreferencesStandardDomain(appName, user, host); + if(domain) _CFApplicationPreferencesDomainHasChanged(domain); + + return domain ? _CFPreferencesDomainSynchronize(domain) : false; +} + +CFArrayRef CFPreferencesCopyApplicationList(CFStringRef userName, CFStringRef hostName) { + CFArrayRef array; + CFAssert1(userName != NULL && hostName != NULL, __kCFLogAssertion, "%s(): Cannot access preferences for a NULL user or host", __PRETTY_FUNCTION__); + array = _CFPreferencesCreateDomainList(userName, hostName); + return array; +} + +CFArrayRef CFPreferencesCopyKeyList(CFStringRef appName, CFStringRef userName, CFStringRef hostName) { + CFPreferencesDomainRef domain; + CFAssert1(appName != NULL && userName != NULL && hostName != NULL, __kCFLogAssertion, "%s(): Cannot access preferences for a NULL application name, user, or host", __PRETTY_FUNCTION__); + + domain = _CFPreferencesStandardDomain(appName, userName, hostName); + if (!domain) { + return NULL; + } else { + void **buf = NULL; + CFAllocatorRef alloc = __CFPreferencesAllocator(); + CFArrayRef result; + CFIndex numPairs = 0; + _CFPreferencesDomainGetKeysAndValues(alloc, domain, &buf, &numPairs); + if (numPairs == 0) { + result = NULL; + } else { + // It would be nice to avoid this allocation.... + result = CFArrayCreate(alloc, (const void **)buf, numPairs, &kCFTypeArrayCallBacks); + CFAllocatorDeallocate(alloc, buf); + } + return result; + } +} + + +/****************************/ +/* CFPreferencesDomain */ +/****************************/ + +static CFStringRef __CFPreferencesDomainCopyDescription(CFTypeRef cf) { + return CFStringCreateWithFormat(__CFPreferencesAllocator(), NULL, CFSTR("\n"), (UInt32)cf); +} + +static void __CFPreferencesDomainDeallocate(CFTypeRef cf) { + const struct __CFPreferencesDomain *domain = cf; + CFAllocatorRef alloc = __CFPreferencesAllocator(); + domain->_callBacks->freeDomain(alloc, domain->_context, domain->_domain); + if (domain->_context) CFRelease(domain->_context); +} + +static CFTypeID __kCFPreferencesDomainTypeID = _kCFRuntimeNotATypeID; + +static const CFRuntimeClass __CFPreferencesDomainClass = { + 0, + "CFPreferencesDomain", + NULL, // init + NULL, // copy + __CFPreferencesDomainDeallocate, + NULL, + NULL, + NULL, // + __CFPreferencesDomainCopyDescription +}; + +/* This is called once at CFInitialize() time. */ +__private_extern__ void __CFPreferencesDomainInitialize(void) { + __kCFPreferencesDomainTypeID = _CFRuntimeRegisterClass(&__CFPreferencesDomainClass); +} + +/* We spend a lot of time constructing these prefixes; we should cache. REW, 7/19/99 */ +__private_extern__ CFStringRef _CFPreferencesCachePrefixForUserHost(CFStringRef userName, CFStringRef hostName) { + Boolean freeHost = false; + CFStringRef result; + if (userName == kCFPreferencesCurrentUser) { + userName = CFGetUserName(); + } else if (userName == kCFPreferencesAnyUser) { + userName = CFSTR("*"); + } + + if (hostName == kCFPreferencesCurrentHost) { + hostName = __CFCopyEthernetAddrString(); + if (!hostName) hostName = _CFStringCreateHostName(); + freeHost = true; + } else if (hostName == kCFPreferencesAnyHost) { + hostName = CFSTR("*"); + } + result = CFStringCreateWithFormat(__CFPreferencesAllocator(), NULL, CFSTR("%@/%@/"), userName, hostName); + if (freeHost && hostName != NULL) CFRelease(hostName); + return result; +} + +// It would be nice if we could remember the key for "well-known" combinations, so we're not constantly allocing more strings.... - REW 2/3/99 +static CFStringRef _CFPreferencesStandardDomainCacheKey(CFStringRef domainName, CFStringRef userName, CFStringRef hostName) { + CFStringRef prefix = _CFPreferencesCachePrefixForUserHost(userName, hostName); + CFStringRef result = NULL; + + if (prefix) { + result = CFStringCreateWithFormat(__CFPreferencesAllocator(), NULL, CFSTR("%@%@"), prefix, domainName); + CFRelease(prefix); + } + return result; +} + +#if defined(__MACOS8__) +// Define a custom hash function so that we don't inadvertantly make the +// result of CFHash() on a string persistent, and locked-in for all time. +static UInt16 hashString(CFStringRef str) { + UInt32 h = 0; + CFIndex idx, cnt; + cnt = CFStringGetLength(str); + h = cnt; + for (idx = 0; idx < cnt; idx++) { + h <<= 2; + h += CFStringGetCharacterAtIndex(str, idx); + } + return (h >> 16) ^ (h & 0xFFFF); +} +#endif + +static CFURLRef _CFPreferencesURLForStandardDomainWithSafetyLevel(CFStringRef domainName, CFStringRef userName, CFStringRef hostName, unsigned long safeLevel) { + CFURLRef theURL = NULL; + CFAllocatorRef prefAlloc = __CFPreferencesAllocator(); +#if defined(__MACH__) + CFURLRef prefDir = _preferencesDirectoryForUserHostSafetyLevel(userName, hostName, safeLevel); + CFStringRef appName; + CFStringRef fileName; + Boolean mustFreeAppName = false; + + if (!prefDir) return NULL; + if (domainName == kCFPreferencesAnyApplication) { + appName = CFSTR(".GlobalPreferences"); + } else if (domainName == kCFPreferencesCurrentApplication) { + CFBundleRef mainBundle = CFBundleGetMainBundle(); + appName = mainBundle ? CFBundleGetIdentifier(mainBundle) : NULL; + if (!appName || CFStringGetLength(appName) == 0) { + appName = _CFProcessNameString(); + } + } else { + appName = domainName; + } + if (userName != kCFPreferencesAnyUser) { + if (hostName == kCFPreferencesAnyHost) { + fileName = CFStringCreateWithFormat(prefAlloc, NULL, CFSTR("%@.plist"), appName); + } else if (hostName == kCFPreferencesCurrentHost) { + CFStringRef host = __CFCopyEthernetAddrString(); + if (!host) host = _CFStringCreateHostName(); + fileName = CFStringCreateWithFormat(prefAlloc, NULL, CFSTR("%@.%@.plist"), appName, host); + CFRelease(host); + } else { + fileName = CFStringCreateWithFormat(prefAlloc, NULL, CFSTR("%@.%@.plist"), appName, hostName); + } + } else { + fileName = CFStringCreateWithFormat(prefAlloc, NULL, CFSTR("%@.plist"), appName); + } + if (mustFreeAppName) { + CFRelease(appName); + } + if (fileName) { + theURL = CFURLCreateWithFileSystemPathRelativeToBase(prefAlloc, fileName, kCFURLPOSIXPathStyle, false, prefDir); + if (prefDir) CFRelease(prefDir); + CFRelease(fileName); + } +#else +#error Do not know where to store NSUserDefaults on this platform +#endif + return theURL; +} + +static CFURLRef _CFPreferencesURLForStandardDomain(CFStringRef domainName, CFStringRef userName, CFStringRef hostName) { + return _CFPreferencesURLForStandardDomainWithSafetyLevel(domainName, userName, hostName, __CFSafeLaunchLevel); +} + +CFPreferencesDomainRef _CFPreferencesStandardDomain(CFStringRef domainName, CFStringRef userName, CFStringRef hostName) { + CFPreferencesDomainRef domain; + CFStringRef domainKey; + Boolean shouldReleaseDomain = true; + domainKey = _CFPreferencesStandardDomainCacheKey(domainName, userName, hostName); + __CFSpinLock(&domainCacheLock); + if (!domainCache) { + CFAllocatorRef alloc = __CFPreferencesAllocator(); + domainCache = CFDictionaryCreateMutable(alloc, 0, & kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + } + domain = (CFPreferencesDomainRef)CFDictionaryGetValue(domainCache, domainKey); + __CFSpinUnlock(&domainCacheLock); + if (!domain) { + // Domain's not in the cache; load from permanent storage + CFURLRef theURL = _CFPreferencesURLForStandardDomain(domainName, userName, hostName); + if (theURL) { + domain = _CFPreferencesDomainCreate(theURL, &__kCFXMLPropertyListDomainCallBacks); + if (userName == kCFPreferencesAnyUser) { + _CFPreferencesDomainSetIsWorldReadable(domain, true); + } + CFRelease(theURL); + } + __CFSpinLock(&domainCacheLock); + if (domain && domainCache) { + // We've just synthesized a domain & we're about to throw it in the domain cache. The problem is that someone else might have gotten in here behind our backs, so we can't just blindly set the domain (3021920). We'll need to check to see if this happened, and compensate. + CFPreferencesDomainRef checkDomain = (CFPreferencesDomainRef)CFDictionaryGetValue(domainCache, domainKey); + if(checkDomain) { + // Someone got in here ahead of us, so we shouldn't smash the domain we're given. checkDomain is the current version, we should use that. + // checkDomain was retrieved with a Get, so we don't want to over-release. + shouldReleaseDomain = false; + CFRelease(domain); // release the domain we synthesized earlier. + domain = checkDomain; // repoint it at the domain picked up out of the cache. + } else { + // We must not have found the domain in the cache, so it's ok for us to put this in. + CFDictionarySetValue(domainCache, domainKey, domain); + } + if(shouldReleaseDomain) CFRelease(domain); + } + __CFSpinUnlock(&domainCacheLock); + } + CFRelease(domainKey); + return domain; +} + +static void __CFPreferencesPerformSynchronize(const void *key, const void *value, void *context) { + CFPreferencesDomainRef domain = (CFPreferencesDomainRef)value; + Boolean *cumulativeResult = (Boolean *)context; + if (!_CFPreferencesDomainSynchronize(domain)) *cumulativeResult = false; +} + +__private_extern__ Boolean _CFSynchronizeDomainCache(void) { + Boolean result = true; + __CFSpinLock(&domainCacheLock); + if (domainCache) { + CFDictionaryApplyFunction(domainCache, __CFPreferencesPerformSynchronize, &result); + } + __CFSpinUnlock(&domainCacheLock); + return result; +} + +__private_extern__ void _CFPreferencesPurgeDomainCache(void) { + _CFSynchronizeDomainCache(); + __CFSpinLock(&domainCacheLock); + if (domainCache) { + CFRelease(domainCache); + domainCache = NULL; + } + __CFSpinUnlock(&domainCacheLock); +} + +__private_extern__ CFArrayRef _CFPreferencesCreateDomainList(CFStringRef userName, CFStringRef hostName) { +#if 0 && defined(__WIN32__) + DWORD idx, numSubkeys, maxSubKey, cnt; + CFMutableArrayRef retVal; + LONG result; + id *list, buffer[512]; + result = RegQueryInfoKeyA(_masterKey, NULL, NULL, NULL, &numSubkeys, &maxSubKey, NULL, NULL, NULL, NULL, NULL, NULL); + if (result != ERROR_SUCCESS) { + NSLog(@"%@: cannot query master key info; %d", _NSMethodExceptionProem(self, _cmd), result); + return [NSArray array]; + } + maxSubKey++; + list = (numSubkeys <= 512) ? buffer : NSZoneMalloc(NULL, numSubkeys * sizeof(void *)); + if (_useCStringDomains < 0) + _useCStringDomains = (NSWindows95OperatingSystem == [[NSProcessInfo processInfo] operatingSystem]); + if (_useCStringDomains) { + for (idx = 0, cnt = 0; idx < numSubkeys; idx++) { + char name[maxSubKey + 1]; + DWORD nameSize = maxSubKey; + if (RegEnumKeyExA(_masterKey, idx, name, &nameSize, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) + list[cnt++] = [NSString stringWithCString:name length:nameSize]; + } + } else { + for (idx = 0, cnt = 0; idx < numSubkeys; idx++) { + unichar name[maxSubKey + 1]; + DWORD nameSize = maxSubKey; + if (RegEnumKeyExW(_masterKey, idx, name, &nameSize, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) + list[cnt++] = [NSString stringWithCharacters:name length:nameSize]; + } + } + retVal = [NSArray arrayWithObjects:list count:cnt]; + if (list != buffer) NSZoneFree(NULL, list); + return retVal; +#elif defined(__MACH__) || defined(__svr4__) || defined(__hpux__) + CFAllocatorRef prefAlloc = __CFPreferencesAllocator(); + CFArrayRef domains; + CFMutableArrayRef marray; + CFStringRef *cachedDomainKeys; + CFPreferencesDomainRef *cachedDomains; + SInt32 idx, cnt; + CFStringRef suffix; + UInt32 suffixLen; + CFURLRef prefDir = _preferencesDirectoryForUserHost(userName, hostName); + + if (!prefDir) { + return NULL; + } + if (hostName == kCFPreferencesAnyHost) { + suffix = CFStringCreateWithCString(prefAlloc, ".plist", kCFStringEncodingASCII); + } else if (hostName == kCFPreferencesCurrentHost) { + CFStringRef host = __CFCopyEthernetAddrString(); + if (!host) host = _CFStringCreateHostName(); + suffix = CFStringCreateWithFormat(prefAlloc, NULL, CFSTR(".%@.plist"), host); + CFRelease(host); + } else { + suffix = CFStringCreateWithFormat(prefAlloc, NULL, CFSTR(".%@.plist"), hostName); + } + suffixLen = CFStringGetLength(suffix); + + domains = CFURLCreatePropertyFromResource(prefAlloc, prefDir, kCFURLFileDirectoryContents, NULL); + CFRelease(prefDir); + if (domains){ + marray = CFArrayCreateMutableCopy(prefAlloc, 0, domains); + CFRelease(domains); + } else { + marray = CFArrayCreateMutable(prefAlloc, 0, & kCFTypeArrayCallBacks); + } + for (idx = CFArrayGetCount(marray)-1; idx >= 0; idx --) { + CFURLRef url = CFArrayGetValueAtIndex(marray, idx); + CFStringRef string = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle); + if (!CFStringHasSuffix(string, suffix)) { + CFArrayRemoveValueAtIndex(marray, idx); + } else { + CFStringRef dom = CFStringCreateWithSubstring(prefAlloc, string, CFRangeMake(0, CFStringGetLength(string) - suffixLen)); + if (CFEqual(dom, CFSTR(".GlobalPreferences"))) { + CFArraySetValueAtIndex(marray, idx, kCFPreferencesAnyApplication); + } else { + CFArraySetValueAtIndex(marray, idx, dom); + } + CFRelease(dom); + } + CFRelease(string); + } + CFRelease(suffix); + + // Now add any domains added in the cache; delete any that have been deleted in the cache + __CFSpinLock(&domainCacheLock); + if (!domainCache) { + __CFSpinUnlock(&domainCacheLock); + return marray; + } + cnt = CFDictionaryGetCount(domainCache); + cachedDomainKeys = CFAllocatorAllocate(prefAlloc, 2 * cnt * sizeof(CFStringRef), 0); + cachedDomains = (CFPreferencesDomainRef *)(cachedDomainKeys + cnt); + CFDictionaryGetKeysAndValues(domainCache, (const void **)cachedDomainKeys, (const void **)cachedDomains); + __CFSpinUnlock(&domainCacheLock); + suffix = _CFPreferencesCachePrefixForUserHost(userName, hostName); + suffixLen = CFStringGetLength(suffix); + + for (idx = 0; idx < cnt; idx ++) { + CFStringRef domainKey = cachedDomainKeys[idx]; + CFPreferencesDomainRef domain = cachedDomains[idx]; + CFStringRef domainName; + CFIndex keyCount = 0; + + if (!CFStringHasPrefix(domainKey, suffix)) continue; + domainName = CFStringCreateWithSubstring(prefAlloc, domainKey, CFRangeMake(suffixLen, CFStringGetLength(domainKey) - suffixLen)); + if (CFEqual(domainName, CFSTR("*"))) { + CFRelease(domainName); + domainName = CFRetain(kCFPreferencesAnyApplication); + } else if (CFEqual(domainName, kCFPreferencesCurrentApplication)) { + CFRelease(domainName); + domainName = CFRetain(_CFProcessNameString()); + } + _CFPreferencesDomainGetKeysAndValues(kCFAllocatorNull, domain, NULL, &keyCount); + if (keyCount == 0) { + // Domain was deleted + SInt32 firstIndexOfValue = CFArrayGetFirstIndexOfValue(marray, CFRangeMake(0, CFArrayGetCount(marray)), domainName); + if (0 <= firstIndexOfValue) { + CFArrayRemoveValueAtIndex(marray, firstIndexOfValue); + } + } else if (!CFArrayContainsValue(marray, CFRangeMake(0, CFArrayGetCount(marray)), domainName)) { + CFArrayAppendValue(marray, domainName); + } + CFRelease(domainName); + } + CFRelease(suffix); + CFAllocatorDeallocate(prefAlloc, cachedDomainKeys); + return marray; +#else +#endif +} + +// +// CFPreferencesDomain functions +// + +CFPreferencesDomainRef _CFPreferencesDomainCreate(CFTypeRef context, const _CFPreferencesDomainCallBacks *callBacks) { + CFAllocatorRef alloc = __CFPreferencesAllocator(); + CFPreferencesDomainRef newDomain; + CFAssert(callBacks != NULL && callBacks->createDomain != NULL && callBacks->freeDomain != NULL && callBacks->fetchValue != NULL && callBacks->writeValue != NULL, __kCFLogAssertion, "Cannot create a domain with NULL callbacks"); + newDomain = (CFPreferencesDomainRef)_CFRuntimeCreateInstance(alloc, __kCFPreferencesDomainTypeID, sizeof(struct __CFPreferencesDomain) - sizeof(CFRuntimeBase), NULL); + if (newDomain) { + newDomain->_callBacks = callBacks; + if (context) CFRetain(context); + newDomain->_context = context; + newDomain->_domain = callBacks->createDomain(alloc, context); + } + return newDomain; +} + +CFTypeRef _CFPreferencesDomainCreateValueForKey(CFPreferencesDomainRef domain, CFStringRef key) { + return domain->_callBacks->fetchValue(domain->_context, domain->_domain, key); +} + +void _CFPreferencesDomainSet(CFPreferencesDomainRef domain, CFStringRef key, CFTypeRef value) { + domain->_callBacks->writeValue(domain->_context, domain->_domain, key, value); +} + +__private_extern__ Boolean _CFPreferencesDomainSynchronize(CFPreferencesDomainRef domain) { + return domain->_callBacks->synchronize(domain->_context, domain->_domain); +} + +__private_extern__ void _CFPreferencesDomainGetKeysAndValues(CFAllocatorRef alloc, CFPreferencesDomainRef domain, void **buf[], CFIndex *numKeyValuePairs) { + domain->_callBacks->getKeysAndValues(alloc, domain->_context, domain->_domain, buf, numKeyValuePairs); +} + +__private_extern__ void _CFPreferencesDomainSetIsWorldReadable(CFPreferencesDomainRef domain, Boolean isWorldReadable) { + if (domain->_callBacks->setIsWorldReadable) { + domain->_callBacks->setIsWorldReadable(domain->_context, domain->_domain, isWorldReadable); + } +} + +void _CFPreferencesDomainSetDictionary(CFPreferencesDomainRef domain, CFDictionaryRef dict) { + CFTypeRef buf[32], *keys = buf; + CFIndex idx, count = 16; + CFAllocatorRef alloc = __CFPreferencesAllocator(); + + _CFPreferencesDomainGetKeysAndValues(kCFAllocatorNull, domain, (void ***)(&keys), &count); + if (count > 16) { + // Have to allocate + keys = NULL; + count = 0; + _CFPreferencesDomainGetKeysAndValues(alloc, domain, (void ***)(&keys), &count); + } + for (idx = 0; idx < count; idx ++) { + _CFPreferencesDomainSet(domain, (CFStringRef)keys[idx], NULL); + } + if (keys != buf) { + CFAllocatorDeallocate(alloc, keys); + } + + if (dict && (count = CFDictionaryGetCount(dict)) != 0) { + CFStringRef *newKeys = (count < 32) ? buf : CFAllocatorAllocate(alloc, count * sizeof(CFStringRef), 0); + CFDictionaryGetKeysAndValues(dict, (const void **)newKeys, NULL); + for (idx = 0; idx < count; idx ++) { + CFStringRef key = newKeys[idx]; + _CFPreferencesDomainSet(domain, key, (CFTypeRef)CFDictionaryGetValue(dict, key)); + } + if (((CFTypeRef)newKeys) != buf) { + CFAllocatorDeallocate(alloc, newKeys); + } + } +} + +CFDictionaryRef _CFPreferencesDomainCopyDictionary(CFPreferencesDomainRef domain) { + CFTypeRef *keys = NULL; + CFIndex count = 0; + CFAllocatorRef alloc = __CFPreferencesAllocator(); + CFDictionaryRef dict = NULL; + _CFPreferencesDomainGetKeysAndValues(alloc, domain, (void ***)&keys, &count); + if (count && keys) { + CFTypeRef *values = keys + count; + dict = CFDictionaryCreate(alloc, keys, values, count, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFAllocatorDeallocate(alloc, keys); + } + return dict; +} + +CFDictionaryRef _CFPreferencesDomainDeepCopyDictionary(CFPreferencesDomainRef domain) { + CFDictionaryRef result = domain->_callBacks->copyDomainDictionary(domain->_context, domain->_domain); + if(result && CFDictionaryGetCount(result) == 0) { + CFRelease(result); + result = NULL; + } + return result; +} + +Boolean _CFPreferencesDomainExists(CFStringRef domainName, CFStringRef userName, CFStringRef hostName) { + CFPreferencesDomainRef domain; + CFIndex count = 0; + domain = _CFPreferencesStandardDomain(domainName, userName, hostName); + if (domain) { + _CFPreferencesDomainGetKeysAndValues(kCFAllocatorNull, domain, NULL, &count); + return (count > 0); + } else { + return false; + } +} + +/* Volatile domains - context is ignored; domain is a CFDictionary (mutable) */ +static void *createVolatileDomain(CFAllocatorRef allocator, CFTypeRef context) { + return CFDictionaryCreateMutable(allocator, 0, & kCFTypeDictionaryKeyCallBacks, & kCFTypeDictionaryValueCallBacks); +} + +static void freeVolatileDomain(CFAllocatorRef allocator, CFTypeRef context, void *domain) { + CFRelease((CFTypeRef)domain); +} + +static CFTypeRef fetchVolatileValue(CFTypeRef context, void *domain, CFStringRef key) { + CFTypeRef result = CFDictionaryGetValue((CFMutableDictionaryRef )domain, key); + if (result) CFRetain(result); + return result; +} + +static void writeVolatileValue(CFTypeRef context, void *domain, CFStringRef key, CFTypeRef value) { + if (value) + CFDictionarySetValue((CFMutableDictionaryRef )domain, key, value); + else + CFDictionaryRemoveValue((CFMutableDictionaryRef )domain, key); +} + +static Boolean synchronizeVolatileDomain(CFTypeRef context, void *domain) { + return true; +} + +static void getVolatileKeysAndValues(CFAllocatorRef alloc, CFTypeRef context, void *domain, void **buf[], CFIndex *numKeyValuePairs) { + CFMutableDictionaryRef dict = (CFMutableDictionaryRef)domain; + CFIndex count = CFDictionaryGetCount(dict); + + if (buf) { + void **values; + if ( count < *numKeyValuePairs ) { + values = *buf + count; + CFDictionaryGetKeysAndValues(dict, (const void **)*buf, (const void **)values); + } else if (alloc != kCFAllocatorNull) { + if (*buf) { + *buf = CFAllocatorReallocate(alloc, *buf, count * 2 * sizeof(void *), 0); + } else { + *buf = CFAllocatorAllocate(alloc, count*2*sizeof(void *), 0); + } + if (*buf) { + values = *buf + count; + CFDictionaryGetKeysAndValues(dict, (const void **)*buf, (const void **)values); + } + } + } + *numKeyValuePairs = count; +} + +static CFDictionaryRef copyVolatileDomainDictionary(CFTypeRef context, void *volatileDomain) { + CFMutableDictionaryRef dict = (CFMutableDictionaryRef)volatileDomain; + + CFDictionaryRef result = (CFDictionaryRef)CFPropertyListCreateDeepCopy(__CFPreferencesAllocator(), dict, kCFPropertyListImmutable); + return result; +} + +const _CFPreferencesDomainCallBacks __kCFVolatileDomainCallBacks = {createVolatileDomain, freeVolatileDomain, fetchVolatileValue, writeVolatileValue, synchronizeVolatileDomain, getVolatileKeysAndValues, copyVolatileDomainDictionary, NULL}; + + diff --git a/Preferences.subproj/CFPreferences.h b/Preferences.subproj/CFPreferences.h new file mode 100644 index 0000000..c3b1915 --- /dev/null +++ b/Preferences.subproj/CFPreferences.h @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFPreferences.h + Copyright (c) 1998-2005, Apple, Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFPREFERENCES__) +#define __COREFOUNDATION_CFPREFERENCES__ 1 + +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +CF_EXPORT +const CFStringRef kCFPreferencesAnyApplication; +CF_EXPORT +const CFStringRef kCFPreferencesCurrentApplication; +CF_EXPORT +const CFStringRef kCFPreferencesAnyHost; +CF_EXPORT +const CFStringRef kCFPreferencesCurrentHost; +CF_EXPORT +const CFStringRef kCFPreferencesAnyUser; +CF_EXPORT +const CFStringRef kCFPreferencesCurrentUser; + +/* NOTE: All CFPropertyListRef values returned from + CFPreferences API should be assumed to be immutable. +*/ + +/* The "App" functions search the various sources of defaults that + apply to the given application, and should never be called with + kCFPreferencesAnyApplication - only kCFPreferencesCurrentApplication + or an application's ID (its bundle identifier). +*/ + +/* Searches the various sources of application defaults to find the +value for the given key. key must not be NULL. If a value is found, +it returns it; otherwise returns NULL. Caller must release the +returned value */ +CF_EXPORT +CFPropertyListRef CFPreferencesCopyAppValue(CFStringRef key, CFStringRef applicationID); + +/* Convenience to interpret a preferences value as a boolean directly. +Returns false if the key doesn't exist, or has an improper format; under +those conditions, keyExistsAndHasValidFormat (if non-NULL) is set to false */ +CF_EXPORT +Boolean CFPreferencesGetAppBooleanValue(CFStringRef key, CFStringRef applicationID, Boolean *keyExistsAndHasValidFormat); + +/* Convenience to interpret a preferences value as an integer directly. +Returns 0 if the key doesn't exist, or has an improper format; under +those conditions, keyExistsAndHasValidFormat (if non-NULL) is set to false */ +CF_EXPORT +CFIndex CFPreferencesGetAppIntegerValue(CFStringRef key, CFStringRef applicationID, Boolean *keyExistsAndHasValidFormat); + +/* Sets the given value for the given key in the "normal" place for +application preferences. key must not be NULL. If value is NULL, +key is removed instead. */ +CF_EXPORT +void CFPreferencesSetAppValue(CFStringRef key, CFPropertyListRef value, CFStringRef applicationID); + +/* Adds the preferences for the given suite to the app preferences for + the specified application. To write to the suite domain, use + CFPreferencesSetValue(), below, using the suiteName in place + of the appName */ +CF_EXPORT +void CFPreferencesAddSuitePreferencesToApp(CFStringRef applicationID, CFStringRef suiteID); + +CF_EXPORT +void CFPreferencesRemoveSuitePreferencesFromApp(CFStringRef applicationID, CFStringRef suiteID); + +/* Writes all changes in all sources of application defaults. +Returns success or failure. */ +CF_EXPORT +Boolean CFPreferencesAppSynchronize(CFStringRef applicationID); + +/* The primitive get mechanism; all arguments must be non-NULL +(use the constants above for common values). Only the exact +location specified by app-user-host is searched. The returned +CFType must be released by the caller when it is finished with it. */ +CF_EXPORT +CFPropertyListRef CFPreferencesCopyValue(CFStringRef key, CFStringRef applicationID, CFStringRef userName, CFStringRef hostName); + +/* Convenience to fetch multiple keys at once. Keys in +keysToFetch that are not present in the returned dictionary +are not present in the domain. If keysToFetch is NULL, all +keys are fetched. */ +CF_EXPORT +CFDictionaryRef CFPreferencesCopyMultiple(CFArrayRef keysToFetch, CFStringRef applicationID, CFStringRef userName, CFStringRef hostName); + +/* The primitive set function; all arguments except value must be +non-NULL. If value is NULL, the given key is removed */ +CF_EXPORT +void CFPreferencesSetValue(CFStringRef key, CFPropertyListRef value, CFStringRef applicationID, CFStringRef userName, CFStringRef hostName); + +/* Convenience to set multiple values at once. Behavior is undefined +if a key is in both keysToSet and keysToRemove */ +CF_EXPORT +void CFPreferencesSetMultiple(CFDictionaryRef keysToSet, CFArrayRef keysToRemove, CFStringRef applicationID, CFStringRef userName, CFStringRef hostName); + +CF_EXPORT +Boolean CFPreferencesSynchronize(CFStringRef applicationID, CFStringRef userName, CFStringRef hostName); + +/* Constructs and returns the list of the name of all applications +which have preferences in the scope of the given user and host. +The returned value must be released by the caller; neither argument +may be NULL. */ +CF_EXPORT +CFArrayRef CFPreferencesCopyApplicationList(CFStringRef userName, CFStringRef hostName); + +/* Constructs and returns the list of all keys set in the given +location. The returned value must be released by the caller; +all arguments must be non-NULL */ +CF_EXPORT +CFArrayRef CFPreferencesCopyKeyList(CFStringRef applicationID, CFStringRef userName, CFStringRef hostName); + + +#if defined(__cplusplus) +} +#endif + +#endif /* ! __COREFOUNDATION_CFPREFERENCES__ */ + diff --git a/Preferences.subproj/CFXMLPreferencesDomain.c b/Preferences.subproj/CFXMLPreferencesDomain.c new file mode 100644 index 0000000..e259c44 --- /dev/null +++ b/Preferences.subproj/CFXMLPreferencesDomain.c @@ -0,0 +1,570 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFXMLPreferencesDomain.c + Copyright 1998-2002, Apple, Inc. All rights reserved. + Responsibility: Chris Parker +*/ + +#if !defined(__WIN32__) + +#include +#include +#include +#include +#include +#include "CFInternal.h" +#include +#include +#include +#include +#include +#include + +Boolean __CFPreferencesShouldWriteXML(void); + +typedef struct { + CFMutableDictionaryRef _domainDict; // Current value of the domain dictionary + CFMutableArrayRef _dirtyKeys; // The array of keys which must be synchronized + CFAbsoluteTime _lastReadTime; // The last time we synchronized with the disk + CFSpinLock_t _lock; // Lock for accessing fields in the domain + Boolean _isWorldReadable; // HACK - this is because we have no good way to propogate the kCFPreferencesAnyUser information from the upper level CFPreferences routines REW, 1/13/00 + char _padding[3]; +} _CFXMLPreferencesDomain; + +static void *createXMLDomain(CFAllocatorRef allocator, CFTypeRef context); +static void freeXMLDomain(CFAllocatorRef allocator, CFTypeRef context, void *tDomain); +static CFTypeRef fetchXMLValue(CFTypeRef context, void *xmlDomain, CFStringRef key); +static void writeXMLValue(CFTypeRef context, void *xmlDomain, CFStringRef key, CFTypeRef value); +static Boolean synchronizeXMLDomain(CFTypeRef context, void *xmlDomain); +static void getXMLKeysAndValues(CFAllocatorRef alloc, CFTypeRef context, void *xmlDomain, void **buf[], CFIndex *numKeyValuePairs); +static CFDictionaryRef copyXMLDomainDictionary(CFTypeRef context, void *domain); +static void setXMLDomainIsWorldReadable(CFTypeRef context, void *domain, Boolean isWorldReadable); + +__private_extern__ const _CFPreferencesDomainCallBacks __kCFXMLPropertyListDomainCallBacks = {createXMLDomain, freeXMLDomain, fetchXMLValue, writeXMLValue, synchronizeXMLDomain, getXMLKeysAndValues, copyXMLDomainDictionary, setXMLDomainIsWorldReadable}; + +// Directly ripped from Foundation.... +static void __CFMilliSleep(uint32_t msecs) { +#if defined(__WIN32__) + SleepEx(msecs, false); +#elif defined(__svr4__) || defined(__hpux__) + sleep((msecs + 900) / 1000); +#elif defined(__MACH__) + struct timespec input; + input.tv_sec = msecs / 1000; + input.tv_nsec = (msecs - input.tv_sec * 1000) * 1000000; + nanosleep(&input, NULL); +#else +#error Dont know how to define sleep for this platform +#endif +} + +static CFSpinLock_t _propDictLock = 0; // Annoying that we need this, but otherwise we have a multithreading risk + +CF_INLINE CFDictionaryRef URLPropertyDictForPOSIXMode(SInt32 mode) { + static CFMutableDictionaryRef _propertyDict = NULL; + CFNumberRef num = CFNumberCreate(__CFPreferencesAllocator(), kCFNumberSInt32Type, &mode); + __CFSpinLock(&_propDictLock); + if (!_propertyDict) { + _propertyDict = CFDictionaryCreateMutable(__CFPreferencesAllocator(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + } + CFDictionarySetValue(_propertyDict, kCFURLFilePOSIXMode, num); + CFRelease(num); + return _propertyDict; +} + +CF_INLINE void URLPropertyDictRelease(void) { + __CFSpinUnlock(&_propDictLock); +} + +// Asssumes caller already knows the directory doesn't exist. +static Boolean _createDirectory(CFURLRef dirURL, Boolean worldReadable) { + CFAllocatorRef alloc = __CFPreferencesAllocator(); + CFURLRef parentURL = CFURLCreateCopyDeletingLastPathComponent(alloc, dirURL); + CFBooleanRef val = CFURLCreatePropertyFromResource(alloc, parentURL, kCFURLFileExists, NULL); + Boolean parentExists = (val && CFBooleanGetValue(val)); + SInt32 mode; + Boolean result; + if (val) CFRelease(val); + if (!parentExists) { + CFStringRef path = CFURLCopyPath(parentURL); + if (!CFEqual(path, CFSTR("/"))) { + _createDirectory(parentURL, worldReadable); + val = CFURLCreatePropertyFromResource(alloc, parentURL, kCFURLFileExists, NULL); + parentExists = (val && CFBooleanGetValue(val)); + if (val) CFRelease(val); + } + CFRelease(path); + } + if (parentURL) CFRelease(parentURL); + if (!parentExists) return false; + + mode = worldReadable ? S_IRWXU|S_IRWXG|S_IROTH|S_IXOTH : S_IRWXU; + + result = CFURLWriteDataAndPropertiesToResource(dirURL, (CFDataRef)dirURL, URLPropertyDictForPOSIXMode(mode), NULL); + URLPropertyDictRelease(); + return result; +} + + +/* XML - context is the CFURL where the property list is stored on disk; domain is an _CFXMLPreferencesDomain */ +static void *createXMLDomain(CFAllocatorRef allocator, CFTypeRef context) { + _CFXMLPreferencesDomain *domain = CFAllocatorAllocate(allocator, sizeof(_CFXMLPreferencesDomain), 0); + domain->_lastReadTime = 0.0; + domain->_domainDict = NULL; + domain->_dirtyKeys = CFArrayCreateMutable(allocator, 0, & kCFTypeArrayCallBacks); + domain->_lock = 0; + domain->_isWorldReadable = false; + return domain; +} + +static void freeXMLDomain(CFAllocatorRef allocator, CFTypeRef context, void *tDomain) { + _CFXMLPreferencesDomain *domain = (_CFXMLPreferencesDomain *)tDomain; + if (domain->_domainDict) CFRelease(domain->_domainDict); + if (domain->_dirtyKeys) CFRelease(domain->_dirtyKeys); + CFAllocatorDeallocate(allocator, domain); +} + +// Assumes the domain has already been locked +static void _loadXMLDomainIfStale(CFURLRef url, _CFXMLPreferencesDomain *domain) { + CFAllocatorRef alloc = __CFPreferencesAllocator(); + int idx; + if (domain->_domainDict) { + CFDateRef modDate; + CFAbsoluteTime modTime; + CFURLRef testURL = url; + + if (CFDictionaryGetCount(domain->_domainDict) == 0) { + // domain never existed; check the parent directory, not the child + testURL = CFURLCreateWithFileSystemPathRelativeToBase(alloc, CFSTR(".."), kCFURLPOSIXPathStyle, true, url); + } + + modDate = (CFDateRef )CFURLCreatePropertyFromResource(alloc, testURL, kCFURLFileLastModificationTime, NULL); + modTime = modDate ? CFDateGetAbsoluteTime(modDate) : 0.0; + + // free before possible return. we can test non-NULL of modDate but don't depend on contents after this. + if (testURL != url) CFRelease(testURL); + if (modDate) CFRelease(modDate); + + if (modDate != NULL && modTime < domain->_lastReadTime) { // We're up-to-date + return; + } + } + + + // We're out-of-date; destroy domainDict and reload + if (domain->_domainDict) { + CFRelease(domain->_domainDict); + domain->_domainDict = NULL; + } + + // We no longer lock on read; instead, we assume parse failures are because someone else is writing the file, and just try to parse again. If we fail 3 times in a row, we assume the file is corrupted. REW, 7/13/99 + + for (idx = 0; idx < 3; idx ++) { + CFDataRef data; + if (!CFURLCreateDataAndPropertiesFromResource(alloc, url, &data, NULL, NULL, NULL) || !data) { + // Either a file system error (so we can't read the file), or an empty (or perhaps non-existant) file + domain->_domainDict = CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + break; + } else { + CFTypeRef pList = CFPropertyListCreateFromXMLData(alloc, data, kCFPropertyListImmutable, NULL); + CFRelease(data); + if (pList && CFGetTypeID(pList) == CFDictionaryGetTypeID()) { + domain->_domainDict = CFDictionaryCreateMutableCopy(alloc, 0, (CFDictionaryRef)pList); + CFRelease(pList); + break; + } else if (pList) { + CFRelease(pList); + } + // Assume the file is being written; sleep for a short time (to allow the write to complete) then re-read + __CFMilliSleep(150); + } + } + if (!domain->_domainDict) { + // Failed to ever load + domain->_domainDict = CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + } + domain->_lastReadTime = CFAbsoluteTimeGetCurrent(); +} + +static CFTypeRef fetchXMLValue(CFTypeRef context, void *xmlDomain, CFStringRef key) { + _CFXMLPreferencesDomain *domain = (_CFXMLPreferencesDomain *)xmlDomain; + CFTypeRef result; + + // Never reload if we've looked at the file system within the last 5 seconds. + __CFSpinLock(&domain->_lock); + if (domain->_domainDict == NULL) _loadXMLDomainIfStale((CFURLRef )context, domain); + result = CFDictionaryGetValue(domain->_domainDict, key); + if (result) CFRetain(result); + __CFSpinUnlock(&domain->_lock); + + return result; +} + + +#if defined(__MACH__) +#include +#if 0 +// appends a unique 8.3 path name to the directory name specified in fn, +// atomically determining uniqueness and opening the file. The file +// descriptor is returned by reference in the second parameter. 0 is +// returned on success, -1 on failure. +// We don't currently handle the case where the directory name is very +// long and adding an 8.3 name makes the path too long. +static int __CFmkstemp83(char *fn, char *prefix, int mode, int *fd) { + static CFSpinLock_t counter_lock = 0; + static unsigned int extension_counter = 0; + int origlen = strlen(fn); + char idbuf[6], extbuf[6], prebuf[5]; + uint16_t pid, origpid, ext, origext; + + __CFSpinLock(&counter_lock); + ext = extension_counter++; + if (0xFFF < extension_counter) extension_counter = 0; + __CFSpinUnlock(&counter_lock); + origext = ext; + do { + char *s1 = prebuf; + const char *s2 = prefix; + int n = 0; + for (; (*s1 = *s2) && (n < 4); s1++, s2++, n++); + } while (0); + prebuf[4] = '\0'; + if (0 < origlen && fn[origlen - 1] != '/') + fn[origlen++] = '/'; + pid = getpid() & 0xFFFF; + origpid = pid; + snprintf(idbuf, 6, "%04x", pid); + snprintf(extbuf, 6, ".%03x", ext); + fn[origlen] = '\0'; + strcat(fn, prebuf); + strcat(fn, idbuf); + strcat(fn, extbuf); + for (;;) { + *fd = open(fn, O_CREAT|O_EXCL|O_RDWR, mode); + if (0 <= *fd) + return 0; + if (EEXIST != thread_errno()) + return -1; + ext = (ext + 1) & 0xFFF; + if (origext == ext) { + // bump the number and start over with extension + pid = (pid + 1) & 0xFFFF; + if (pid == origpid) + return -1; // 2^28 file names tried! errno == EEXIST + snprintf(idbuf, 6, "%04x", pid); + } + snprintf(extbuf, 6, ".%03x", ext); + fn[origlen] = '\0'; + strcat(fn, prebuf); + strcat(fn, idbuf); + strcat(fn, extbuf); + } + return -1; +} +#endif + +/* __CFWriteBytesToFileWithAtomicity is a "safe save" facility. Write the bytes using the specified mode on the file to the provided URL. If the atomic flag is true, try to do it in a fashion that will enable a safe save. + */ +static Boolean __CFWriteBytesToFileWithAtomicity(CFURLRef url, const void *bytes, int length, SInt32 mode, Boolean atomic) { + int fd = -1; + char auxPath[CFMaxPathSize + 16]; + char cpath[CFMaxPathSize]; + int fsyncErr = 0; + + if (!CFURLGetFileSystemRepresentation(url, true, cpath, CFMaxPathSize)) { + return false; + } + if (-1 == mode) { + struct stat statBuf; + mode = (0 == stat(cpath, &statBuf)) ? statBuf.st_mode : 0600; + } + if (atomic) { + CFURLRef dir = CFURLCreateCopyDeletingLastPathComponent(NULL, url); + CFURLRef tempFile = CFURLCreateCopyAppendingPathComponent(NULL, dir, CFSTR("cf#XXXXX"), false); + CFRelease(dir); + if (!CFURLGetFileSystemRepresentation(tempFile, true, auxPath, CFMaxPathSize)) { + CFRelease(tempFile); + return false; + } + CFRelease(tempFile); + fd = mkstemp(auxPath); + } else { + fd = open(cpath, O_WRONLY|O_CREAT|O_TRUNC, mode); + } + if (fd < 0) return false; + if (length && (write(fd, bytes, length) != length || fsync(fd) < 0)) { + int saveerr = thread_errno(); + close(fd); + if (atomic) + unlink(auxPath); + thread_set_errno(saveerr); + return false; + } + close(fd); + if (atomic) { + // preserve the mode as passed in originally + chmod(auxPath, mode); + + if (0 != rename(auxPath, cpath)) { + unlink(auxPath); + return false; + } + } + return true; +} +#endif + +// domain should already be locked. +static Boolean _writeXMLFile(CFURLRef url, CFMutableDictionaryRef dict, Boolean isWorldReadable, Boolean *tryAgain) { + Boolean success = false; + CFAllocatorRef alloc = __CFPreferencesAllocator(); + *tryAgain = false; + if (CFDictionaryGetCount(dict) == 0) { + // Destroy the file + CFBooleanRef val = CFURLCreatePropertyFromResource(alloc, url, kCFURLFileExists, NULL); + if (val && CFBooleanGetValue(val)) { + success = CFURLDestroyResource(url, NULL); + } else { + success = true; + } + if (val) CFRelease(val); + } else { + CFPropertyListFormat desiredFormat = __CFPreferencesShouldWriteXML() ? kCFPropertyListXMLFormat_v1_0 : kCFPropertyListBinaryFormat_v1_0; + CFWriteStreamRef binStream = CFWriteStreamCreateWithAllocatedBuffers(alloc, alloc); + CFWriteStreamOpen(binStream); + CFPropertyListWriteToStream(dict, binStream, desiredFormat, NULL); + CFWriteStreamClose(binStream); + CFDataRef data = CFWriteStreamCopyProperty(binStream, kCFStreamPropertyDataWritten); + CFRelease(binStream); + if (data) { + SInt32 mode; + mode = isWorldReadable ? S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH : S_IRUSR|S_IWUSR; +#if 1 && defined(__MACH__) + { // Try quick atomic way first, then fallback to slower ways and error cases + CFStringRef scheme = CFURLCopyScheme(url); + if (!scheme) { + *tryAgain = false; + CFRelease(data); + return false; + } else if (CFStringCompare(scheme, CFSTR("file"), 0) == kCFCompareEqualTo) { + SInt32 length = CFDataGetLength(data); + const void *bytes = (0 == length) ? (const void *)"" : CFDataGetBytePtr(data); + Boolean atomicWriteSuccess = __CFWriteBytesToFileWithAtomicity(url, bytes, length, mode, true); + if (atomicWriteSuccess) { + CFRelease(scheme); + *tryAgain = false; + CFRelease(data); + return true; + } + if (!atomicWriteSuccess && thread_errno() == ENOSPC) { + CFRelease(scheme); + *tryAgain = false; + CFRelease(data); + return false; + } + } + CFRelease(scheme); + } +#endif + success = CFURLWriteDataAndPropertiesToResource(url, data, URLPropertyDictForPOSIXMode(mode), NULL); + URLPropertyDictRelease(); + if (success) { + CFDataRef readData; + if (!CFURLCreateDataAndPropertiesFromResource(alloc, url, &readData, NULL, NULL, NULL) || !CFEqual(readData, data)) { + success = false; + *tryAgain = true; + } + if (readData) CFRelease(readData); + } else { + CFBooleanRef val = CFURLCreatePropertyFromResource(alloc, url, kCFURLFileExists, NULL); + if (!val || !CFBooleanGetValue(val)) { + CFURLRef tmpURL = CFURLCreateWithFileSystemPathRelativeToBase(alloc, CFSTR("."), kCFURLPOSIXPathStyle, true, url); // Just "." because url is not a directory URL + CFURLRef parentURL = tmpURL ? CFURLCopyAbsoluteURL(tmpURL) : NULL; + if (tmpURL) CFRelease(tmpURL); + if (val) CFRelease(val); + val = CFURLCreatePropertyFromResource(alloc, parentURL, kCFURLFileExists, NULL); + if ((!val || !CFBooleanGetValue(val)) && _createDirectory(parentURL, isWorldReadable)) { + // parent directory didn't exist; now it does; try again to write + success = CFURLWriteDataAndPropertiesToResource(url, data, URLPropertyDictForPOSIXMode(mode), NULL); + URLPropertyDictRelease(); + if (success) { + CFDataRef rdData; + if (!CFURLCreateDataAndPropertiesFromResource(alloc, url, &rdData, NULL, NULL, NULL) || !CFEqual(rdData, data)) { + success = false; + *tryAgain = true; + } + if (rdData) CFRelease(rdData); + } + + } + if (parentURL) CFRelease(parentURL); + } + if (val) CFRelease(val); + } + CFRelease(data); + } else { + // ??? This should never happen + CFLog(__kCFLogAssertion, CFSTR("Could not generate XML data for property list")); + success = false; + } + } + return success; +} + +static void writeXMLValue(CFTypeRef context, void *xmlDomain, CFStringRef key, CFTypeRef value) { + _CFXMLPreferencesDomain *domain = (_CFXMLPreferencesDomain *)xmlDomain; + const void *existing = NULL; + + __CFSpinLock(&domain->_lock); + if (domain->_domainDict == NULL) { + _loadXMLDomainIfStale((CFURLRef )context, domain); + } + + // check to see if the value is the same + // if (1) the key is present AND value is !NULL and equal to existing, do nothing, or + // if (2) the key is not present AND value is NULL, do nothing + // these things are no-ops, and should not dirty the domain + if (CFDictionaryGetValueIfPresent(domain->_domainDict, key, &existing)) { + if (NULL != value && (existing == value || CFEqual(existing, value))) { + __CFSpinUnlock(&domain->_lock); + return; + } + } else { + if (NULL == value) { + __CFSpinUnlock(&domain->_lock); + return; + } + } + + // We must append first so key gets another retain (in case we're + // about to remove it from the dictionary, and that's the sole reference) + // This should be a set not an array. + if (!CFArrayContainsValue(domain->_dirtyKeys, CFRangeMake(0, CFArrayGetCount(domain->_dirtyKeys)), key)) { + CFArrayAppendValue(domain->_dirtyKeys, key); + } + if (value) { + // Must copy for two reasons - we don't want mutable objects in the cache, and we don't want objects allocated from a different allocator in the cache. + CFTypeRef newValue = CFPropertyListCreateDeepCopy(__CFPreferencesAllocator(), value, kCFPropertyListImmutable); + CFDictionarySetValue(domain->_domainDict, key, newValue); + CFRelease(newValue); + } else { + CFDictionaryRemoveValue(domain->_domainDict, key); + } + __CFSpinUnlock(&domain->_lock); +} + +static void getXMLKeysAndValues(CFAllocatorRef alloc, CFTypeRef context, void *xmlDomain, void **buf[], CFIndex *numKeyValuePairs) { + _CFXMLPreferencesDomain *domain = (_CFXMLPreferencesDomain *)xmlDomain; + CFIndex count; + __CFSpinLock(&domain->_lock); + if (!domain->_domainDict) { + _loadXMLDomainIfStale((CFURLRef )context, domain); + } + count = CFDictionaryGetCount(domain->_domainDict); + if (buf) { + void **values; + if (count <= *numKeyValuePairs) { + values = *buf + count; + CFDictionaryGetKeysAndValues(domain->_domainDict, (const void **)*buf, (const void **)values); + } else if (alloc != kCFAllocatorNull) { + *buf = CFAllocatorReallocate(alloc, (*buf ? *buf : NULL), count * 2 * sizeof(void *), 0); + if (*buf) { + values = *buf + count; + CFDictionaryGetKeysAndValues(domain->_domainDict, (const void **)*buf, (const void **)values); + } + } + } + *numKeyValuePairs = count; + __CFSpinUnlock(&domain->_lock); +} + +static CFDictionaryRef copyXMLDomainDictionary(CFTypeRef context, void *xmlDomain) { + _CFXMLPreferencesDomain *domain = (_CFXMLPreferencesDomain *)xmlDomain; + CFDictionaryRef result; + + __CFSpinLock(&domain->_lock); + if(!domain->_domainDict) { + _loadXMLDomainIfStale((CFURLRef)context, domain); + } + + result = (CFDictionaryRef)CFPropertyListCreateDeepCopy(__CFPreferencesAllocator(), domain->_domainDict, kCFPropertyListImmutable); + + __CFSpinUnlock(&domain->_lock); + return result; +} + + +static void setXMLDomainIsWorldReadable(CFTypeRef context, void *domain, Boolean isWorldReadable) { + ((_CFXMLPreferencesDomain *)domain)->_isWorldReadable = isWorldReadable; +} + +static Boolean synchronizeXMLDomain(CFTypeRef context, void *xmlDomain) { + _CFXMLPreferencesDomain *domain = (_CFXMLPreferencesDomain *)xmlDomain; + CFMutableDictionaryRef cachedDict; + CFMutableArrayRef changedKeys; + SInt32 idx, count; + Boolean success, tryAgain; + + __CFSpinLock(&domain->_lock); + cachedDict = domain->_domainDict; + changedKeys = domain->_dirtyKeys; + count = CFArrayGetCount(changedKeys); + + if (count == 0) { + // no changes were made to this domain; just remove it from the cache to guarantee it will be taken from disk next access + if (cachedDict) { + CFRelease(cachedDict); + domain->_domainDict = NULL; + } + __CFSpinUnlock(&domain->_lock); + return true; + } + + domain->_domainDict = NULL; // This forces a reload. Note that we now have a retain on cachedDict + do { + _loadXMLDomainIfStale((CFURLRef )context, domain); + // now cachedDict holds our changes; domain->_domainDict has the latest version from the disk + for (idx = 0; idx < count; idx ++) { + CFStringRef key = CFArrayGetValueAtIndex(changedKeys, idx); + CFTypeRef value = CFDictionaryGetValue(cachedDict, key); + if (value) + CFDictionarySetValue(domain->_domainDict, key, value); + else + CFDictionaryRemoveValue(domain->_domainDict, key); + } + success = _writeXMLFile((CFURLRef )context, domain->_domainDict, domain->_isWorldReadable, &tryAgain); + if (tryAgain) { + __CFMilliSleep(((__CFReadTSR() & 0xf) + 1) * 50); + } + } while (tryAgain); + CFRelease(cachedDict); + if (success) { + CFArrayRemoveAllValues(domain->_dirtyKeys); + } + domain->_lastReadTime = CFAbsoluteTimeGetCurrent(); + __CFSpinUnlock(&domain->_lock); + return success; +} + +#endif /* !defined(__WIN32__) */ + diff --git a/README b/README new file mode 100644 index 0000000..8abe9a8 --- /dev/null +++ b/README @@ -0,0 +1,94 @@ +This is the CoreFoundation framework project for Darwin, +sometimes also known as "CF-lite", because it does not +contain every facility available from the CoreFoundation +framework in Mac OS X. + +The purpose of this README file is to share "what needs +doing", "how to do things", and Q&A information about +CF-lite, as this information is discovered. + +--- What Apple is NOT interested in, with CF-lite: +* Everybody's little convenience methods. Just because + "everybody has to write their own", it does not + follow immediately that it is a good idea to add it + to the system libraries. It is not a goal of CF to + be a "Swiss Army Knife"; that just increases the + size of the binaries, and the documentation and + maintenance burden. Functions to reverse a string + or merge two arrays by taking alternate elements + from the two arrays are not compelling. + + +--- Major changes since March 2000: +* Partial port to Linux +* CF runtime refactored into CFRuntime.[hc], changed how + types are registered/known to the runtime +* CFMachPort and CFMessagePort now public API. +* Most private symbols now are flagged with + __private_extern__ so they aren't exported from + the binary. +* Partial port to FreeBSD. +* CFBundle, CFPlugIn APIs added to Darwin. + + +--- CF-lite to-do list: +[Note: when it says "Apple has code" below, that usually +means "Apple has some code it could provide to start an +effort here", not "Apple has some code in the pipe, don't +bother with this item". Anyone known to be doing work on +any of these items will be listed here, including Apple.] +* Some classes have a fair number of assertions, nearly all + related to parameter checking. More assertions are needed + nearly everywhere. The assertions that are there have been + often found to be valuable -- you just get a message about + some bad parameter and there's the bug. +* More header doc is needed. CFArray.h and CFDictionary.h + are models. +* An exception model, similar to Cocoa Foundation's. Apple + has some code for this already, and try/catch model like + C++ is simple enough to support; finally blocks a la Java + don't seem to be practical within the confines of ANSI C. +* A CFFileDescriptor is needed which can act as a run loop + source. Or maybe it should be CFPipeDescriptor. This is + NOT something for general file handling -- just monitoring + a file descriptor which is a pipe (for which there are + notifications or other async activity). + + +Contributors +------------ +March 2000: partial port to Linux by Pedro Ivo Tavares (ptavares@iname.com). + This was mostly integrated by ckane@apple.com by CoreFoundation-5, + but the style was changed, and the hand-crafted Makefile, which is + new since this port, tries to take care of some of the things that + are needed for Linux (and build a libCoreFoundation.a instead of a + CoreFoundation.framework), but ckane does not have a Linux box and + can't finish that and test building. + +December 2000: port to FreeBSD by Sarwat Khan (sarwat@sarwat.net). + Port to FreeBSD, except for RunLoop.subproj items. Integrated by + ckane@apple.com into CoreFoundation-8. ckane also marginally + improved the Linux port in that version. + +March 2001: CoreFoundation-9, corresponding to CoreFoundation-197 in + Mac OS X, synchronizes Darwin CF with Mac OS X's released CF. + +May 2001: CoreFoundation-10, corresponding to CoreFoundation-206.5 + synchronizes Darwin CF with TOT CoreFoundation. + +June 2001: Linux port maintenance by Robert Thompson . + +July 2001: Integrated addition of headerdoc for CFBinaryHeap.h from + Kevin Van Vechten . + +Sept 2001: CoreFoundation-14, sync with Mac OS X 10.1 + +Oct 2001: More header doc (e.g., CFSet.h) and some CFTree implementation + from Kevin Van Vechten . + +Jan 2002: Windows(TM) port changes from Kevin Van Vechten . + +Feb 2002: Some Windows(TM) port changes from Aleskey Dukhnyakov, Andrew Dzubandovsky, + Roman Mukhin, and Sergey Zubarev; Orc Software + + diff --git a/RunLoop.subproj/CFMachPort.c b/RunLoop.subproj/CFMachPort.c index 1353b1e..9cf96d8 100644 --- a/RunLoop.subproj/CFMachPort.c +++ b/RunLoop.subproj/CFMachPort.c @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -306,13 +304,13 @@ static void __CFNotifyDeadMachPort(CFMachPortRef port, void *msg, CFIndex size, static Boolean __CFMachPortEqual(CFTypeRef cf1, CFTypeRef cf2) { CFMachPortRef mp1 = (CFMachPortRef)cf1; CFMachPortRef mp2 = (CFMachPortRef)cf2; - __CFMachPortCheckForFork(); +// __CFMachPortCheckForFork(); do not do this here return (mp1->_port == mp2->_port); } static CFHashCode __CFMachPortHash(CFTypeRef cf) { CFMachPortRef mp = (CFMachPortRef)cf; - __CFMachPortCheckForFork(); +// __CFMachPortCheckForFork(); do not do this here -- can cause strange reentrancies 3843642 return (CFHashCode)mp->_port; } @@ -455,11 +453,11 @@ CFMachPortRef CFMachPortCreateWithPort(CFAllocatorRef allocator, mach_port_t por __CFMachPortSetValid(memory); memory->_callout = callout; if (NULL != context) { - memmove(&memory->_context, context, sizeof(CFMachPortContext)); + CF_WRITE_BARRIER_MEMMOVE(&memory->_context, context, sizeof(CFMachPortContext)); memory->_context.info = context->retain ? (void *)context->retain(context->info) : context->info; } if (NULL == __CFAllMachPorts) { - __CFAllMachPorts = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, NULL); + __CFAllMachPorts = CFDictionaryCreateMutable(kCFAllocatorMallocZone, 0, NULL, NULL); // XXX_PCB make it GC weak. _CFDictionarySetCapacity(__CFAllMachPorts, 20); } CFDictionaryAddValue(__CFAllMachPorts, (void *)port, memory); @@ -496,7 +494,7 @@ 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)); + CF_WRITE_BARRIER_MEMMOVE(context, &mp->_context, sizeof(CFMachPortContext)); } void CFMachPortInvalidate(CFMachPortRef mp) { @@ -592,6 +590,7 @@ void CFMachPortInvalidateAll(void) { // it was a very bad idea to call it. } + static mach_port_t __CFMachPortGetPort(void *info) { CFMachPortRef mp = info; __CFMachPortCheckForFork(); @@ -628,6 +627,10 @@ CFRunLoopSourceRef CFMachPortCreateRunLoopSource(CFAllocatorRef allocator, CFMac __CFGenericValidateType(mp, __kCFMachPortTypeID); __CFMachPortCheckForFork(); __CFMachPortLock(mp); + if (!__CFMachPortIsValid(mp)) { + __CFMachPortUnlock(mp); + return NULL; + } #if 0 #warning CF: adding ref to receive right is disabled for now -- doesnt work in 1F if (!__CFMachPortHasReceive(mp)) { diff --git a/RunLoop.subproj/CFMachPort.h b/RunLoop.subproj/CFMachPort.h index 510e408..35c1c27 100644 --- a/RunLoop.subproj/CFMachPort.h +++ b/RunLoop.subproj/CFMachPort.h @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -23,7 +21,7 @@ * @APPLE_LICENSE_HEADER_END@ */ /* CFMachPort.h - Copyright (c) 1998-2003, Apple, Inc. All rights reserved. + Copyright (c) 1998-2005, Apple, Inc. All rights reserved. */ #if !defined(__COREFOUNDATION_CFMACHPORT__) diff --git a/RunLoop.subproj/CFMessagePort.c b/RunLoop.subproj/CFMessagePort.c index 5112eed..2ca7e96 100644 --- a/RunLoop.subproj/CFMessagePort.c +++ b/RunLoop.subproj/CFMessagePort.c @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -183,6 +181,7 @@ static Boolean __CFMessagePortNativeSetNameLocal(CFMachPortRef port, uint8_t *po kern_return_t ret; task_get_bootstrap_port(mach_task_self(), &bp); ret = bootstrap_register(bp, portname, CFMachPortGetPort(port)); +if (ret != KERN_SUCCESS) CFLog(0, CFSTR("CFMessagePort: bootstrap_register(): failed %d (0x%x), port = 0x%x, name = '%s'\nSee /usr/include/servers/bootstrap_defs.h for the error codes."), ret, ret, CFMachPortGetPort(port), portname); return (ret == KERN_SUCCESS) ? true : false; } @@ -321,6 +320,7 @@ CFMessagePortRef CFMessagePortCreateLocal(CFAllocatorRef allocator, CFStringRef CFRelease(name); } CFAllocatorDeallocate(allocator, utfname); +CFLog(99, CFSTR("CFMessagePortCreateLocal(): failed to allocate object")); return NULL; } __CFMessagePortUnsetValid(memory); @@ -344,7 +344,9 @@ CFMessagePortRef CFMessagePortCreateLocal(CFAllocatorRef allocator, CFStringRef ctx.release = NULL; ctx.copyDescription = NULL; native = CFMachPortCreate(allocator, __CFMessagePortDummyCallback, &ctx, NULL); +if (!native) CFLog(99, CFSTR("CFMessagePortCreateLocal(): failed to allocate CFMachPortRef")); if (NULL != native && NULL != name && !__CFMessagePortNativeSetNameLocal(native, utfname)) { +CFLog(99, CFSTR("CFMessagePortCreateLocal(): failed to name Mach port (%@)"), name); CFMachPortInvalidate(native); CFRelease(native); native = NULL; diff --git a/RunLoop.subproj/CFMessagePort.h b/RunLoop.subproj/CFMessagePort.h index 51ce252..2be5fa6 100644 --- a/RunLoop.subproj/CFMessagePort.h +++ b/RunLoop.subproj/CFMessagePort.h @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -23,7 +21,7 @@ * @APPLE_LICENSE_HEADER_END@ */ /* CFMessagePort.h - Copyright (c) 1998-2003, Apple, Inc. All rights reserved. + Copyright (c) 1998-2005, Apple, Inc. All rights reserved. */ #if !defined(__COREFOUNDATION_CFMESSAGEPORT__) diff --git a/RunLoop.subproj/CFRunLoop.c b/RunLoop.subproj/CFRunLoop.c index ec5a976..e960db1 100644 --- a/RunLoop.subproj/CFRunLoop.c +++ b/RunLoop.subproj/CFRunLoop.c @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -30,19 +28,168 @@ #include #include #include +#include "CFRunLoopPriv.h" #include "CFInternal.h" #include +#include #include #if defined(__MACH__) #include #include #include #else +#if !defined(__MINGW32__) && !defined(__CYGWIN__) +// With the MS headers, turning off Standard-C gets you macros for stat vs _stat. +// Strictly speaking, this is supposed to control traditional vs ANSI C features. +#undef __STDC__ +#endif #include +#include +#if !defined(__MINGW32__) && !defined(__CYGWIN__) +#define __STDC__ +#endif #endif + extern bool CFDictionaryGetKeyIfPresent(CFDictionaryRef dict, const void *key, const void **actualkey); +// In order to reuse most of the code across Mach and Windows v1 RunLoopSources, we define a +// simple abstraction layer spanning Mach ports and Windows HANDLES +#if defined(__MACH__) + +typedef mach_port_t __CFPort; +#define CFPORT_NULL MACH_PORT_NULL +typedef mach_port_t __CFPortSet; + +static __CFPort __CFPortAllocate(void) { + __CFPort result; + kern_return_t ret; + ret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &result); + if (KERN_SUCCESS == ret) { + ret = mach_port_insert_right(mach_task_self(), result, result, MACH_MSG_TYPE_MAKE_SEND); + } + if (KERN_SUCCESS == ret) { + mach_port_limits_t limits; + limits.mpl_qlimit = 1; + ret = mach_port_set_attributes(mach_task_self(), result, MACH_PORT_LIMITS_INFO, (mach_port_info_t)&limits, MACH_PORT_LIMITS_INFO_COUNT); + } + return (KERN_SUCCESS == ret) ? result : CFPORT_NULL; +} + +CF_INLINE void __CFPortFree(__CFPort port) { + mach_port_destroy(mach_task_self(), port); +} + +CF_INLINE __CFPortSet __CFPortSetAllocate(void) { + __CFPortSet result; + kern_return_t ret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_PORT_SET, &result); + return (KERN_SUCCESS == ret) ? result : CFPORT_NULL; +} + +CF_INLINE Boolean __CFPortSetInsert(__CFPort port, __CFPortSet portSet) { + kern_return_t ret = mach_port_insert_member(mach_task_self(), port, portSet); + return (KERN_SUCCESS == ret); +} + +CF_INLINE Boolean __CFPortSetRemove(__CFPort port, __CFPortSet portSet) { + kern_return_t ret = mach_port_extract_member(mach_task_self(), port, portSet); + return (KERN_SUCCESS == ret); +} + +CF_INLINE void __CFPortSetFree(__CFPortSet portSet) { + kern_return_t ret; + mach_port_name_array_t array; + mach_msg_type_number_t idx, number; + + ret = mach_port_get_set_status(mach_task_self(), portSet, &array, &number); + if (KERN_SUCCESS == ret) { + for (idx = 0; idx < number; idx++) { + mach_port_extract_member(mach_task_self(), array[idx], portSet); + } + vm_deallocate(mach_task_self(), (vm_address_t)array, number * sizeof(mach_port_name_t)); + } + mach_port_destroy(mach_task_self(), portSet); +} + +#elif defined(__WIN32__) + +typedef HANDLE __CFPort; +#define CFPORT_NULL NULL + +// A simple dynamic array of HANDLEs, which grows to a high-water mark +typedef struct ___CFPortSet { + uint16_t used; + uint16_t size; + HANDLE *handles; + CFSpinLock_t lock; // insert and remove must be thread safe, like the Mach calls +} *__CFPortSet; + +CF_INLINE __CFPort __CFPortAllocate(void) { + return CreateEvent(NULL, true, false, NULL); +} + +CF_INLINE void __CFPortFree(__CFPort port) { + CloseHandle(port); +} + +static __CFPortSet __CFPortSetAllocate(void) { + __CFPortSet result = CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(struct ___CFPortSet), 0); + result->used = 0; + result->size = 4; + result->handles = CFAllocatorAllocate(kCFAllocatorSystemDefault, result->size * sizeof(HANDLE), 0); + result->lock = 0; + return result; +} + +static void __CFPortSetFree(__CFPortSet portSet) { + CFAllocatorDeallocate(kCFAllocatorSystemDefault, portSet->handles); + CFAllocatorDeallocate(kCFAllocatorSystemDefault, portSet); +} + +// Returns portBuf if ports fit in that space, else returns another ptr that must be freed +static __CFPort *__CFPortSetGetPorts(__CFPortSet portSet, __CFPort *portBuf, uint32_t bufSize, uint32_t *portsUsed) { + __CFSpinLock(&(portSet->lock)); + __CFPort *result = portBuf; + if (bufSize > portSet->used) + result = CFAllocatorAllocate(kCFAllocatorSystemDefault, portSet->used * sizeof(HANDLE), 0); + memmove(result, portSet->handles, portSet->used * sizeof(HANDLE)); + *portsUsed = portSet->used; + __CFSpinUnlock(&(portSet->lock)); + return result; +} + +static Boolean __CFPortSetInsert(__CFPort port, __CFPortSet portSet) { + __CFSpinLock(&(portSet->lock)); + if (portSet->used >= portSet->size) { + portSet->size += 4; + portSet->handles = CFAllocatorReallocate(kCFAllocatorSystemDefault, portSet->handles, portSet->size * sizeof(HANDLE), 0); + } + if (portSet->used >= MAXIMUM_WAIT_OBJECTS) + CFLog(0, CFSTR("*** More than MAXIMUM_WAIT_OBJECTS (%d) ports add to a port set. The last ones will be ignored."), MAXIMUM_WAIT_OBJECTS); + portSet->handles[portSet->used++] = port; + __CFSpinUnlock(&(portSet->lock)); + return true; +} + +static Boolean __CFPortSetRemove(__CFPort port, __CFPortSet portSet) { + int i, j; + __CFSpinLock(&(portSet->lock)); + for (i = 0; i < portSet->used; i++) { + if (portSet->handles[i] == port) { + for (j = i+1; j < portSet->used; j++) { + portSet->handles[j-1] = portSet->handles[j]; + } + portSet->used--; + __CFSpinUnlock(&(portSet->lock)); + return true; + } + } + __CFSpinUnlock(&(portSet->lock)); + return false; +} + +#endif + #if defined(__MACH__) extern mach_port_name_t mk_timer_create(void); extern kern_return_t mk_timer_destroy(mach_port_name_t name); @@ -55,9 +202,7 @@ CF_INLINE AbsoluteTime __CFUInt64ToAbsoluteTime(int64_t x) { 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; @@ -67,26 +212,9 @@ static uint32_t __CFSendTrivialMachMessage(mach_port_t port, uint32_t msg_id, CF header.msgh_local_port = MACH_PORT_NULL; header.msgh_id = msg_id; result = mach_msg(&header, MACH_SEND_MSG|options, header.msgh_size, 0, MACH_PORT_NULL, timeout, MACH_PORT_NULL); + if (result == MACH_SEND_TIMED_OUT) mach_msg_destroy(&header); return result; } - -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 */ @@ -112,14 +240,17 @@ struct __CFRunLoopMode { CFMutableSetRef _observers; CFMutableSetRef _timers; CFMutableArrayRef _submodes; // names of the submodes + __CFPortSet _portSet; #if defined(__MACH__) - mach_port_t _portSet; + int _kq; #endif #if defined(__WIN32__) DWORD _msgQMask; #endif }; +static int64_t __CFRunLoopGetNextTimerFireTSR(CFRunLoopRef rl, CFRunLoopModeRef rlm); + CF_INLINE void __CFRunLoopModeLock(CFRunLoopModeRef rlm) { __CFSpinLock(&(rlm->_lock)); } @@ -161,21 +292,16 @@ static void __CFRunLoopModeDeallocate(CFTypeRef cf) { if (NULL != rlm->_timers) CFRelease(rlm->_timers); if (NULL != rlm->_submodes) CFRelease(rlm->_submodes); CFRelease(rlm->_name); + __CFPortSetFree(rlm->_portSet); #if defined(__MACH__) - __CFClearPortSet(mach_task_self(), rlm->_portSet); - mach_port_destroy(mach_task_self(), rlm->_portSet); + if (-1 != rlm->_kq) close(rlm->_kq); #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 + __CFPort _wakeUpPort; // used for CFRunLoopWakeUp volatile CFIndex *_stopped; CFMutableSetRef _commonModes; CFMutableSetRef _commonModeItems; @@ -231,7 +357,7 @@ 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("{locked = %s, wait port = 0x%x, stopped = %s,\ncurrent mode = %@,\n"), cf, CFGetAllocator(cf), rl->_lock ? "true" : "false", rl->_wakeUpPort, (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; } @@ -263,15 +389,11 @@ static CFRunLoopModeRef __CFRunLoopFindMode(CFRunLoopRef rl, CFStringRef modeNam rlm->_observers = NULL; rlm->_timers = NULL; rlm->_submodes = NULL; + rlm->_portSet = __CFPortSetAllocate(); + if (CFPORT_NULL == rlm->_portSet) HALT; + if (!__CFPortSetInsert(rl->_wakeUpPort, rlm->_portSet)) HALT; #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; - } + rlm->_kq = -1; #endif #if defined(__WIN32__) rlm->_msgQMask = 0; @@ -282,12 +404,13 @@ static CFRunLoopModeRef __CFRunLoopFindMode(CFRunLoopRef rl, CFStringRef modeNam return rlm; } -#if defined(__WIN32__) // expects rl and rlm locked static Boolean __CFRunLoopModeIsEmpty(CFRunLoopRef rl, CFRunLoopModeRef rlm) { if (NULL == rlm) return true; +#if defined(__WIN32__) if (0 != rlm->_msgQMask) return false; +#endif if (NULL != rlm->_sources && 0 < CFSetGetCount(rlm->_sources)) return false; if (NULL != rlm->_timers && 0 < CFSetGetCount(rlm->_timers)) return false; if (NULL != rlm->_submodes) { @@ -305,6 +428,7 @@ static Boolean __CFRunLoopModeIsEmpty(CFRunLoopRef rl, CFRunLoopModeRef rlm) { return true; } +#if defined(__WIN32__) DWORD __CFRunLoopGetWindowsMessageQueueMask(CFRunLoopRef rl, CFStringRef modeName) { CFRunLoopModeRef rlm; DWORD result = 0; @@ -326,29 +450,6 @@ void __CFRunLoopSetWindowsMessageQueueMask(CFRunLoopRef rl, DWORD mask, CFString __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 */ @@ -373,9 +474,8 @@ struct __CFRunLoopSource { CFMutableBagRef _runLoops; union { CFRunLoopSourceContext version0; /* immutable, except invalidation */ -#if defined(__MACH__) - CFRunLoopSourceContext1 version1; /* immutable, except invalidation */ -#endif + CFRunLoopSourceContext1 version1; /* immutable, except invalidation */ + CFRunLoopSourceContext2 version2; /* immutable, except invalidation */ } _context; }; @@ -405,7 +505,9 @@ CF_INLINE void __CFRunLoopSourceUnlock(CFRunLoopSourceRef rls) { static void __CFRunLoopSourceSchedule(CFRunLoopSourceRef rls, CFRunLoopRef rl, CFRunLoopModeRef rlm) { /* DOES CALLOUT */ __CFRunLoopSourceLock(rls); if (NULL == rls->_runLoops) { - rls->_runLoops = CFBagCreateMutable(CFGetAllocator(rls), 0, NULL); + // GrP GC: source -> runloop is a WEAK REFERENCE + // Use non-scanned memory and non-retaining callbacks. + rls->_runLoops = CFBagCreateMutable(CF_USING_COLLECTABLE_MEMORY ? kCFAllocatorMallocZone : CFGetAllocator(rls), 0, NULL); } CFBagAddValue(rls->_runLoops, rl); __CFRunLoopSourceUnlock(rls); // have to unlock before the callout -- cannot help clients with safety @@ -413,12 +515,21 @@ static void __CFRunLoopSourceSchedule(CFRunLoopSourceRef rls, CFRunLoopRef rl, C 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); + __CFPort port = rls->_context.version1.getPort(rls->_context.version1.info); /* CALLOUT */ + if (CFPORT_NULL != port) { + __CFPortSetInsert(port, rlm->_portSet); + } + } else if (2 == rls->_context.version0.version) { +#if defined(__MACH__) + if (-1 == rlm->_kq) { + rlm->_kq = kqueue_from_portset_np(rlm->_portSet); + } + rls->_context.version2.event.flags |= EV_ADD; + int ret = kevent(rlm->_kq, &(rls->_context.version2.event), 1, NULL, 0, NULL); + rls->_context.version2.event.flags &= ~EV_ADD; + if (ret < 0) { + CFLog(0, CFSTR("CFRunLoop: tragic kevent failure #1")); } #endif } @@ -430,12 +541,21 @@ static void __CFRunLoopSourceCancel(CFRunLoopSourceRef rls, CFRunLoopRef rl, CFR 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); + __CFPort port = rls->_context.version1.getPort(rls->_context.version1.info); /* CALLOUT */ + if (CFPORT_NULL != port) { + __CFPortSetRemove(port, rlm->_portSet); + } + } else if (2 == rls->_context.version0.version) { +#if defined(__MACH__) + if (-1 == rlm->_kq) { + rlm->_kq = kqueue_from_portset_np(rlm->_portSet); + } + rls->_context.version2.event.flags |= EV_DELETE; + int ret = kevent(rlm->_kq, &(rls->_context.version2.event), 1, NULL, 0, NULL); + rls->_context.version2.event.flags &= ~EV_DELETE; + if (ret < 0) { + CFLog(0, CFSTR("CFRunLoop: tragic kevent failure #2")); } #endif } @@ -526,7 +646,7 @@ struct __CFRunLoopTimer { }; /* 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 */ +/* Bit 1 of the base reserved bits is used for fired-during-callout state */ CF_INLINE Boolean __CFRunLoopTimerIsFiring(CFRunLoopTimerRef rlt) { return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)rlt)->_info, 0, 0); @@ -540,6 +660,18 @@ CF_INLINE void __CFRunLoopTimerUnsetFiring(CFRunLoopTimerRef rlt) { __CFBitfieldSetValue(((CFRuntimeBase *)rlt)->_info, 0, 0, 0); } +CF_INLINE Boolean __CFRunLoopTimerDidFire(CFRunLoopTimerRef rlt) { + return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)rlt)->_info, 1, 1); +} + +CF_INLINE void __CFRunLoopTimerSetDidFire(CFRunLoopTimerRef rlt) { + __CFBitfieldSetValue(((CFRuntimeBase *)rlt)->_info, 1, 1, 1); +} + +CF_INLINE void __CFRunLoopTimerUnsetDidFire(CFRunLoopTimerRef rlt) { + __CFBitfieldSetValue(((CFRuntimeBase *)rlt)->_info, 1, 1, 0); +} + CF_INLINE void __CFRunLoopTimerLock(CFRunLoopTimerRef rlt) { __CFSpinLock(&(rlt->_lock)); } @@ -558,6 +690,7 @@ CF_INLINE void __CFRunLoopTimerFireTSRUnlock(void) { __CFSpinUnlock(&__CFRLTFireTSRLock); } +#if defined(__MACH__) static CFMutableDictionaryRef __CFRLTPortMap = NULL; static CFSpinLock_t __CFRLTPortMapLock = 0; @@ -568,6 +701,7 @@ CF_INLINE void __CFRunLoopTimerPortMapLock(void) { CF_INLINE void __CFRunLoopTimerPortMapUnlock(void) { __CFSpinUnlock(&__CFRLTPortMapLock); } +#endif static void __CFRunLoopTimerSchedule(CFRunLoopTimerRef rlt, CFRunLoopRef rl, CFRunLoopModeRef rlm) { #if defined(__MACH__) @@ -594,7 +728,7 @@ static void __CFRunLoopTimerSchedule(CFRunLoopTimerRef rlt, CFRunLoopRef rl, CFR 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); + __CFPortSetRemove(rlt->_port, rlm->_portSet); rlt->_rlCount--; if (0 == rlt->_rlCount) { __CFRunLoopTimerPortMapLock(); @@ -609,29 +743,73 @@ static void __CFRunLoopTimerCancel(CFRunLoopTimerRef rlt, CFRunLoopRef rl, CFRun #endif } +// Caller must hold the Timer lock for safety static void __CFRunLoopTimerRescheduleWithAllModes(CFRunLoopTimerRef rlt, CFRunLoopRef rl) { #if defined(__MACH__) mk_timer_arm(rlt->_port, __CFUInt64ToAbsoluteTime(rlt->_fireTSR)); #endif } +#if defined(__WIN32__) + +struct _collectTimersContext { + CFMutableArrayRef results; + int64_t cutoffTSR; +}; + +static void __CFRunLoopCollectTimers(const void *value, void *ctx) { + CFRunLoopTimerRef rlt = (CFRunLoopTimerRef)value; + struct _collectTimersContext *context = ctx; + if (rlt->_fireTSR <= context->cutoffTSR) { + if (NULL == context->results) + context->results = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks); + CFArrayAppendValue(context->results, rlt); + } +} + +// RunLoop and RunLoopMode must be locked +static void __CFRunLoopTimersToFireRecursive(CFRunLoopRef rl, CFRunLoopModeRef rlm, struct _collectTimersContext *ctxt) { + if (NULL != rlm->_timers && 0 < CFSetGetCount(rlm->_timers)) { + __CFRunLoopTimerFireTSRLock(); + CFSetApplyFunction(rlm->_timers, __CFRunLoopCollectTimers, ctxt); + __CFRunLoopTimerFireTSRUnlock(); + } + if (NULL != rlm->_submodes) { + CFIndex idx, cnt; + for (idx = 0, cnt = CFArrayGetCount(rlm->_submodes); idx < cnt; idx++) { + CFStringRef modeName = (CFStringRef)CFArrayGetValueAtIndex(rlm->_submodes, idx); + CFRunLoopModeRef subrlm; + subrlm = __CFRunLoopFindMode(rl, modeName, false); + if (NULL != subrlm) { + __CFRunLoopTimersToFireRecursive(rl, subrlm, ctxt); + __CFRunLoopModeUnlock(subrlm); + } + } + } +} + +// RunLoop and RunLoopMode must be locked +static CFArrayRef __CFRunLoopTimersToFire(CFRunLoopRef rl, CFRunLoopModeRef rlm) { + struct _collectTimersContext ctxt = {NULL, __CFReadTSR()}; + __CFRunLoopTimersToFireRecursive(rl, rlm, &ctxt); + return ctxt.results; +} +#endif /* CFRunLoop */ CONST_STRING_DECL(kCFRunLoopDefaultMode, "kCFRunLoopDefaultMode") CONST_STRING_DECL(kCFRunLoopCommonModes, "kCFRunLoopCommonModes") -#if defined(__MACH__) - struct _findsource { - mach_port_t port; + __CFPort 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; + __CFPort port; if (NULL != context->result) return; if (1 != rls->_context.version0.version) return; __CFRunLoopSourceLock(rls); @@ -643,7 +821,7 @@ static void __CFRunLoopFindSource(const void *value, void *ctx) { } // call with rl and rlm locked -static CFRunLoopSourceRef __CFRunLoopModeFindSourceForMachPort(CFRunLoopRef rl, CFRunLoopModeRef rlm, mach_port_t port) { /* DOES CALLOUT */ +static CFRunLoopSourceRef __CFRunLoopModeFindSourceForMachPort(CFRunLoopRef rl, CFRunLoopModeRef rlm, __CFPort port) { /* DOES CALLOUT */ struct _findsource context = {port, NULL}; if (NULL != rlm->_sources) { CFSetApplyFunction(rlm->_sources, (__CFRunLoopFindSource), &context); @@ -668,8 +846,9 @@ static CFRunLoopSourceRef __CFRunLoopModeFindSourceForMachPort(CFRunLoopRef rl, return context.result; } +#if defined(__MACH__) // call with rl and rlm locked -static CFRunLoopTimerRef __CFRunLoopModeFindTimerForMachPort(CFRunLoopModeRef rlm, mach_port_name_t port) { +static CFRunLoopTimerRef __CFRunLoopModeFindTimerForMachPort(CFRunLoopModeRef rlm, __CFPort port) { CFRunLoopTimerRef result = NULL; __CFRunLoopTimerPortMapLock(); if (NULL != __CFRLTPortMap) { @@ -766,14 +945,8 @@ static void __CFRunLoopDeallocate(CFTypeRef cf) { 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 + __CFPortFree(rl->_wakeUpPort); + rl->_wakeUpPort = CFPORT_NULL; __CFRunLoopUnlock(rl); } @@ -820,23 +993,8 @@ static CFRunLoopRef __CFRunLoopCreate(void) { } 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->_wakeUpPort = __CFPortAllocate(); + if (CFPORT_NULL == loop->_wakeUpPort) HALT; loop->_commonModes = CFSetCreateMutable(CFGetAllocator(loop), 0, &kCFTypeSetCallBacks); CFSetAddValue(loop->_commonModes, kCFRunLoopDefaultMode); loop->_commonModeItems = NULL; @@ -848,6 +1006,10 @@ static CFRunLoopRef __CFRunLoopCreate(void) { return loop; } +#if defined(__MACH__) +// We don't properly call _CFRunLoopSetMain on Win32, so better to cut these routines +// out until they are properly implemented. + static CFRunLoopRef mainLoop = NULL; static int mainLoopPid = 0; static CFSpinLock_t mainLoopLock = 0; @@ -874,11 +1036,14 @@ static void _CFRunLoopSetMain(CFRunLoopRef rl) { mainLoop = rl; } } +#endif CFRunLoopRef CFRunLoopGetCurrent(void) { +#if defined(__MACH__) if (pthread_main_np()) { return CFRunLoopGetMain(); } +#endif CFRunLoopRef currentLoop = __CFGetThreadSpecificData_inline()->_runLoop; int currentLoopPid = __CFGetThreadSpecificData_inline()->_runLoop_pid; if (currentLoopPid != getpid()) { @@ -894,9 +1059,11 @@ CFRunLoopRef CFRunLoopGetCurrent(void) { } void _CFRunLoopSetCurrent(CFRunLoopRef rl) { +#if defined(__MACH__) if (pthread_main_np()) { return _CFRunLoopSetMain(rl); } +#endif CFRunLoopRef currentLoop = __CFGetThreadSpecificData_inline()->_runLoop; if (rl != currentLoop) { if (rl) CFRetain(rl); @@ -1162,8 +1329,12 @@ static Boolean __CFRunLoopDoSources0(CFRunLoopRef rl, CFRunLoopModeRef rlm, Bool return sourceHandled; } +// msg, size and reply are unused on Windows +static Boolean __CFRunLoopDoSource1(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunLoopSourceRef rls #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 */ + , mach_msg_header_t *msg, CFIndex size, mach_msg_header_t **reply +#endif + ) { /* DOES CALLOUT */ Boolean sourceHandled = false; /* Fire a version 1 source */ @@ -1174,7 +1345,11 @@ static Boolean __CFRunLoopDoSource1(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRun __CFRunLoopSourceUnsetSignaled(rls); __CFRunLoopSourceUnlock(rls); if (NULL != rls->_context.version1.perform) { +#if defined(__MACH__) *reply = rls->_context.version1.perform(msg, size, kCFAllocatorSystemDefault, rls->_context.version1.info); /* CALLOUT */ +#else + rls->_context.version1.perform(rls->_context.version1.info); /* CALLOUT */ +#endif } sourceHandled = true; } else { @@ -1184,7 +1359,6 @@ static Boolean __CFRunLoopDoSource1(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRun __CFRunLoopModeLock(rlm); return sourceHandled; } -#endif static Boolean __CFRunLoopDoTimer(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunLoopTimerRef rlt) { /* DOES CALLOUT */ Boolean timerHandled = false; @@ -1195,6 +1369,7 @@ static Boolean __CFRunLoopDoTimer(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunLo __CFRunLoopModeUnlock(rlm); __CFRunLoopTimerLock(rlt); if (__CFIsValid(rlt) && !__CFRunLoopTimerIsFiring(rlt)) { + __CFRunLoopTimerUnsetDidFire(rlt); __CFRunLoopTimerSetFiring(rlt); __CFRunLoopTimerUnlock(rlt); __CFRunLoopTimerFireTSRLock(); @@ -1204,6 +1379,13 @@ static Boolean __CFRunLoopDoTimer(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunLo __CFRunLoopTimerUnsetFiring(rlt); timerHandled = true; } else { + // If the timer fires while it is firing in a higher activiation, + // it is not allowed to fire, but we have to remember that fact. + // Later, if the timer's fire date is being handled manually, we + // need to re-arm the kernel timer, since it has possibly already + // fired (this firing which is being skipped, say) and the timer + // will permanently stop if we completely drop this firing. + if (__CFRunLoopTimerIsFiring(rlt)) __CFRunLoopTimerSetDidFire(rlt); __CFRunLoopTimerUnlock(rlt); } if (__CFIsValid(rlt) && timerHandled) { @@ -1220,6 +1402,10 @@ static Boolean __CFRunLoopDoTimer(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunLo if (oldFireTSR < currentFireTSR) { /* Next fire TSR was set, and set to a date after the previous * fire date, so we honor it. */ + if (__CFRunLoopTimerDidFire(rlt)) { + __CFRunLoopTimerRescheduleWithAllModes(rlt, rl); + __CFRunLoopTimerUnsetDidFire(rlt); + } } else { if ((uint64_t)LLONG_MAX <= (uint64_t)oldFireTSR + (uint64_t)rlt->_intervalTSR) { currentFireTSR = LLONG_MAX; @@ -1230,10 +1416,10 @@ static Boolean __CFRunLoopDoTimer(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunLo currentFireTSR += rlt->_intervalTSR; } } + rlt->_fireTSR = currentFireTSR; + __CFRunLoopTimerRescheduleWithAllModes(rlt, rl); } - rlt->_fireTSR = currentFireTSR; __CFRunLoopTimerFireTSRUnlock(); - __CFRunLoopTimerRescheduleWithAllModes(rlt, rl); } } CFRelease(rlt); @@ -1255,8 +1441,7 @@ CF_EXPORT Boolean _CFRunLoopFinished(CFRunLoopRef rl, CFStringRef modeName) { } // rl is locked, rlm is locked on entry and exit -#if defined(__MACH__) -static void __CFRunLoopModeAddPortsToPortSet(CFRunLoopRef rl, CFRunLoopModeRef rlm, mach_port_t portSet) { +static void __CFRunLoopModeAddPortsToPortSet(CFRunLoopRef rl, CFRunLoopModeRef rlm, __CFPortSet portSet) { CFIndex idx, cnt; const void **list, *buffer[256]; @@ -1267,15 +1452,27 @@ static void __CFRunLoopModeAddPortsToPortSet(CFRunLoopRef rl, CFRunLoopModeRef r 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 (1 == rls->_context.version0.version) { + __CFPort port = rls->_context.version1.getPort(rls->_context.version1.info); /* CALLOUT */ + if (CFPORT_NULL != port) { + __CFPortSetInsert(port, portSet); + } + } else if (2 == rls->_context.version0.version) { +#if defined(__MACH__) + int kq = kqueue_from_portset_np(portSet); + rls->_context.version2.event.flags |= EV_ADD; + int ret = kevent(kq, &(rls->_context.version2.event), 1, NULL, 0, NULL); + rls->_context.version2.event.flags &= ~EV_ADD; + close(kq); + if (ret < 0) { + CFLog(0, CFSTR("CFRunLoop: tragic kevent failure #3")); + } +#endif } } if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list); } +#if defined(__MACH__) if (NULL != rlm->_timers) { cnt = CFSetGetCount(rlm->_timers); list = (cnt <= 256) ? buffer : CFAllocatorAllocate(kCFAllocatorSystemDefault, cnt * sizeof(void *), 0); @@ -1288,6 +1485,7 @@ static void __CFRunLoopModeAddPortsToPortSet(CFRunLoopRef rl, CFRunLoopModeRef r } if (list != buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, list); } +#endif // iterate over submodes for (idx = 0, cnt = NULL != rlm->_submodes ? CFArrayGetCount(rlm->_submodes) : 0; idx < cnt; idx++) { CFStringRef modeName = (CFStringRef)CFArrayGetValueAtIndex(rlm->_submodes, idx); @@ -1299,18 +1497,46 @@ static void __CFRunLoopModeAddPortsToPortSet(CFRunLoopRef rl, CFRunLoopModeRef r } } } + +static __CFPortSet _LastMainWaitSet = NULL; + +// return NO if we're the main runloop and there are no messages waiting on the port set +int _CFRunLoopInputsReady(void) { + // XXX_PCB: the following 2 lines aren't safe to call during GC, because another + // thread may have entered CFRunLoopGetMain(), which grabs a spink lock, and then + // is suspended by the GC. We can check for the main thread more directly + // by calling pthread_main_np(). + // CFRunLoopRef current = CFRunLoopGetMain() + // if (current != CFRunLoopGetMain()) return true; +#if defined(__MACH__) + if (!pthread_main_np()) return true; #endif + // XXX_PCB: can't be any messages waiting if the wait set is NULL. + if (_LastMainWaitSet == MACH_PORT_NULL) return false; + + // prepare a message header with no space for any data, nor a trailer + mach_msg_header_t msg; + msg.msgh_size = sizeof(msg); // just the header, ma'am + // need the waitset, actually XXX + msg.msgh_local_port = _LastMainWaitSet; + msg.msgh_remote_port = MACH_PORT_NULL; + msg.msgh_id = 0; + + kern_return_t ret = mach_msg(&msg, MACH_RCV_MSG | MACH_RCV_TIMEOUT | MACH_RCV_LARGE, 0, msg.msgh_size, _LastMainWaitSet, 0, MACH_PORT_NULL); + + return (MACH_RCV_TOO_LARGE == ret); +} + /* 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; + Boolean firstPass = true; if (__CFRunLoopIsStopped(rl)) { return kCFRunLoopRunStopped; @@ -1318,7 +1544,6 @@ static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInter rlm->_stopped = false; return kCFRunLoopRunStopped; } -#if !defined(__WIN32__) if (seconds <= 0.0) { termTSR = 0; } else if (__CFTSRToTimeInterval(LLONG_MAX) < seconds) { @@ -1327,30 +1552,29 @@ static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInter termTSR = LLONG_MAX; } else { termTSR = (int64_t)__CFReadTSR() + __CFTimeIntervalToTSR(seconds); +#if defined(__MACH__) 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; } + if (rl == mainLoop) _LastMainWaitSet = CFPORT_NULL; for (;;) { + __CFPortSet waitSet = CFPORT_NULL; + waitSet = CFPORT_NULL; + Boolean destroyWaitSet = false; + CFRunLoopSourceRef rls; #if defined(__MACH__) mach_msg_header_t *msg; kern_return_t ret; - mach_port_t waitSet = MACH_PORT_NULL; - Boolean destroyWaitSet = false; + uint8_t buffer[1024 + 80]; // large enough for 1k of inline payload +#else + CFArrayRef timersToCall = NULL; #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); @@ -1365,30 +1589,35 @@ static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInter __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; + waitSet = __CFPortSetAllocate(); + if (CFPORT_NULL == waitSet) 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); +#if defined(__MACH__) + if (CFPORT_NULL != timeoutPort) { + __CFPortSetInsert(timeoutPort, waitSet); } - destroyWaitSet = true; +#endif + destroyWaitSet = true; } else { waitSet = rlm->_portSet; - if (!timeoutPortAdded && MACH_PORT_NULL != timeoutPort) { - mach_port_insert_member(mach_task_self(), timeoutPort, waitSet); +#if defined(__MACH__) + if (!timeoutPortAdded && CFPORT_NULL != timeoutPort) { + __CFPortSetInsert(timeoutPort, waitSet); timeoutPortAdded = true; } +#endif } + if (rl == mainLoop) _LastMainWaitSet = waitSet; __CFRunLoopModeUnlock(rlm); - msg = (mach_msg_header_t *)buffer; +#if defined(__MACH__) + msg = (mach_msg_header_t *)buffer; msg->msgh_size = sizeof(buffer); /* In that sleep of death what nightmares may come ... */ @@ -1397,17 +1626,9 @@ static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInter 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; @@ -1419,23 +1640,54 @@ static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInter } else if (MACH_MSG_SUCCESS != ret) { HALT; } -#elif defined(__WIN32__) -// should msgQMask be an OR'ing of this and all submodes' masks? +#elif defined(__WIN32__) + __CFRunLoopModeUnlock(rlm); + DWORD waitResult = WAIT_TIMEOUT; + HANDLE handleBuf[MAXIMUM_WAIT_OBJECTS]; + HANDLE *handles; + uint32_t handleCount; + Boolean freeHandles; + if (destroyWaitSet) { + // wait set is a local, no one else could modify it, no need to copy handles + handles = waitSet->handles; + handleCount = waitSet->used; + freeHandles = FALSE; + } else { + // copy out the handles to be safe from other threads at work + handles = __CFPortSetGetPorts(waitSet, handleBuf, MAXIMUM_WAIT_OBJECTS, &handleCount); + freeHandles = (handles != handleBuf); + } + // 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); + DWORD timeout; + if (poll) + timeout = 0; + else { + int64_t nextStop = __CFRunLoopGetNextTimerFireTSR(rl, rlm); + if (nextStop <= 0) + nextStop = termTSR; + else if (nextStop > termTSR) + nextStop = termTSR; + // else the next stop is dictated by the next timer + int64_t timeoutTSR = nextStop - __CFReadTSR(); + if (timeoutTSR < 0) + timeout = 0; + else { + CFTimeInterval timeoutCF = __CFTSRToTimeInterval(timeoutTSR) * 1000; + if (timeoutCF > MAXDWORD) + timeout = INFINITE; + else + timeout = timeoutCF; + } + } + waitResult = MsgWaitForMultipleObjects(__CFMin(handleCount, MAXIMUM_WAIT_OBJECTS), handles, false, timeout, rlm->_msgQMask); + } + ResetEvent(rl->_wakeUpPort); #endif - -#if defined(__MACH__) if (destroyWaitSet) { - __CFClearPortSet(mach_task_self(), waitSet); - mach_port_destroy(mach_task_self(), waitSet); + __CFPortSetFree(waitSet); + if (rl == mainLoop) _LastMainWaitSet = NULL; } -#endif __CFRunLoopLock(rl); __CFRunLoopModeLock(rlm); __CFRunLoopUnlock(rl); @@ -1448,37 +1700,103 @@ static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInter __CFRunLoopLock(rl); __CFRunLoopModeLock(rlm); + __CFPort livePort = CFPORT_NULL; #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)) { + livePort = msg->msgh_local_port; + } +#elif defined(__WIN32__) + CFAssert2(waitResult != WAIT_FAILED, __kCFLogAssertion, "%s(): error %d from MsgWaitForMultipleObjects", __PRETTY_FUNCTION__, GetLastError()); + if (waitResult == WAIT_TIMEOUT) { + // do nothing, just return to caller + } else if (waitResult >= WAIT_OBJECT_0 && waitResult < WAIT_OBJECT_0+handleCount) { + // a handle was signaled + livePort = handles[waitResult-WAIT_OBJECT_0]; + } else if (waitResult == WAIT_OBJECT_0+handleCount) { + // windows message received - the CFWindowsMessageQueue will pick this up when + // the v0 RunLoopSources get their chance + } else if (waitResult >= WAIT_ABANDONED_0 && waitResult < WAIT_ABANDONED_0+handleCount) { + // an "abandoned mutex object" + livePort = handles[waitResult-WAIT_ABANDONED_0]; + } else { + CFAssert2(waitResult == WAIT_FAILED, __kCFLogAssertion, "%s(): unexpected result from MsgWaitForMultipleObjects: %d", __PRETTY_FUNCTION__, waitResult); + } + if (freeHandles) + CFAllocatorDeallocate(kCFAllocatorSystemDefault, handles); + timersToCall = __CFRunLoopTimersToFire(rl, rlm); +#endif + + if (CFPORT_NULL == livePort) { + __CFRunLoopUnlock(rl); +#if defined(__MACH__) + if (NULL != msg) { + // This must be a kevent, msgh_local_port is MACH_PORT_NULL in that case + struct kevent *kev = (void *)msg + sizeof(mach_msg_header_t) + ((msg->msgh_bits & MACH_MSGH_BITS_COMPLEX) ? (sizeof(mach_msg_body_t) + sizeof(mach_msg_descriptor_t) * ((mach_msg_base_t *)msg)->body.msgh_descriptor_count) : 0); + rls = kev->udata; + kev->udata = NULL; + + /* Fire a version 2 source */ + CFRetain(rls); + __CFRunLoopModeUnlock(rlm); + __CFRunLoopSourceLock(rls); + if (__CFIsValid(rls)) { + __CFRunLoopSourceUnsetSignaled(rls); + __CFRunLoopSourceUnlock(rls); + if (NULL != rls->_context.version2.perform) { + rls->_context.version2.perform(kev, rls->_context.version2.info); /* CALLOUT */ + } sourceHandledThisLoop = true; + } else { + __CFRunLoopSourceUnlock(rls); } - if (NULL != reply) { - ret = mach_msg(reply, MACH_SEND_MSG, reply->msgh_size, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL); + CFRelease(rls); + __CFRunLoopModeLock(rlm); + } +#endif + } else if (livePort == rl->_wakeUpPort) { + // wakeup + __CFRunLoopUnlock(rl); + } +#if defined(__MACH__) + else if (livePort == timeoutPort) { + returnValue = kCFRunLoopRunTimedOut; + __CFRunLoopUnlock(rl); + } else if (NULL != (rls = __CFRunLoopModeFindSourceForMachPort(rl, rlm, livePort))) { + 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); - } + CFAllocatorDeallocate(kCFAllocatorSystemDefault, reply); } - if (msg != (mach_msg_header_t *)buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, msg); } else { + CFRunLoopTimerRef rlt; + rlt = __CFRunLoopModeFindTimerForMachPort(rlm, livePort); + __CFRunLoopUnlock(rl); + if (NULL != rlt) { + __CFRunLoopDoTimer(rl, rlm, rlt); + } + } + if (msg != (mach_msg_header_t *)buffer) CFAllocatorDeallocate(kCFAllocatorSystemDefault, msg); +#else + else if (NULL != (rls = __CFRunLoopModeFindSourceForMachPort(rl, rlm, livePort))) { __CFRunLoopUnlock(rl); - } + if (__CFRunLoopDoSource1(rl, rlm, rls)) { + sourceHandledThisLoop = true; + } + } +#endif + +#if defined(__WIN32__) + if (NULL != timersToCall) { + int i; + for (i = CFArrayGetCount(timersToCall)-1; i >= 0; i--) + __CFRunLoopDoTimer(rl, rlm, (CFRunLoopTimerRef)CFArrayGetValueAtIndex(timersToCall, i)); + CFRelease(timersToCall); + } #endif __CFRunLoopModeUnlock(rlm); // locks must be taken in order @@ -1486,7 +1804,10 @@ static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInter __CFRunLoopModeLock(rlm); if (sourceHandledThisLoop && stopAfterHandle) { returnValue = kCFRunLoopRunHandledSource; - } else if (0 != returnValue || (uint64_t)termTSR <= __CFReadTSR()) { + // If we're about to timeout, but we just did a zero-timeout poll that only found our own + // internal wakeup signal on the first look at the portset, we'll go around the loop one + // more time, so as not to starve a v1 source that was just added along with a runloop wakeup. + } else if (0 != returnValue || (uint64_t)termTSR <= __CFReadTSR()) { returnValue = kCFRunLoopRunTimedOut; } else if (__CFRunLoopIsStopped(rl)) { returnValue = kCFRunLoopRunStopped; @@ -1500,22 +1821,16 @@ static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInter if (0 != returnValue) { #if defined(__MACH__) if (MACH_PORT_NULL != timeoutPort) { - if (!destroyWaitSet) mach_port_extract_member(mach_task_self(), timeoutPort, waitSet); + if (!destroyWaitSet) __CFPortSetRemove(timeoutPort, waitSet); mk_timer_destroy(timeoutPort); } #endif return returnValue; } + firstPass = false; } } -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; @@ -1529,6 +1844,7 @@ SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterva __CFRunLoopUnlock(rl); return kCFRunLoopRunFinished; } + // We can drop the volatile-ness for the previousStopped ptr previousStopped = (CFIndex *)rl->_stopped; rl->_stopped = CFAllocatorAllocate(kCFAllocatorSystemDefault, 16, 0); rl->_stopped[0] = 0x4346524C; @@ -1543,42 +1859,74 @@ SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterva __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit); __CFRunLoopModeUnlock(currentMode); __CFRunLoopLock(rl); - CFAllocatorDeallocate(kCFAllocatorSystemDefault, (void *)rl->_stopped); + CFAllocatorDeallocate(kCFAllocatorSystemDefault, (CFIndex *)rl->_stopped); rl->_stopped = previousStopped; rl->_currentMode = previousMode; __CFRunLoopUnlock(rl); return result; } +void CFRunLoopRun(void) { /* DOES CALLOUT */ + int32_t result; + do { + result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false); + } while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != 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; + if (__CFIsValid(rlt)) { + CFRunLoopTimerRef *result = ctx; + if (NULL == *result || rlt->_fireTSR < (*result)->_fireTSR) { + *result = rlt; + } } } -CFAbsoluteTime CFRunLoopGetNextTimerFireDate(CFRunLoopRef rl, CFStringRef modeName) { - CFRunLoopModeRef rlm; +static int64_t __CFRunLoopGetNextTimerFireTSR(CFRunLoopRef rl, 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; + if (result) + fireTime = result->_fireTSR; __CFRunLoopTimerFireTSRUnlock(); } - __CFRunLoopModeUnlock(rlm); - } - return (0 == fireTime) ? 0.0 : __CFTSRToAbsoluteTime(fireTime); + if (NULL != rlm->_submodes) { + CFIndex idx, cnt; + for (idx = 0, cnt = CFArrayGetCount(rlm->_submodes); idx < cnt; idx++) { + CFStringRef modeName = (CFStringRef)CFArrayGetValueAtIndex(rlm->_submodes, idx); + CFRunLoopModeRef subrlm; + subrlm = __CFRunLoopFindMode(rl, modeName, false); + if (NULL != subrlm) { + int64_t newFireTime = __CFRunLoopGetNextTimerFireTSR(rl, subrlm); + __CFRunLoopModeUnlock(subrlm); + if (fireTime == 0 || (newFireTime != 0 && newFireTime < fireTime)) + fireTime = newFireTime; + } + } + } + __CFRunLoopModeUnlock(rlm); +} + return fireTime; +} + +CFAbsoluteTime CFRunLoopGetNextTimerFireDate(CFRunLoopRef rl, CFStringRef modeName) { + CFRunLoopModeRef rlm; + int64_t fireTSR; + __CFRunLoopLock(rl); + rlm = __CFRunLoopFindMode(rl, modeName, false); + __CFRunLoopUnlock(rl); + fireTSR = __CFRunLoopGetNextTimerFireTSR(rl, rlm); + int64_t now2 = (int64_t)mach_absolute_time(); + CFAbsoluteTime now1 = CFAbsoluteTimeGetCurrent(); + return (0 == fireTSR) ? 0.0 : (now1 + __CFTSRToTimeInterval(fireTSR - now2)); } Boolean CFRunLoopIsWaiting(CFRunLoopRef rl) { @@ -1591,12 +1939,12 @@ void CFRunLoopWakeUp(CFRunLoopRef rl) { /* 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); + ret = __CFSendTrivialMachMessage(rl->_wakeUpPort, 0, MACH_SEND_TIMEOUT, 0); if (ret != MACH_MSG_SUCCESS && ret != MACH_SEND_TIMED_OUT) { HALT; } #else - SetEvent(rl->_waitPort); + SetEvent(rl->_wakeUpPort); #endif } @@ -1979,6 +2327,8 @@ static Boolean __CFRunLoopSourceEqual(CFTypeRef cf1, CFTypeRef cf2) { /* DOES CA 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 (2 == rls1->_context.version0.version && rls1->_context.version2.perform != rls2->_context.version2.perform) return false; + if (2 == rls1->_context.version0.version && !(rls1->_context.version2.event.ident == rls2->_context.version2.event.ident && rls1->_context.version2.event.filter == rls2->_context.version2.event.filter)) 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); @@ -2001,7 +2351,7 @@ static CFStringRef __CFRunLoopSourceCopyDescription(CFTypeRef cf) { /* DOES CALL 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); +result = CFStringCreateWithFormat(CFGetAllocator(rls), NULL, CFSTR("{locked = %s, signaled = %s, valid = %s, order = %d, context = %@}"), cf, CFGetAllocator(rls), rls->_lock ? "Yes" : "No", __CFRunLoopSourceIsSignaled(rls) ? "Yes" : "No", __CFIsValid(rls) ? "Yes" : "No", rls->_order, contextDesc); CFRelease(contextDesc); return result; } @@ -2015,7 +2365,7 @@ static void __CFRunLoopSourceDeallocate(CFTypeRef cf) { /* DOES CALLOUT */ } static const CFRuntimeClass __CFRunLoopSourceClass = { - 0, + _kCFRuntimeScannedObject, "CFRunLoopSource", NULL, // init NULL, // copy @@ -2049,11 +2399,23 @@ CFRunLoopSourceRef CFRunLoopSourceCreate(CFAllocatorRef allocator, CFIndex order 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 + size = 0; + switch (context->version) { + case 0: + size = sizeof(CFRunLoopSourceContext); + break; + case 1: + size = sizeof(CFRunLoopSourceContext1); + break; + case 2: + size = sizeof(CFRunLoopSourceContext2); + break; + } + CF_WRITE_BARRIER_MEMMOVE(&memory->_context, context, size); + if (2 == memory->_context.version0.version) { + memory->_context.version2.event.udata = memory; + memory->_context.version2.event.flags &= ~(EV_SYSFLAGS | 0xFF0F); // clear all but a few flags + } if (context->retain) { memory->_context.version0.info = (void *)context->retain(context->info); } @@ -2113,13 +2475,20 @@ Boolean CFRunLoopSourceIsValid(CFRunLoopSourceRef 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 + CFAssert1(0 == context->version || 1 == context->version || 2 == context->version, __kCFLogAssertion, "%s(): context version not initialized to 0 or 1", __PRETTY_FUNCTION__); + CFIndex size = 0; + switch (context->version) { + case 0: + size = sizeof(CFRunLoopSourceContext); + break; + case 1: + size = sizeof(CFRunLoopSourceContext1); + break; + case 2: + size = sizeof(CFRunLoopSourceContext2); + break; + } + memmove(context, &rls->_context, size); } void CFRunLoopSourceSignal(CFRunLoopSourceRef rls) { @@ -2137,7 +2506,6 @@ static CFStringRef __CFRunLoopObserverCopyDescription(CFTypeRef cf) { /* DOES CA CFRunLoopObserverRef rlo = (CFRunLoopObserverRef)cf; CFStringRef result; CFStringRef contextDesc = NULL; - __CFRunLoopObserverLock(rlo); if (NULL != rlo->_context.copyDescription) { contextDesc = rlo->_context.copyDescription(rlo->_context.info); } @@ -2145,7 +2513,6 @@ static CFStringRef __CFRunLoopObserverCopyDescription(CFTypeRef cf) { /* DOES CA 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; } @@ -2277,15 +2644,15 @@ static CFStringRef __CFRunLoopTimerCopyDescription(CFTypeRef cf) { /* DOES CALLO __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); + int64_t now2 = (int64_t)mach_absolute_time(); + CFAbsoluteTime now1 = CFAbsoluteTimeGetCurrent(); + 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), now1 + __CFTSRToTimeInterval(fireTime - now2), rlt->_order, rlt->_callout, contextDesc); CFRelease(contextDesc); return result; } @@ -2325,6 +2692,7 @@ CFRunLoopTimerRef CFRunLoopTimerCreate(CFAllocatorRef allocator, CFAbsoluteTime } __CFSetValid(memory); __CFRunLoopTimerUnsetFiring(memory); + __CFRunLoopTimerUnsetDidFire(memory); memory->_lock = 0; memory->_runLoop = NULL; memory->_rlCount = 0; @@ -2332,12 +2700,14 @@ CFRunLoopTimerRef CFRunLoopTimerCreate(CFAllocatorRef allocator, CFAbsoluteTime memory->_port = MACH_PORT_NULL; #endif memory->_order = order; - if (fireDate < __CFTSRToAbsoluteTime(0)) { - memory->_fireTSR = 0; - } else if (__CFTSRToAbsoluteTime(LLONG_MAX) < fireDate) { + int64_t now2 = (int64_t)mach_absolute_time(); + CFAbsoluteTime now1 = CFAbsoluteTimeGetCurrent(); + if (fireDate < now1) { + memory->_fireTSR = now2; + } else if (now1 + __CFTSRToTimeInterval(LLONG_MAX) < fireDate) { memory->_fireTSR = LLONG_MAX; } else { - memory->_fireTSR = __CFAbsoluteTimeToTSR(fireDate); + memory->_fireTSR = now2 + __CFTimeIntervalToTSR(fireDate - now1); } if (interval <= 0.0) { memory->_intervalTSR = 0; @@ -2377,22 +2747,26 @@ CFAbsoluteTime CFRunLoopTimerGetNextFireDate(CFRunLoopTimerRef rlt) { result = fireTime; } __CFRunLoopTimerUnlock(rlt); - return __CFTSRToAbsoluteTime(result); + int64_t now2 = (int64_t)mach_absolute_time(); + CFAbsoluteTime now1 = CFAbsoluteTimeGetCurrent(); + return (0 == result) ? 0.0 : now1 + __CFTSRToTimeInterval(result - now2); } void CFRunLoopTimerSetNextFireDate(CFRunLoopTimerRef rlt, CFAbsoluteTime fireDate) { __CFRunLoopTimerFireTSRLock(); - if (fireDate < __CFTSRToAbsoluteTime(0)) { - rlt->_fireTSR = 0; - } else if (__CFTSRToAbsoluteTime(LLONG_MAX) < fireDate) { + int64_t now2 = (int64_t)mach_absolute_time(); + CFAbsoluteTime now1 = CFAbsoluteTimeGetCurrent(); + if (fireDate < now1) { + rlt->_fireTSR = now2; + } else if (now1 + __CFTSRToTimeInterval(LLONG_MAX) < fireDate) { rlt->_fireTSR = LLONG_MAX; } else { - rlt->_fireTSR = __CFAbsoluteTimeToTSR(fireDate); - } - __CFRunLoopTimerFireTSRUnlock(); + rlt->_fireTSR = now2 + __CFTimeIntervalToTSR(fireDate - now1); + } if (rlt->_runLoop != NULL) { __CFRunLoopTimerRescheduleWithAllModes(rlt, rlt->_runLoop); } + __CFRunLoopTimerFireTSRUnlock(); } CFTimeInterval CFRunLoopTimerGetInterval(CFRunLoopTimerRef rlt) { diff --git a/RunLoop.subproj/CFRunLoop.h b/RunLoop.subproj/CFRunLoop.h index 111d291..1b5a132 100644 --- a/RunLoop.subproj/CFRunLoop.h +++ b/RunLoop.subproj/CFRunLoop.h @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -23,7 +21,7 @@ * @APPLE_LICENSE_HEADER_END@ */ /* CFRunLoop.h - Copyright (c) 1998-2003, Apple, Inc. All rights reserved. + Copyright (c) 1998-2005, Apple, Inc. All rights reserved. */ /*! @@ -221,8 +219,8 @@ CF_EXPORT void CFRunLoopRemoveTimer(CFRunLoopRef rl, CFRunLoopTimerRef timer, CF 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 + source. Version 1 sources are available on Mach and Windows, + 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 @@ -251,26 +249,27 @@ CF_EXPORT void CFRunLoopRemoveTimer(CFRunLoopRef rl, CFRunLoopTimerRef timer, CF 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. + the Mach port or Windows HANDLE of a kernel object to + represent the source to the run loop. This function + must return the same result 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 + handling is required for the source. For a version 1 source + on Mach, this function is called when a Mach message arrives + on the source's Mach port, with the 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). + sending). For a version 1 source on Windows the function + is called when the kernel object is in the signaled state. */ typedef struct { CFIndex version; @@ -285,7 +284,6 @@ typedef struct { void (*perform)(void *info); } CFRunLoopSourceContext; -#if defined(__MACH__) typedef struct { CFIndex version; void * info; @@ -294,10 +292,14 @@ typedef struct { CFStringRef (*copyDescription)(const void *info); Boolean (*equal)(const void *info1, const void *info2); CFHashCode (*hash)(const void *info); +#if defined(__MACH__) mach_port_t (*getPort)(void *info); void * (*perform)(void *msg, CFIndex size, CFAllocatorRef allocator, void *info); -} CFRunLoopSourceContext1; +#else + HANDLE (*getPort)(void *info); + void (*perform)(void *info); #endif +} CFRunLoopSourceContext1; /*! @function CFRunLoopSourceGetTypeID diff --git a/RunLoop.subproj/CFRunLoopPriv.h b/RunLoop.subproj/CFRunLoopPriv.h new file mode 100644 index 0000000..60a7a78 --- /dev/null +++ b/RunLoop.subproj/CFRunLoopPriv.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* +*/ + +#include +#include +#include + +typedef struct { + CFIndex version; + void * info; + const void *(*retain)(const void *info); + void (*release)(const void *info); + CFStringRef (*copyDescription)(const void *info); + Boolean (*equal)(const void *info1, const void *info2); + CFHashCode (*hash)(const void *info); + void (*perform)(const struct kevent *kev, void *info); + struct kevent event; +} CFRunLoopSourceContext2; + +// The bits in the flags field in the kevent structure are cleared except for EV_ONESHOT and EV_CLEAR. +// Do not use the udata field of the kevent structure -- that field is smashed by CFRunLoop. +// There is no way to EV_ENABLE or EV_DISABLE a kevent. +// The "autoinvalidation" of EV_ONESHOT is not handled properly by CFRunLoop yet. +// The "autoinvalidation" of EV_DELETE on the last close of a file descriptor is not handled properly by CFRunLoop yet. +// There is no way to reset the state in a kevent (such as clearing the EV_EOF state for fifos). + diff --git a/RunLoop.subproj/CFSocket.c b/RunLoop.subproj/CFSocket.c index 45300be..8635d88 100644 --- a/RunLoop.subproj/CFSocket.c +++ b/RunLoop.subproj/CFSocket.c @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -39,9 +37,15 @@ #include #include "CFInternal.h" #if defined(__WIN32__) -#include -#define EINPROGRESS 36 -//#include +#include +#include +// Careful with remapping these - different WinSock routines return different errors than +// on BSD, so if these are used many places they won't work. +#define EINPROGRESS WSAEINPROGRESS +#ifdef EBADF +#undef EBADF +#endif +#define EBADF WSAENOTSOCK #elif defined(__MACH__) #include #else @@ -52,6 +56,36 @@ #include #endif +// On Mach we use a v0 RunLoopSource to make client callbacks. That source is signalled by a +// separate SocketManager thread who uses select() to watch the sockets' fds. +// +// On Win32 we (primarily) use a v1 RunLoopSource. Code protected by USE_V1_RUN_LOOP_SOURCE currently +// assumes __WIN32__ is defined. The Win32 v1 RunLoopSource uses a Windows event object to be notified +// of socket events like FD_READ, FD_CONNECT, etc, at which point it can immediately make client +// callbacks instead of doing any inter-thread signaling. +// +// Because of the peculiar way that FD_WRITE is signalled (see WSAEventSelect doc in MSDN), we +// could not implement the current CFSocket client write callback semantics on top of the FD_WRITE +// events received by the v1 source. However, because the performance gains on the read side +// were so great with the v1 source, we use a hybrid approach to implement the write side. Read +// callbacks are triggered straightforwardly by FD_READ events. Write callbacks are triggered in +// two ways. Most commonly, as we return to the core run loop we poll a socket's writability +// using select(). If it can accept bytes, we signal our v1 RunLoopSource such that it will be +// immediately fired, and we can make the write callback. Alternatively, if the socket is full, +// we then use the old v0-style approach of notifying the SocketManager thread to listen for +// notification that the socket can accept bytes using select(). Of course these two modes also +// must respect the various write callback settings and autoenabling flags setup by the client. +// The net effect is that we rarely must interact with the SocketMgr thread, which leads to a +// performance win. +// +// Because of this hybrid, we end up needing both a v1 RunLoopSource (to watch the Windows FD_* +// events) and a v0 RunLoopSource (to be signaled from the socket manager). Since our API exports +// a single RunLoopSource that clients may schedule, we hand out the v0 RunLoopSource, and as it +// is scheduled and canceled we install the v1 RunLoopSource in the same modes. +#if defined(__WIN32__) +#define USE_V1_RUN_LOOP_SOURCE +#endif + //#define LOG_CFSOCKET #if !defined(__WIN32__) @@ -79,28 +113,41 @@ CONST_STRING_DECL(__kCFSocketRegistryRequestRunLoopMode, "CFSocketRegistryReques */ static CFSpinLock_t __CFAllSocketsLock = 0; /* controls __CFAllSockets */ static CFMutableDictionaryRef __CFAllSockets = NULL; -static CFSpinLock_t __CFActiveSocketsLock = 0; /* controls __CFRead/WriteSockets and __CFRead/WriteSocketsFds */ +static CFSpinLock_t __CFActiveSocketsLock = 0; /* controls __CFRead/WriteSockets, __CFRead/WriteSocketsFds, __CFSocketManagerThread, and __CFSocketManagerIteration */ +static volatile UInt32 __CFSocketManagerIteration = 0; static CFMutableArrayRef __CFWriteSockets = NULL; static CFMutableArrayRef __CFReadSockets = NULL; static CFMutableDataRef __CFWriteSocketsFds = NULL; static CFMutableDataRef __CFReadSocketsFds = NULL; +#if defined(__WIN32__) +// We need to select on exceptFDs on Win32 to hear of connect failures +static CFMutableDataRef __CFExceptSocketsFds = NULL; +#endif +static CFDataRef zeroLengthData = NULL; -static CFSocketNativeHandle __CFWakeupSocketPair[2] = {0, 0}; +static CFSocketNativeHandle __CFWakeupSocketPair[2] = {INVALID_SOCKET, INVALID_SOCKET}; static void *__CFSocketManagerThread = NULL; -#if defined(__WIN32__) -static Boolean __CFSocketWinSockInitialized = false; -#else +#if !defined(__WIN32__) #define CFSOCKET_USE_SOCKETPAIR #define closesocket(a) close((a)) #define ioctlsocket(a,b,c) ioctl((a),(b),(c)) #endif static CFTypeID __kCFSocketTypeID = _kCFRuntimeNotATypeID; +static void __CFSocketDoCallback(CFSocketRef s, CFDataRef data, CFDataRef address, CFSocketNativeHandle sock); +static void __CFSocketInvalidate(CFSocketRef s, Boolean wakeup); struct __CFSocket { CFRuntimeBase _base; - uint32_t _flags; + struct { + unsigned client:8; // flags set by client (reenable, CloseOnInvalidate) + unsigned disabled:8; // flags marking disabled callbacks + unsigned connected:1; // Are we connected yet? (also true for connectionless sockets) + unsigned writableHint:1; // Did the polling the socket show it to be writable? + unsigned closeSignaled:1; // Have we seen FD_CLOSE? (only used on Win32) + unsigned unused:13; + } _f; CFSpinLock_t _lock; CFSpinLock_t _writeLock; CFSocketNativeHandle _socket; /* immutable */ @@ -109,13 +156,19 @@ struct __CFSocket { CFDataRef _address; CFDataRef _peerAddress; SInt32 _socketSetCount; - CFRunLoopSourceRef _source; + CFRunLoopSourceRef _source0; // v0 RLS, messaged from SocketMgr CFMutableArrayRef _runLoops; CFSocketCallBack _callout; /* immutable */ CFSocketContext _context; /* immutable */ - CFIndex _maxQueueLen; +#if !defined(USE_V1_RUN_LOOP_SOURCE) + CFIndex _maxQueueLen; // queues to pass data from SocketMgr thread CFMutableArrayRef _dataQueue; CFMutableArrayRef _addressQueue; +#else + CFRunLoopSourceRef _source1; // v1 RLS, triggered by _event happenings + HANDLE _event; // used to hear about socket events + long _oldEventMask; // last event mask value set with WSAEventSelect +#endif }; /* Bit 6 in the base reserved bits is used for write-signalled state (mutable) */ @@ -192,6 +245,10 @@ CF_INLINE Boolean __CFSocketIsConnectionOriented(CFSocketRef s) { return (SOCK_STREAM == s->_socketType || SOCK_SEQPACKET == s->_socketType); } +CF_INLINE Boolean __CFSocketIsScheduled(CFSocketRef s) { + return (s->_socketSetCount > 0); +} + CF_INLINE void __CFSocketEstablishAddress(CFSocketRef s) { /* socket should already be locked */ uint8_t name[MAX_SOCKADDR_LEN]; @@ -212,13 +269,32 @@ CF_INLINE void __CFSocketEstablishPeerAddress(CFSocketRef s) { CF_INLINE CFIndex __CFSocketFdGetSize(CFDataRef fdSet) { #if defined(__WIN32__) - fd_set* set = CFDataGetBytePtr(fdSet); + fd_set* set = (fd_set*)CFDataGetBytePtr(fdSet); return set ? set->fd_count : 0; #else return NBBY * CFDataGetLength(fdSet); #endif } +CF_INLINE int __CFSocketLastError(void) { +#if defined(__WIN32__) + return WSAGetLastError(); +#else + return thread_errno(); +#endif +} + +static Boolean __CFNativeSocketIsValid(CFSocketNativeHandle sock) { +#if defined(__WIN32__) + SInt32 errorCode = 0; + int errorSize = sizeof(errorCode); + return !(0 != getsockopt(sock, SOL_SOCKET, SO_ERROR, (void *)&errorCode, &errorSize) && WSAGetLastError() == WSAENOTSOCK); +#else + SInt32 flags = fcntl(sock, F_GETFL, 0); + return !(0 > flags && EBADF == thread_errno()); +#endif +} + CF_INLINE Boolean __CFSocketFdSet(CFSocketNativeHandle sock, CFMutableDataRef fdSet) { /* returns true if a change occurred, false otherwise */ Boolean retval = false; @@ -301,36 +377,169 @@ static SInt32 __CFSocketCreateWakeupSocketPair(void) { if (0 > error) { closesocket(__CFWakeupSocketPair[0]); closesocket(__CFWakeupSocketPair[1]); + __CFWakeupSocketPair[0] = INVALID_SOCKET; + __CFWakeupSocketPair[1] = INVALID_SOCKET; } return error; #endif } -static void __CFSocketInitializeSockets(void) { - UInt32 yes = 1; +#if defined(USE_V1_RUN_LOOP_SOURCE) +// Version 1 RunLoopSources set a mask in a Windows System Event to control what socket activity we +// hear about. Because you can only set the mask as a whole, we must remember the previous value so +// set can make relative changes to it. The way we enable/disable precludes calculating the whole +// mask from scratch from the current state we keep. + +static Boolean __CFSocketSetWholeEventMask(CFSocketRef s, long newMask) { + if (s->_oldEventMask != newMask) { + int err; +#if defined(LOG_CFSOCKET) + fprintf(stdout, "calling WSAEventSelect for socket/event %d/%d with event flags 0x%lx\n", s->_socket, (int)s->_event, newMask); +#endif + err = WSAEventSelect(s->_socket, s->_event, newMask); + CFAssert2(0 == err, __kCFLogAssertion, "%s(): WSAEventSelect failed: %d", __PRETTY_FUNCTION__, WSAGetLastError()); + s->_oldEventMask = newMask; + return TRUE; + } else + return FALSE; +} + +CF_INLINE Boolean __CFSocketSetFDForRead(CFSocketRef s) { + long bitToSet; + // we assume that some read bits are set - all callers have checked this + CFAssert1(0 != __CFSocketReadCallBackType(s), __kCFLogAssertion, "%s(): __CFSocketReadCallBackType is zero", __PRETTY_FUNCTION__); + bitToSet = (__CFSocketReadCallBackType(s) == kCFSocketAcceptCallBack) ? FD_ACCEPT : FD_READ; + return __CFSocketSetWholeEventMask(s, s->_oldEventMask | bitToSet); +} + +CF_INLINE Boolean __CFSocketClearFDForRead(CFSocketRef s) { + long bitToClear; + // we assume that some read bits are set - all callers have checked this + CFAssert1(0 != __CFSocketReadCallBackType(s), __kCFLogAssertion, "%s(): __CFSocketReadCallBackType is zero", __PRETTY_FUNCTION__); + bitToClear = (__CFSocketReadCallBackType(s) == kCFSocketAcceptCallBack) ? FD_ACCEPT : FD_READ; + return __CFSocketSetWholeEventMask(s, s->_oldEventMask & ~bitToClear); +} + +#else // !USE_V1_RUN_LOOP_SOURCE +// Version 0 RunLoopSources set a mask in an FD set to control what socket activity we hear about. +CF_INLINE Boolean __CFSocketSetFDForRead(CFSocketRef s) { + return __CFSocketFdSet(s->_socket, __CFReadSocketsFds); +} + +CF_INLINE Boolean __CFSocketClearFDForRead(CFSocketRef s) { + return __CFSocketFdClr(s->_socket, __CFReadSocketsFds); +} +#endif + +CF_INLINE Boolean __CFSocketSetFDForWrite(CFSocketRef s) { + return __CFSocketFdSet(s->_socket, __CFWriteSocketsFds); +} + +CF_INLINE Boolean __CFSocketClearFDForWrite(CFSocketRef s) { + return __CFSocketFdClr(s->_socket, __CFWriteSocketsFds); +} + +#if defined(USE_V1_RUN_LOOP_SOURCE) +static Boolean __CFSocketCanAcceptBytes(CFSocketRef s) { + struct timeval timeout = {0, 0}; + fd_set set; + int result; + FD_ZERO(&set); + FD_SET(s->_socket, &set); + result = select(s->_socket + 1, NULL, &set, NULL, &timeout); +#if defined(LOG_CFSOCKET) + fprintf(stdout, "polling writability of %d yields %d\n", s->_socket, result); +#endif + return result == 1; +} + +static Boolean __CFSocketHasBytesToRead(CFSocketRef s) { + unsigned long avail; + int err = ioctlsocket(s->_socket, FIONREAD, &avail); + CFAssert3(0 == err, __kCFLogAssertion, "%s(): unexpected error from ioctlsocket(%d, FIONREAD,...): %d", __PRETTY_FUNCTION__, s->_socket, WSAGetLastError()); +#if defined(LOG_CFSOCKET) + fprintf(stdout, "polling readability of %d yields %ld\n", s->_socket, avail); +#endif + return (0 == err) && avail > 0; +} +#endif + #if defined(__WIN32__) - if (!__CFSocketWinSockInitialized) { - WORD versionRequested = MAKEWORD(1, 1); +static Boolean WinSockUsed = FALSE; + +static void __CFSocketInitializeWinSock_Guts(void) { + if (!WinSockUsed) { + WinSockUsed = TRUE; + WORD versionRequested = MAKEWORD(2, 0); WSADATA wsaData; int errorStatus = WSAStartup(versionRequested, &wsaData); if (errorStatus != 0 || LOBYTE(wsaData.wVersion) != LOBYTE(versionRequested) || HIBYTE(wsaData.wVersion) != HIBYTE(versionRequested)) { WSACleanup(); CFLog(0, CFSTR("*** Could not initialize WinSock subsystem!!!")); } - __CFSocketWinSockInitialized = true; } +} + +CF_EXPORT void __CFSocketInitializeWinSock(void) { + __CFSpinLock(&__CFActiveSocketsLock); + __CFSocketInitializeWinSock_Guts(); + __CFSpinUnlock(&__CFActiveSocketsLock); +} + +__private_extern__ void __CFSocketCleanup(void) { + __CFSpinLock(&__CFActiveSocketsLock); + if (NULL != __CFReadSockets) { + CFRelease(__CFWriteSockets); + __CFWriteSockets = NULL; + CFRelease(__CFReadSockets); + __CFReadSockets = NULL; + CFRelease(__CFWriteSocketsFds); + __CFWriteSocketsFds = NULL; + CFRelease(__CFReadSocketsFds); + __CFReadSocketsFds = NULL; + CFRelease(__CFExceptSocketsFds); + __CFExceptSocketsFds = NULL; + CFRelease(zeroLengthData); + zeroLengthData = NULL; + } + if (NULL != __CFAllSockets) { + CFRelease(__CFAllSockets); + __CFAllSockets = NULL; + } + if (INVALID_SOCKET != __CFWakeupSocketPair[0]) { + closesocket(__CFWakeupSocketPair[0]); + __CFWakeupSocketPair[0] = INVALID_SOCKET; + } + if (INVALID_SOCKET != __CFWakeupSocketPair[1]) { + closesocket(__CFWakeupSocketPair[1]); + __CFWakeupSocketPair[1] = INVALID_SOCKET; + } + if (WinSockUsed) { + WSACleanup(); + } + __CFSpinUnlock(&__CFActiveSocketsLock); +} #endif + +// CFNetwork needs to call this, especially for Win32 to get WSAStartup +static void __CFSocketInitializeSockets(void) { __CFWriteSockets = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, NULL); __CFReadSockets = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, NULL); __CFWriteSocketsFds = CFDataCreateMutable(kCFAllocatorSystemDefault, 0); __CFReadSocketsFds = CFDataCreateMutable(kCFAllocatorSystemDefault, 0); + zeroLengthData = CFDataCreateMutable(kCFAllocatorSystemDefault, 0); #if defined(__WIN32__) + __CFSocketInitializeWinSock_Guts(); + // make sure we have space for the count field and the first socket CFDataIncreaseLength(__CFWriteSocketsFds, sizeof(u_int) + sizeof(SOCKET)); CFDataIncreaseLength(__CFReadSocketsFds, sizeof(u_int) + sizeof(SOCKET)); + __CFExceptSocketsFds = CFDataCreateMutable(kCFAllocatorSystemDefault, 0); + CFDataIncreaseLength(__CFExceptSocketsFds, sizeof(u_int) + sizeof(SOCKET)); #endif if (0 > __CFSocketCreateWakeupSocketPair()) { CFLog(0, CFSTR("*** Could not create wakeup socket pair for CFSocket!!!")); } else { + UInt32 yes = 1; /* wakeup sockets must be non-blocking */ ioctlsocket(__CFWakeupSocketPair[0], FIONBIO, &yes); ioctlsocket(__CFWakeupSocketPair[1], FIONBIO, &yes); @@ -348,82 +557,90 @@ static CFRunLoopRef __CFSocketCopyRunLoopToWakeUp(CFSocketRef s) { if (value != rl) rl = NULL; } if (NULL == rl) { /* more than one different rl, so we must pick one */ - for (idx = 0; idx < cnt; idx++) { + /* ideally, this would be a run loop which isn't also in a + * signaled state for this or another source, but that's tricky; + * we pick one that is running in an appropriate mode for this + * source, and from those if possible one that is waiting; then + * we move this run loop to the end of the list to scramble them + * a bit, and always search from the front */ + Boolean foundIt = false, foundBackup = false; + SInt32 foundIdx = 0; + for (idx = 0; !foundIt && idx < cnt; idx++) { CFRunLoopRef value = (CFRunLoopRef)CFArrayGetValueAtIndex(s->_runLoops, idx); CFStringRef currentMode = CFRunLoopCopyCurrentMode(value); - if (NULL != currentMode && CFRunLoopIsWaiting(value) && CFRunLoopContainsSource(value, s->_source, currentMode)) { + if (NULL != currentMode) { + if (CFRunLoopContainsSource(value, s->_source0, currentMode)) { + if (CFRunLoopIsWaiting(value)) { + foundIdx = idx; + foundIt = true; + } else if (!foundBackup) { + foundIdx = idx; + foundBackup = true; + } + } CFRelease(currentMode); - /* 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); } + rl = (CFRunLoopRef)CFArrayGetValueAtIndex(s->_runLoops, foundIdx); + CFRetain(rl); + CFArrayRemoveValueAtIndex(s->_runLoops, foundIdx); CFArrayAppendValue(s->_runLoops, rl); + } else { + CFRetain(rl); } } - if (NULL != rl) CFRetain(rl); return rl; } -static void __CFSocketHandleWrite(CFSocketRef s) { +// If callBackNow, we immediately do client callbacks, else we have to signal a v0 RunLoopSource so the +// callbacks can happen in another thread. +static void __CFSocketHandleWrite(CFSocketRef s, Boolean callBackNow) { SInt32 errorCode = 0; int errorSize = sizeof(errorCode); - CFRunLoopRef rl = NULL; CFOptionFlags writeCallBacksAvailable; if (!CFSocketIsValid(s)) return; - if (0 != getsockopt(s->_socket, SOL_SOCKET, SO_ERROR, &errorCode, &errorSize)) errorCode = 0; + if (0 != getsockopt(s->_socket, SOL_SOCKET, SO_ERROR, (void *)&errorCode, &errorSize)) errorCode = 0; // cast for WinSock bad API #if defined(LOG_CFSOCKET) - if (errorCode) printf("error %d on socket %d\n", errorCode, s->_socket); + if (errorCode) fprintf(stdout, "error %ld 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)) { + if ((s->_f.client & kCFSocketConnectCallBack) != 0) writeCallBacksAvailable &= ~kCFSocketConnectCallBack; + if (!__CFSocketIsValid(s) || ((s->_f.disabled & 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); + fprintf(stdout, "write signaling source for socket %d\n", s->_socket); #endif - rl = __CFSocketCopyRunLoopToWakeUp(s); - __CFSocketUnlock(s); - if (NULL != rl) { - CFRunLoopWakeUp(rl); - CFRelease(rl); + if (callBackNow) { + __CFSocketDoCallback(s, NULL, NULL, 0); + } else { + CFRunLoopRef rl; + CFRunLoopSourceSignal(s->_source0); + rl = __CFSocketCopyRunLoopToWakeUp(s); + __CFSocketUnlock(s); + if (NULL != rl) { + CFRunLoopWakeUp(rl); + CFRelease(rl); + } } } static void __CFSocketHandleRead(CFSocketRef s) { - static CFDataRef zeroLengthData = NULL; - CFRunLoopRef rl = NULL; - if (NULL == zeroLengthData) zeroLengthData = CFDataCreateMutable(kCFAllocatorSystemDefault, 0); + CFDataRef data = NULL, address = NULL; + CFSocketNativeHandle sock = INVALID_SOCKET; 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); + fprintf(stdout, "read %ld bytes on socket %d\n", recvlen, s->_socket); #endif if (0 >= recvlen) { //??? should return error if <0 @@ -449,6 +666,7 @@ static void __CFSocketHandleRead(CFSocketRef s) { if (NULL == address) { address = CFRetain(zeroLengthData); } +#if !defined(USE_V1_RUN_LOOP_SOURCE) if (NULL == s->_dataQueue) { s->_dataQueue = CFArrayCreateMutable(CFGetAllocator(s), 0, &kCFTypeArrayCallBacks); } @@ -459,17 +677,23 @@ static void __CFSocketHandleRead(CFSocketRef s) { 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)) { +#endif // !USE_V1_RUN_LOOP_SOURCE + if (0 < recvlen + && (s->_f.client & kCFSocketDataCallBack) != 0 && (s->_f.disabled & kCFSocketDataCallBack) == 0 + && __CFSocketIsScheduled(s) +#if !defined(USE_V1_RUN_LOOP_SOURCE) + && (0 == s->_maxQueueLen || CFArrayGetCount(s->_dataQueue) < s->_maxQueueLen) +#endif // !USE_V1_RUN_LOOP_SOURCE + ) { __CFSpinLock(&__CFActiveSocketsLock); /* restore socket to fds */ - __CFSocketFdSet(s->_socket, __CFReadSocketsFds); + __CFSocketSetFDForRead(s); __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; + sock = accept(s->_socket, (struct sockaddr *)name, &namelen); if (INVALID_SOCKET == sock) { //??? should return error return; @@ -487,6 +711,7 @@ static void __CFSocketHandleRead(CFSocketRef s) { return; } __CFSocketSetReadSignalled(s); +#if !defined(USE_V1_RUN_LOOP_SOURCE) if (NULL == s->_dataQueue) { s->_dataQueue = CFArrayCreateMutable(CFGetAllocator(s), 0, NULL); } @@ -496,38 +721,74 @@ static void __CFSocketHandleRead(CFSocketRef s) { 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)) { +#endif // !USE_V1_RUN_LOOP_SOURCE + if ((s->_f.client & kCFSocketAcceptCallBack) != 0 && (s->_f.disabled & kCFSocketAcceptCallBack) == 0 + && __CFSocketIsScheduled(s) +#if !defined(USE_V1_RUN_LOOP_SOURCE) + && (0 == s->_maxQueueLen || CFArrayGetCount(s->_dataQueue) < s->_maxQueueLen) +#endif // !USE_V1_RUN_LOOP_SOURCE + ) { __CFSpinLock(&__CFActiveSocketsLock); /* restore socket to fds */ - __CFSocketFdSet(s->_socket, __CFReadSocketsFds); + __CFSocketSetFDForRead(s); __CFSpinUnlock(&__CFActiveSocketsLock); } } else { __CFSocketLock(s); - if (!__CFSocketIsValid(s) || ((s->_flags >> 24) & kCFSocketReadCallBack) != 0) { + if (!__CFSocketIsValid(s) || (s->_f.disabled & kCFSocketReadCallBack) != 0) { __CFSocketUnlock(s); return; } __CFSocketSetReadSignalled(s); } - CFRunLoopSourceSignal(s->_source); #if defined(LOG_CFSOCKET) - printf("read signaling source for socket %d\n", s->_socket); + fprintf(stdout, "read signaling source for socket %d\n", s->_socket); #endif - rl = __CFSocketCopyRunLoopToWakeUp(s); +#if defined(USE_V1_RUN_LOOP_SOURCE) + // since in the v0 case data and sock come from the same queue, only one could be set + CFAssert1(NULL == data || 0 == sock, __kCFLogAssertion, "%s(): both data and sock are set", __PRETTY_FUNCTION__); + __CFSocketDoCallback(s, data, address, sock); // does __CFSocketUnlock(s) + if (NULL != data) CFRelease(data); + if (NULL != address) CFRelease(address); +#else + CFRunLoopSourceSignal(s->_source0); + CFRunLoopRef rl = __CFSocketCopyRunLoopToWakeUp(s); __CFSocketUnlock(s); if (NULL != rl) { CFRunLoopWakeUp(rl); CFRelease(rl); } +#endif // !USE_V1_RUN_LOOP_SOURCE } -static void * __CFSocketManager(void * arg) { +#if defined(LOG_CFSOCKET) +static void __CFSocketWriteSocketList(CFArrayRef sockets, CFDataRef fdSet, Boolean onlyIfSet) { + fd_set *tempfds = (fd_set *)CFDataGetBytePtr(fdSet); + SInt32 idx, cnt; + for (idx = 0, cnt = CFArrayGetCount(sockets); idx < cnt; idx++) { + CFSocketRef s = (CFSocketRef)CFArrayGetValueAtIndex(sockets, idx); + if (FD_ISSET(s->_socket, tempfds)) { + fprintf(stdout, "%d ", s->_socket); + } else if (!onlyIfSet) { + fprintf(stdout, "(%d) ", s->_socket); + } + } +} +#endif + +#ifdef __GNUC__ +__attribute__ ((noreturn)) // mostly interesting for shutting up a warning +#endif +static void __CFSocketManager(void * arg) +{ SInt32 nrfds, maxnrfds, fdentries = 1; + SInt32 rfds, wfds; #if defined(__WIN32__) + fd_set *exceptfds = (fd_set *)CFAllocatorAllocate(kCFAllocatorSystemDefault, fdentries * sizeof(SOCKET) + sizeof(u_int), 0); fd_set *writefds = (fd_set *)CFAllocatorAllocate(kCFAllocatorSystemDefault, fdentries * sizeof(SOCKET) + sizeof(u_int), 0); fd_set *readfds = (fd_set *)CFAllocatorAllocate(kCFAllocatorSystemDefault, fdentries * sizeof(SOCKET) + sizeof(u_int), 0); #else + fd_set *exceptfds = NULL; fd_set *writefds = (fd_set *)CFAllocatorAllocate(kCFAllocatorSystemDefault, fdentries * sizeof(fd_mask), 0); fd_set *readfds = (fd_set *)CFAllocatorAllocate(kCFAllocatorSystemDefault, fdentries * sizeof(fd_mask), 0); #endif @@ -539,38 +800,36 @@ static void * __CFSocketManager(void * arg) { for (;;) { __CFSpinLock(&__CFActiveSocketsLock); + __CFSocketManagerIteration++; #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); - } + fprintf(stdout, "socket manager iteration %lu looking at read sockets ", __CFSocketManagerIteration); + __CFSocketWriteSocketList(__CFReadSockets, __CFReadSocketsFds, FALSE); + if (0 < CFArrayGetCount(__CFWriteSockets)) { + fprintf(stdout, " and write sockets "); + __CFSocketWriteSocketList(__CFWriteSockets, __CFWriteSocketsFds, FALSE); +#if defined(__WIN32__) + fprintf(stdout, " and except sockets "); + __CFSocketWriteSocketList(__CFWriteSockets, __CFExceptSocketsFds, TRUE); +#endif } - printf("\n"); + fprintf(stdout, "\n"); #endif - maxnrfds = MAX(__CFSocketFdGetSize(__CFWriteSocketsFds), __CFSocketFdGetSize(__CFReadSocketsFds)); + rfds = __CFSocketFdGetSize(__CFReadSocketsFds); + wfds = __CFSocketFdGetSize(__CFWriteSocketsFds); + maxnrfds = __CFMax(rfds, wfds); #if defined(__WIN32__) if (maxnrfds > fdentries) { fdentries = maxnrfds; + exceptfds = (fd_set *)CFAllocatorReallocate(kCFAllocatorSystemDefault, exceptfds, fdentries * sizeof(SOCKET) + sizeof(u_int), 0); writefds = (fd_set *)CFAllocatorReallocate(kCFAllocatorSystemDefault, writefds, fdentries * sizeof(SOCKET) + sizeof(u_int), 0); readfds = (fd_set *)CFAllocatorReallocate(kCFAllocatorSystemDefault, readfds, fdentries * sizeof(SOCKET) + sizeof(u_int), 0); } - memset(writefds, 0, fdentries * sizeof(SOCKET) + sizeof(u_int)); - memset(readfds, 0, fdentries * sizeof(SOCKET) + sizeof(u_int)); + memset(exceptfds, 0, fdentries * sizeof(SOCKET) + sizeof(u_int)); + memset(writefds, 0, fdentries * sizeof(SOCKET) + sizeof(u_int)); + memset(readfds, 0, fdentries * sizeof(SOCKET) + sizeof(u_int)); + CFDataGetBytes(__CFExceptSocketsFds, CFRangeMake(0, __CFSocketFdGetSize(__CFExceptSocketsFds) * sizeof(SOCKET) + sizeof(u_int)), (UInt8 *)exceptfds); + CFDataGetBytes(__CFWriteSocketsFds, CFRangeMake(0, wfds * sizeof(SOCKET) + sizeof(u_int)), (UInt8 *)writefds); + CFDataGetBytes(__CFReadSocketsFds, CFRangeMake(0, rfds * sizeof(SOCKET) + sizeof(u_int)), (UInt8 *)readfds); #else if (maxnrfds > fdentries * (int)NFDBITS) { fdentries = (maxnrfds + NFDBITS - 1) / NFDBITS; @@ -578,18 +837,21 @@ static void * __CFSocketManager(void * arg) { 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); + memset(readfds, 0, fdentries * sizeof(fd_mask)); + CFDataGetBytes(__CFWriteSocketsFds, CFRangeMake(0, CFDataGetLength(__CFWriteSocketsFds)), (UInt8 *)writefds); CFDataGetBytes(__CFReadSocketsFds, CFRangeMake(0, CFDataGetLength(__CFReadSocketsFds)), (UInt8 *)readfds); +#endif __CFSpinUnlock(&__CFActiveSocketsLock); - nrfds = select(maxnrfds, readfds, writefds, NULL, NULL); + nrfds = select(maxnrfds, readfds, writefds, exceptfds, NULL); +#if defined(LOG_CFSOCKET) + fprintf(stdout, "socket manager woke from select, ret=%ld\n", nrfds); +#endif if (0 == nrfds) continue; if (0 > nrfds) { - SInt32 selectError = thread_errno(); + SInt32 selectError = __CFSocketLastError(); #if defined(LOG_CFSOCKET) - printf("socket manager received error %d from select\n", selectError); + fprintf(stdout, "socket manager received error %ld from select\n", selectError); #endif if (EBADF == selectError) { CFMutableArrayRef invalidSockets = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks); @@ -597,10 +859,9 @@ static void * __CFSocketManager(void * arg) { 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 (!__CFNativeSocketIsValid(s->_socket)) { #if defined(LOG_CFSOCKET) - printf("socket manager found write socket %d invalid\n", s->_socket); + fprintf(stdout, "socket manager found write socket %d invalid\n", s->_socket); #endif CFArrayAppendValue(invalidSockets, s); } @@ -608,10 +869,9 @@ static void * __CFSocketManager(void * arg) { 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 (!__CFNativeSocketIsValid(s->_socket)) { #if defined(LOG_CFSOCKET) - printf("socket manager found read socket %d invalid\n", s->_socket); + fprintf(stdout, "socket manager found read socket %d invalid\n", s->_socket); #endif CFArrayAppendValue(invalidSockets, s); } @@ -620,7 +880,7 @@ static void * __CFSocketManager(void * arg) { cnt = CFArrayGetCount(invalidSockets); for (idx = 0; idx < cnt; idx++) { - CFSocketInvalidate((CFSocketRef)CFArrayGetValueAtIndex(invalidSockets, idx)); + __CFSocketInvalidate(((CFSocketRef)CFArrayGetValueAtIndex(invalidSockets, idx)), false); } CFRelease(invalidSockets); } @@ -629,7 +889,7 @@ static void * __CFSocketManager(void * arg) { 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]); + fprintf(stdout, "socket manager received %c on wakeup socket\n", buffer[0]); #endif } __CFSpinLock(&__CFActiveSocketsLock); @@ -638,19 +898,53 @@ static void * __CFSocketManager(void * arg) { 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); - } +#if !defined(__WIN32__) + // We might have an new element in __CFWriteSockets that we weren't listening to, + // in which case we must be sure not to test a bit in the fdset that is + // outside our mask size. + Boolean sockInBounds = (0 <= sock && sock < maxnrfds); +#else + // fdset's are arrays, so we don't have that issue above + Boolean sockInBounds = true; +#endif + if (INVALID_SOCKET != sock && sockInBounds) { + if (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); +#if defined(__WIN32__) + fd_set *exfds = (fd_set *)CFDataGetMutableBytePtr(__CFExceptSocketsFds); + FD_CLR(sock, exfds); +#endif + } +#if defined(__WIN32__) + else if (FD_ISSET(sock, exceptfds)) { + // On Win32 connect errors come in on exceptFDs. We treat these as if + // they had come on writeFDs, since the rest of our Unix-based code + // expects that. + CFArrayAppendValue(selectedWriteSockets, s); + fd_set *exfds = (fd_set *)CFDataGetMutableBytePtr(__CFExceptSocketsFds); + FD_CLR(sock, exfds); + } +#endif + } } tempfds = NULL; cnt = CFArrayGetCount(__CFReadSockets); for (idx = 0; idx < cnt; idx++) { CFSocketRef s = (CFSocketRef)CFArrayGetValueAtIndex(__CFReadSockets, idx); CFSocketNativeHandle sock = s->_socket; - if (INVALID_SOCKET != sock && 0 <= sock && sock < maxnrfds && FD_ISSET(sock, readfds)) { +#if !defined(__WIN32__) + // We might have an new element in __CFReadSockets that we weren't listening to, + // in which case we must be sure not to test a bit in the fdset that is + // outside our mask size. + Boolean sockInBounds = (0 <= sock && sock < maxnrfds); +#else + // fdset's are arrays, so we don't have that issue above + Boolean sockInBounds = true; +#endif + if (INVALID_SOCKET != sock && sockInBounds && FD_ISSET(sock, readfds)) { 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); @@ -663,22 +957,21 @@ static void * __CFSocketManager(void * arg) { 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); + fprintf(stdout, "socket manager signaling socket %d for write\n", s->_socket); #endif - __CFSocketHandleWrite(s); + __CFSocketHandleWrite(s, FALSE); } 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); + fprintf(stdout, "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) { @@ -689,7 +982,7 @@ static CFStringRef __CFSocketCopyDescription(CFTypeRef cf) { 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); + CFStringAppendFormat(result, NULL, CFSTR("{valid = %s, type = %d, socket = %d, socket set count = %ld\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->_source0, s->_runLoops); contextInfo = s->_context.info; contextCopyDescription = s->_context.copyDescription; __CFSocketUnlock(s); @@ -767,52 +1060,77 @@ __private_extern__ void CFSocketSetAcceptBacklog(CFSocketRef s, CFIndex limit) { __private_extern__ void CFSocketSetMaximumQueueLength(CFSocketRef s, CFIndex length) { __CFGenericValidateType(s, __kCFSocketTypeID); +#if !defined(USE_V1_RUN_LOOP_SOURCE) __CFSocketLock(s); if (__CFSocketIsValid(s)) { s->_maxQueueLen = length; } __CFSocketUnlock(s); +#endif } CFSocketError CFSocketConnectToAddress(CFSocketRef s, CFDataRef address, CFTimeInterval timeout) { //??? need error handling, retries const uint8_t *name; - SInt32 namelen, result = 0, connect_err = 0, flags; + SInt32 namelen, result = -1, connect_err = 0, select_err = 0; 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); + SInt32 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); +#else + // You can set but not get this flag in WIN32, so assume it was in non-blocking mode. + // The downside is that when we leave this routine we'll leave it non-blocking, + // whether it started that way or not. + if (timeout > 0.0 || timeout < 0.0) ioctlsocket(s->_socket, FIONBIO, &yes); + wasBlocking = false; +#endif result = connect(s->_socket, (struct sockaddr *)name, namelen); + if (result != 0) { + connect_err = __CFSocketLastError(); #if defined(__WIN32__) - if (result != 0 && WSAGetLastError() == WSAEWOULDBLOCK) connect_err = EINPROGRESS; -#else - if (result != 0) connect_err = thread_errno(); + if (connect_err == WSAEWOULDBLOCK) connect_err = EINPROGRESS; #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); +#if !defined(__WIN32__) + fprintf(stdout, "connection attempt returns %ld error %ld on socket %d (flags 0x%lx blocking %d)\n", result, connect_err, s->_socket, flags, wasBlocking); +#else + fprintf(stdout, "connection attempt returns %ld error %ld on socket %d\n", result, connect_err, s->_socket); +#endif #endif if (EINPROGRESS == connect_err && timeout >= 0.0) { /* select on socket */ - CFMutableDataRef fds = CFDataCreateMutable(kCFAllocatorSystemDefault, 0); SInt32 nrfds; + int error_size = sizeof(select_err); struct timeval tv; + CFMutableDataRef fds = CFDataCreateMutable(kCFAllocatorSystemDefault, 0); +#if defined(__WIN32__) + CFDataIncreaseLength(fds , sizeof(u_int) + sizeof(SOCKET)); +#endif __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; + if (nrfds < 0) { + select_err = __CFSocketLastError(); + result = -1; + } else if (nrfds == 0) { + result = -2; + } else { + if (0 != getsockopt(s->_socket, SOL_SOCKET, SO_ERROR, (void *)&select_err, &error_size)) select_err = 0; + result = (select_err == 0) ? 0 : -1; + } CFRelease(fds); #if defined(LOG_CFSOCKET) - printf("timed connection attempt %s on socket %d\n", (result == 0) ? "succeeds" : "fails", s->_socket); + fprintf(stdout, "timed connection attempt %s on socket %d, result %ld, select returns %ld error %ld\n", (result == 0) ? "succeeds" : "fails", s->_socket, result, nrfds, select_err); #endif } if (wasBlocking && (timeout > 0.0 || timeout < 0.0)) ioctlsocket(s->_socket, FIONBIO, &no); @@ -825,13 +1143,12 @@ CFSocketError CFSocketConnectToAddress(CFSocketRef s, CFDataRef address, CFTimeI 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); + fprintf(stdout, "connection attempt continues in background on socket %d\n", s->_socket); #endif } } __CFSocketUnlock(s); //??? should return errno -#endif return result; } @@ -846,9 +1163,10 @@ CFSocketRef CFSocketCreate(CFAllocatorRef allocator, SInt32 protocolFamily, SInt } #if !defined(__WIN32__) if (PF_LOCAL == protocolFamily && 0 >= socketType) socketType = SOCK_STREAM; +#else + /* WinSock needs to be initialized at this point for Windows, before socket() */ + __CFSocketInitializeWinSock(); #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); @@ -880,25 +1198,37 @@ CFSocketRef CFSocketCreateWithNative(CFAllocatorRef allocator, CFSocketNativeHan if (INVALID_SOCKET != sock) __CFSocketSetValid(memory); __CFSocketUnsetWriteSignalled(memory); __CFSocketUnsetReadSignalled(memory); - memory->_flags = ((callBackTypes & (~kCFSocketConnectCallBack)) & (~kCFSocketWriteCallBack)) | kCFSocketCloseOnInvalidate; + memory->_f.client = ((callBackTypes & (~kCFSocketConnectCallBack)) & (~kCFSocketWriteCallBack)) | kCFSocketCloseOnInvalidate; + memory->_f.disabled = 0; + memory->_f.connected = FALSE; + memory->_f.writableHint = FALSE; + memory->_f.closeSignaled = FALSE; 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; + if (INVALID_SOCKET == sock || 0 != getsockopt(sock, SOL_SOCKET, SO_TYPE, (void *)&(memory->_socketType), &typeSize)) memory->_socketType = 0; // cast for WinSock bad API memory->_errorCode = 0; memory->_address = NULL; memory->_peerAddress = NULL; memory->_socketSetCount = 0; - memory->_source = NULL; + memory->_source0 = NULL; if (INVALID_SOCKET != sock) { memory->_runLoops = CFArrayCreateMutable(allocator, 0, NULL); } else { memory->_runLoops = NULL; } memory->_callout = callout; +#if defined(USE_V1_RUN_LOOP_SOURCE) + memory->_event = CreateEvent(NULL, true, false, NULL); + CFAssert1(NULL != memory->_event, __kCFLogAssertion, "%s(): could not create event", __PRETTY_FUNCTION__); + memory->_oldEventMask = 0; + __CFSocketSetWholeEventMask(memory, FD_CLOSE|FD_CONNECT); // always listen for closes, connects + memory->_source1 = NULL; +#else // !USE_V1_RUN_LOOP_SOURCE memory->_dataQueue = NULL; memory->_addressQueue = NULL; memory->_maxQueueLen = 0; +#endif // !USE_V1_RUN_LOOP_SOURCE memory->_context.info = 0; memory->_context.retain = 0; memory->_context.release = 0; @@ -937,39 +1267,75 @@ CFSocketRef CFSocketCreateConnectedToSocketSignature(CFAllocatorRef allocator, c return s; } -void CFSocketInvalidate(CFSocketRef s) { +static void __CFSocketInvalidate(CFSocketRef s, Boolean wakeup) { + UInt32 previousSocketManagerIteration; __CFGenericValidateType(s, __kCFSocketTypeID); +#if defined(LOG_CFSOCKET) + fprintf(stdout, "invalidating socket %d with flags 0x%x disabled 0x%x connected 0x%x wakeup %d\n", s->_socket, s->_f.client, s->_f.disabled, s->_f.connected, wakeup); +#endif CFRetain(s); __CFSpinLock(&__CFAllSocketsLock); __CFSocketLock(s); if (__CFSocketIsValid(s)) { SInt32 idx; - CFRunLoopSourceRef source; + CFRunLoopSourceRef source0; 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); + __CFSocketClearFDForWrite(s); +#if defined(__WIN32__) + __CFSocketFdClr(s->_socket, __CFExceptSocketsFds); +#endif } +#if !defined(USE_V1_RUN_LOOP_SOURCE) + // No need to clear FD's for V1 sources, since we'll just throw the whole event away idx = CFArrayGetFirstIndexOfValue(__CFReadSockets, CFRangeMake(0, CFArrayGetCount(__CFReadSockets)), s); if (0 <= idx) { CFArrayRemoveValueAtIndex(__CFReadSockets, idx); - __CFSocketFdClr(s->_socket, __CFReadSocketsFds); + __CFSocketClearFDForRead(s); } +#endif // !USE_V1_RUN_LOOP_SOURCE + previousSocketManagerIteration = __CFSocketManagerIteration; __CFSpinUnlock(&__CFActiveSocketsLock); +#if 0 + if (wakeup && __CFSocketManagerThread) { + Boolean doneWaiting = false; + uint8_t c = 'i'; +#if defined(LOG_CFSOCKET) + fprintf(stdout, "invalidation wants socket iteration to change from %lu\n", previousSocketManagerIteration); +#endif + send(__CFWakeupSocketPair[0], &c, sizeof(c), 0); +#if !defined(__WIN32__) + while (!doneWaiting) { + __CFSpinLock(&__CFActiveSocketsLock); + if (previousSocketManagerIteration != __CFSocketManagerIteration) doneWaiting = true; +#if defined(LOG_CFSOCKET) + fprintf(stdout, "invalidation comparing socket iteration %lu to previous %lu\n", __CFSocketManagerIteration, previousSocketManagerIteration); +#endif + __CFSpinUnlock(&__CFActiveSocketsLock); + if (!doneWaiting) { + struct timespec ts = {0, 1}; + // ??? depress priority + nanosleep(&ts, NULL); + } + } +#endif + } +#endif CFDictionaryRemoveValue(__CFAllSockets, (void *)(s->_socket)); - if ((s->_flags & kCFSocketCloseOnInvalidate) != 0) closesocket(s->_socket); + if ((s->_f.client & kCFSocketCloseOnInvalidate) != 0) closesocket(s->_socket); s->_socket = INVALID_SOCKET; if (NULL != s->_peerAddress) { CFRelease(s->_peerAddress); s->_peerAddress = NULL; } +#if !defined(USE_V1_RUN_LOOP_SOURCE) if (NULL != s->_dataQueue) { CFRelease(s->_dataQueue); s->_dataQueue = NULL; @@ -979,13 +1345,14 @@ void CFSocketInvalidate(CFSocketRef s) { s->_addressQueue = NULL; } s->_socketSetCount = 0; +#endif // !USE_V1_RUN_LOOP_SOURCE for (idx = CFArrayGetCount(s->_runLoops); idx--;) { CFRunLoopWakeUp((CFRunLoopRef)CFArrayGetValueAtIndex(s->_runLoops, idx)); } CFRelease(s->_runLoops); s->_runLoops = NULL; - source = s->_source; - s->_source = NULL; + source0 = s->_source0; + s->_source0 = NULL; contextInfo = s->_context.info; contextRelease = s->_context.release; s->_context.info = 0; @@ -996,10 +1363,21 @@ void CFSocketInvalidate(CFSocketRef s) { if (NULL != contextRelease) { contextRelease(contextInfo); } - if (NULL != source) { - CFRunLoopSourceInvalidate(source); - CFRelease(source); + if (NULL != source0) { + CFRunLoopSourceInvalidate(source0); + CFRelease(source0); + } +#if defined(USE_V1_RUN_LOOP_SOURCE) + // Important to do the v1 source after the v0 source, since cancelling the v0 source + // references the v1 source (since the v1 source is added/removed from RunLoops as a + // side-effect of the v0 source being added/removed). + if (NULL != s->_source1) { + CFRunLoopSourceInvalidate(s->_source1); + CFRelease(s->_source1); + s->_source1 = NULL; } + CloseHandle(s->_event); +#endif } else { __CFSocketUnlock(s); } @@ -1007,6 +1385,8 @@ void CFSocketInvalidate(CFSocketRef s) { CFRelease(s); } +void CFSocketInvalidate(CFSocketRef s) {__CFSocketInvalidate(s, true);} + Boolean CFSocketIsValid(CFSocketRef s) { __CFGenericValidateType(s, __kCFSocketTypeID); return __CFSocketIsValid(s); @@ -1053,137 +1433,173 @@ __private_extern__ void CFSocketReschedule(CFSocketRef s) { CFOptionFlags CFSocketGetSocketFlags(CFSocketRef s) { __CFGenericValidateType(s, __kCFSocketTypeID); - return (s->_flags & (~(kCFSocketConnectCallBack | (0xff << 24)))); + return s->_f.client; } 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); + fprintf(stdout, "setting flags for socket %d, from 0x%x to 0x%lx\n", s->_socket, s->_f.client, flags); #endif - s->_flags &= (kCFSocketConnectCallBack | (0xff << 24)); - s->_flags |= flags; + s->_f.client = flags; __CFSocketUnlock(s); } void CFSocketDisableCallBacks(CFSocketRef s, CFOptionFlags callBackTypes) { Boolean wakeup = false; - uint8_t c = 'u', readCallBackType; + uint8_t readCallBackType; __CFGenericValidateType(s, __kCFSocketTypeID); __CFSocketLock(s); - if (__CFSocketIsValid(s) && 0 < s->_socketSetCount) { + if (__CFSocketIsValid(s) && __CFSocketIsScheduled(s)) { callBackTypes &= __CFSocketCallBackTypes(s); readCallBackType = __CFSocketReadCallBackType(s); - s->_flags |= (callBackTypes << 24); + s->_f.disabled |= callBackTypes; #if defined(LOG_CFSOCKET) - printf("unscheduling socket %d with flags 0x%x for types 0x%x\n", s->_socket, s->_flags, callBackTypes); + fprintf(stdout, "unscheduling socket %d with flags 0x%x disabled 0x%x connected 0x%x for types 0x%lx\n", s->_socket, s->_f.client, s->_f.disabled, s->_f.connected, callBackTypes); #endif __CFSpinLock(&__CFActiveSocketsLock); - if (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)) { + if ((readCallBackType == kCFSocketAcceptCallBack) || !__CFSocketIsConnectionOriented(s)) s->_f.connected = TRUE; + if (((callBackTypes & kCFSocketWriteCallBack) != 0) || (((callBackTypes & kCFSocketConnectCallBack) != 0) && !s->_f.connected)) { + if (__CFSocketClearFDForWrite(s)) { // do not wake up the socket manager thread if all relevant write callbacks are disabled CFOptionFlags writeCallBacksAvailable = __CFSocketCallBackTypes(s) & (kCFSocketWriteCallBack | kCFSocketConnectCallBack); - if ((s->_flags & kCFSocketConnectCallBack) != 0) writeCallBacksAvailable &= ~kCFSocketConnectCallBack; - if (((s->_flags >> 24) & writeCallBacksAvailable) != writeCallBacksAvailable) wakeup = true; + if (s->_f.connected) writeCallBacksAvailable &= ~kCFSocketConnectCallBack; + if ((s->_f.disabled & writeCallBacksAvailable) != writeCallBacksAvailable) wakeup = true; +#if defined(__WIN32__) + __CFSocketFdClr(s->_socket, __CFExceptSocketsFds); +#endif } } if (readCallBackType != kCFSocketNoCallBack && (callBackTypes & readCallBackType) != 0) { - if (__CFSocketFdClr(s->_socket, __CFReadSocketsFds)) { + if (__CFSocketClearFDForRead(s)) { +#if !defined(USE_V1_RUN_LOOP_SOURCE) // do not wake up the socket manager thread if callback type is read if (readCallBackType != kCFSocketReadCallBack) wakeup = true; +#endif } } __CFSpinUnlock(&__CFActiveSocketsLock); } __CFSocketUnlock(s); - if (wakeup) { - if (NULL == __CFSocketManagerThread) { - __CFSocketManagerThread = __CFStartSimpleThread(__CFSocketManager, 0); - } + if (wakeup && __CFSocketManagerThread) { + uint8_t c = 'u'; 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) { +// "force" means to clear the disabled bits set by DisableCallBacks and always reenable. +// if (!force) we respect those bits, meaning they may stop us from enabling. +// In addition, if !force we assume that the sockets have already been added to the +// __CFReadSockets and __CFWriteSockets arrays. This is true because the callbacks start +// enabled when the CFSocket is created (at which time we enable with force). +// Called with SocketLock held, returns with it released! +void __CFSocketEnableCallBacks(CFSocketRef s, CFOptionFlags callBackTypes, Boolean force, uint8_t wakeupChar) { + Boolean wakeup = FALSE; + if (!callBackTypes) { + __CFSocketUnlock(s); + return; + } + if (__CFSocketIsValid(s) && __CFSocketIsScheduled(s)) { + Boolean turnOnWrite = FALSE, turnOnConnect = FALSE, turnOnRead = FALSE; + uint8_t readCallBackType = __CFSocketReadCallBackType(s); callBackTypes &= __CFSocketCallBackTypes(s); - readCallBackType = __CFSocketReadCallBackType(s); - s->_flags &= ~(callBackTypes << 24); + if (force) s->_f.disabled &= ~callBackTypes; #if defined(LOG_CFSOCKET) - printf("rescheduling socket %d with flags 0x%x for types 0x%x\n", s->_socket, s->_flags, callBackTypes); + fprintf(stdout, "rescheduling socket %d with flags 0x%x disabled 0x%x connected 0x%x for types 0x%lx\n", s->_socket, s->_f.client, s->_f.disabled, s->_f.connected, callBackTypes); #endif - __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; + /* We will wait for connection only for connection-oriented, non-rendezvous sockets that are not already connected. Mark others as already connected. */ + if ((readCallBackType == kCFSocketAcceptCallBack) || !__CFSocketIsConnectionOriented(s)) s->_f.connected = TRUE; + + // First figure out what to turn on + if (s->_f.connected || (callBackTypes & kCFSocketConnectCallBack) == 0) { + // if we want write callbacks and they're not disabled... + if ((callBackTypes & kCFSocketWriteCallBack) != 0 && (s->_f.disabled & kCFSocketWriteCallBack) == 0) turnOnWrite = TRUE; + } else { + // if we want connect callbacks and they're not disabled... + if ((callBackTypes & kCFSocketConnectCallBack) != 0 && (s->_f.disabled & kCFSocketConnectCallBack) == 0) turnOnConnect = TRUE; } - if (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; + // if we want read callbacks and they're not disabled... + if (readCallBackType != kCFSocketNoCallBack && (callBackTypes & readCallBackType) != 0 && (s->_f.disabled & kCFSocketReadCallBack) == 0) turnOnRead = TRUE; + + // Now turn on the callbacks we've determined that we want on + if (turnOnRead || turnOnWrite || turnOnConnect) { + __CFSpinLock(&__CFActiveSocketsLock); +#if defined(USE_V1_RUN_LOOP_SOURCE) + if (turnOnWrite) { + if (force) { + SInt32 idx = CFArrayGetFirstIndexOfValue(__CFWriteSockets, CFRangeMake(0, CFArrayGetCount(__CFWriteSockets)), s); + if (kCFNotFound == idx) CFArrayAppendValue(__CFWriteSockets, s); + } + // If we can tell the socket is writable, just reschedule the v1 source. Else we need + // a real notification from the OS, so enable the SocketManager's listening. + if (__CFSocketCanAcceptBytes(s)) { + SetEvent(s->_event); + s->_f.writableHint = TRUE; + } else { + if (__CFSocketSetFDForWrite(s)) wakeup = true; + } + } + if (turnOnConnect) __CFSocketSetWholeEventMask(s, s->_oldEventMask | FD_CONNECT); + if (turnOnRead) __CFSocketSetFDForRead(s); +#else // !USE_V1_RUN_LOOP_SOURCE + if (turnOnWrite || turnOnConnect) { + if (force) { + SInt32 idx = CFArrayGetFirstIndexOfValue(__CFWriteSockets, CFRangeMake(0, CFArrayGetCount(__CFWriteSockets)), s); + if (kCFNotFound == idx) CFArrayAppendValue(__CFWriteSockets, s); + } + if (__CFSocketSetFDForWrite(s)) wakeup = true; +#if defined(__WIN32__) + if ((callBackTypes & kCFSocketConnectCallBack) != 0 && !s->_f.connected) __CFSocketFdSet(s->_socket, __CFExceptSocketsFds); +#endif + } + if (turnOnRead) { + if (force) { + SInt32 idx = CFArrayGetFirstIndexOfValue(__CFReadSockets, CFRangeMake(0, CFArrayGetCount(__CFReadSockets)), s); + if (kCFNotFound == idx) CFArrayAppendValue(__CFReadSockets, s); + } + if (__CFSocketSetFDForRead(s)) wakeup = true; + } +#endif + if (wakeup && NULL == __CFSocketManagerThread) __CFSocketManagerThread = __CFStartSimpleThread(__CFSocketManager, 0); + __CFSpinUnlock(&__CFActiveSocketsLock); } - __CFSpinUnlock(&__CFActiveSocketsLock); } __CFSocketUnlock(s); - if (wakeup) { - if (NULL == __CFSocketManagerThread) { - __CFSocketManagerThread = __CFStartSimpleThread(__CFSocketManager, 0); - } - send(__CFWakeupSocketPair[0], &c, sizeof(c), 0); - } + if (wakeup) send(__CFWakeupSocketPair[0], &wakeupChar, sizeof(wakeupChar), 0); +} + +void CFSocketEnableCallBacks(CFSocketRef s, CFOptionFlags callBackTypes) { + __CFGenericValidateType(s, __kCFSocketTypeID); + __CFSocketLock(s); + __CFSocketEnableCallBacks(s, callBackTypes, TRUE, 'r'); } 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++; + // Since the v0 source is listened to on the SocketMgr thread, no matter how many modes it + // is added to we just need to enable it there once (and _socketSetCount gives us a refCount + // to know when we can finally disable it). if (1 == s->_socketSetCount) { #if defined(LOG_CFSOCKET) - printf("scheduling socket %d\n", s->_socket); + fprintf(stdout, "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); - } + __CFSocketEnableCallBacks(s, __CFSocketCallBackTypes(s), TRUE, 's'); // unlocks s + } else + __CFSocketUnlock(s); +#if defined(USE_V1_RUN_LOOP_SOURCE) + // Since the v1 source is listened to in rl on this thread, we need to add it to all modes + // the v0 source is added to. + CFRunLoopAddSource(rl, s->_source1, mode); + CFRunLoopWakeUp(rl); +#endif + } else + __CFSocketUnlock(s); } static void __CFSocketCancel(void *info, CFRunLoopRef rl, CFStringRef mode) { @@ -1193,19 +1609,30 @@ static void __CFSocketCancel(void *info, CFRunLoopRef rl, CFStringRef mode) { 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); + __CFSocketClearFDForWrite(s); +#if defined(__WIN32__) + __CFSocketFdClr(s->_socket, __CFExceptSocketsFds); +#endif } +#if !defined(USE_V1_RUN_LOOP_SOURCE) idx = CFArrayGetFirstIndexOfValue(__CFReadSockets, CFRangeMake(0, CFArrayGetCount(__CFReadSockets)), s); if (0 <= idx) { CFArrayRemoveValueAtIndex(__CFReadSockets, idx); - __CFSocketFdClr(s->_socket, __CFReadSocketsFds); + __CFSocketClearFDForRead(s); } +#endif __CFSpinUnlock(&__CFActiveSocketsLock); } +#if defined(USE_V1_RUN_LOOP_SOURCE) + CFRunLoopRemoveSource(rl, s->_source1, mode); + CFRunLoopWakeUp(rl); + if (0 == s->_socketSetCount && s->_socket != INVALID_SOCKET) { + __CFSocketSetWholeEventMask(s, FD_CLOSE|FD_CONNECT); + } +#endif if (NULL != s->_runLoops) { idx = CFArrayGetFirstIndexOfValue(s->_runLoops, CFRangeMake(0, CFArrayGetCount(s->_runLoops)), rl); if (0 <= idx) CFArrayRemoveValueAtIndex(s->_runLoops, idx); @@ -1213,69 +1640,43 @@ static void __CFSocketCancel(void *info, CFRunLoopRef rl, CFStringRef mode) { __CFSocketUnlock(s); } -static void __CFSocketPerform(void *info) { - CFSocketRef s = info; - CFDataRef data = NULL; - CFDataRef address = NULL; - CFSocketNativeHandle sock = INVALID_SOCKET; +// Note: must be called with socket lock held, then returns with it released +// Used by both the v0 and v1 RunLoopSource perform routines +static void __CFSocketDoCallback(CFSocketRef s, CFDataRef data, CFDataRef address, CFSocketNativeHandle sock) { CFSocketCallBack callout = NULL; void *contextInfo = NULL; SInt32 errorCode = 0; - Boolean wakeup = false, readSignalled = false, writeSignalled = false, connectSignalled = false, calledOut = false; - uint8_t c = 'p', readCallBackType, callBackTypes; - CFRunLoopRef rl = NULL; + Boolean readSignalled = false, writeSignalled = false, connectSignalled = false, calledOut = false; + uint8_t readCallBackType, callBackTypes; - __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); + connectSignalled = writeSignalled && !s->_f.connected; __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); + fprintf(stdout, "entering perform for socket %d with read signalled %d write signalled %d connect signalled %d callback types %d\n", s->_socket, readSignalled, writeSignalled, connectSignalled, callBackTypes); #endif - if (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; + s->_f.connected = TRUE; } __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); + fprintf(stdout, "perform calling out error %ld to socket %d\n", errorCode, s->_socket); #endif if (callout) callout(s, kCFSocketConnectCallBack, NULL, &errorCode, contextInfo); calledOut = true; } else { #if defined(LOG_CFSOCKET) - printf("perform calling out connect to socket %d\n", s->_socket); + fprintf(stdout, "perform calling out connect to socket %d\n", s->_socket); #endif if (callout) callout(s, kCFSocketConnectCallBack, NULL, NULL, contextInfo); calledOut = true; @@ -1286,27 +1687,24 @@ static void __CFSocketPerform(void *info) { 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); + fprintf(stdout, "perform calling out data of length %ld to socket %d\n", datalen, s->_socket); #endif if (callout) callout(s, kCFSocketDataCallBack, address, data, contextInfo); calledOut = true; - CFRelease(data); - CFRelease(address); - if (0 == datalen) CFSocketInvalidate(s); + if (0 == datalen) __CFSocketInvalidate(s, true); } } 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); + fprintf(stdout, "perform calling out accept of socket %d to socket %d\n", sock, s->_socket); #endif if (callout) callout(s, kCFSocketAcceptCallBack, address, &sock, contextInfo); calledOut = true; - 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); + fprintf(stdout, "perform calling out read to socket %d\n", s->_socket); #endif if (callout) callout(s, kCFSocketReadCallBack, NULL, NULL, contextInfo); calledOut = true; @@ -1315,43 +1713,141 @@ static void __CFSocketPerform(void *info) { 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); + fprintf(stdout, "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(USE_V1_RUN_LOOP_SOURCE) +static HANDLE __CFSocketGetPort(void *info) { + CFSocketRef s = info; + return s->_event; +} + +static void __CFSocketPerformV1(void *info) { + CFSocketRef s = info; + WSANETWORKEVENTS eventsTranspired; + uint8_t readCallBackType = __CFSocketReadCallBackType(s); + CFOptionFlags callBacksSignalled = 0; + + int err = WSAEnumNetworkEvents(s->_socket, s->_event, &eventsTranspired); + CFAssert2(0 == err, __kCFLogAssertion, "%s(): WSAEnumNetworkEvents failed: %d", __PRETTY_FUNCTION__, WSAGetLastError()); #if defined(LOG_CFSOCKET) - printf("perform signaling source for socket %d with flags 0x%x\n", s->_socket, s->_flags); + fprintf(stdout, "socket %d with flags 0x%x disabled 0x%x connected 0x%x received NetworkEvents 0x%lx\n", s->_socket, s->_f.client, s->_f.disabled, s->_f.connected, eventsTranspired.lNetworkEvents); #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); + // Get these bits cleared before any callouts, just as the SocketMgr thread used to + if (eventsTranspired.lNetworkEvents & FD_READ) { + __CFSpinLock(&__CFActiveSocketsLock); + __CFSocketClearFDForRead(s); + __CFSpinUnlock(&__CFActiveSocketsLock); + } + + if (eventsTranspired.lNetworkEvents & FD_READ || eventsTranspired.lNetworkEvents & FD_ACCEPT) callBacksSignalled |= readCallBackType; + if (eventsTranspired.lNetworkEvents & FD_CONNECT || s->_f.writableHint) callBacksSignalled |= kCFSocketWriteCallBack; + s->_f.writableHint = FALSE; + CFAssert2(0 == (eventsTranspired.lNetworkEvents & FD_WRITE), __kCFLogAssertion, "%s(): WSAEnumNetworkEvents returned unexpected events: %lx", __PRETTY_FUNCTION__, eventsTranspired.lNetworkEvents); + +#if defined(LOG_CFSOCKET) + // I believe all these errors will be re-found in __CFSocketHandleRead and __CFSocketHandleWrite + // so we don't need to check for them here. + if (eventsTranspired.lNetworkEvents & FD_READ && eventsTranspired.iErrorCode[FD_READ_BIT] != 0) + fprintf(stdout, "socket %d has error %d for FD_READ\n", s->_socket, eventsTranspired.iErrorCode[FD_READ_BIT]); + if (eventsTranspired.lNetworkEvents & FD_WRITE && eventsTranspired.iErrorCode[FD_WRITE_BIT] != 0) + fprintf(stdout, "socket %d has error %d for FD_WRITE\n", s->_socket, eventsTranspired.iErrorCode[FD_WRITE_BIT]); + if (eventsTranspired.lNetworkEvents & FD_CLOSE && eventsTranspired.iErrorCode[FD_CLOSE_BIT] != 0) + fprintf(stdout, "socket %d has error %d for FD_CLOSE\n", s->_socket, eventsTranspired.iErrorCode[FD_CLOSE_BIT]); + if (eventsTranspired.lNetworkEvents & FD_CONNECT && eventsTranspired.iErrorCode[FD_CONNECT_BIT] != 0) + fprintf(stdout, "socket %d has error %d for FD_CONNECT\n", s->_socket, eventsTranspired.iErrorCode[FD_CONNECT_BIT]); +#endif + + if (0 != (eventsTranspired.lNetworkEvents & FD_CLOSE)) s->_f.closeSignaled = TRUE; + if (0 != (callBacksSignalled & readCallBackType)) __CFSocketHandleRead(s); + if (0 != (callBacksSignalled & kCFSocketWriteCallBack)) __CFSocketHandleWrite(s, TRUE); + // FD_CLOSE is edge triggered (sent once). FD_READ is level triggered (sent as long as there are + // bytes). Event after we get FD_CLOSE, if there are still bytes to be read we'll keep getting + // FD_READ until the pipe is drained. However, an EOF condition on the socket will -not- + // trigger an FD_READ, so we must be careful not to stall out after the last bytes are read. + // Finally, a client may have already noticed the EOF in the Read callout just done, so we don't + // call him again if the socket has been invalidated. + // All this implies that once we have seen FD_CLOSE, we need to keep checking for EOF on the read + // side to give the client one last callback for that case. + if (__CFSocketIsValid(s) && (eventsTranspired.lNetworkEvents == FD_CLOSE || (s->_f.closeSignaled && !__CFSocketHasBytesToRead(s)))) { + if (readCallBackType != kCFSocketNoCallBack) { + __CFSocketHandleRead(s); + } else if ((__CFSocketCallBackTypes(s) & kCFSocketWriteCallBack) != 0) { + __CFSocketHandleWrite(s, TRUE); } } - 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); + + // Only reenable callbacks that are auto-reenabled + __CFSocketLock(s); + __CFSocketEnableCallBacks(s, callBacksSignalled & s->_f.client, FALSE, 'P'); // unlocks s +} +#endif // USE_V1_RUN_LOOP_SOURCE + +static void __CFSocketPerformV0(void *info) { + CFSocketRef s = info; + CFDataRef data = NULL; + CFDataRef address = NULL; + CFSocketNativeHandle sock = INVALID_SOCKET; + uint8_t readCallBackType, callBackTypes; + CFRunLoopRef rl = NULL; + + __CFSocketLock(s); + if (!__CFSocketIsValid(s)) { + __CFSocketUnlock(s); + return; + } + callBackTypes = __CFSocketCallBackTypes(s); + readCallBackType = __CFSocketReadCallBackType(s); + CFOptionFlags callBacksSignalled = 0; + if (__CFSocketIsReadSignalled(s)) callBacksSignalled |= readCallBackType; + if (__CFSocketIsWriteSignalled(s)) callBacksSignalled |= kCFSocketWriteCallBack; + +#if !defined(USE_V1_RUN_LOOP_SOURCE) + 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); } } - __CFSocketUnlock(s); - if (wakeup) { - if (NULL == __CFSocketManagerThread) { - __CFSocketManagerThread = __CFStartSimpleThread(__CFSocketManager, 0); +#endif + + __CFSocketDoCallback(s, data, address, sock); // does __CFSocketUnlock(s) + if (NULL != data) CFRelease(data); + if (NULL != address) CFRelease(address); + + __CFSocketLock(s); +#if !defined(USE_V1_RUN_LOOP_SOURCE) + if (__CFSocketIsValid(s) && kCFSocketNoCallBack != readCallBackType) { + // if there's still more data, we want to wake back up right away + if ((kCFSocketDataCallBack == readCallBackType || kCFSocketAcceptCallBack == readCallBackType) && NULL != s->_dataQueue && 0 < CFArrayGetCount(s->_dataQueue)) { + CFRunLoopSourceSignal(s->_source0); +#if defined(LOG_CFSOCKET) + fprintf(stdout, "perform short-circuit signaling source for socket %d with flags 0x%x disabled 0x%x connected 0x%x\n", s->_socket, s->_f.client, s->_f.disabled, s->_f.connected); +#endif + rl = __CFSocketCopyRunLoopToWakeUp(s); } - send(__CFWakeupSocketPair[0], &c, sizeof(c), 0); } +#endif + // Only reenable callbacks that are auto-reenabled + __CFSocketEnableCallBacks(s, callBacksSignalled & s->_f.client, FALSE, 'p'); // unlocks s + if (NULL != rl) { CFRunLoopWakeUp(rl); CFRelease(rl); @@ -1363,22 +1859,35 @@ CFRunLoopSourceRef CFSocketCreateRunLoopSource(CFAllocatorRef allocator, CFSocke __CFGenericValidateType(s, __kCFSocketTypeID); __CFSocketLock(s); if (__CFSocketIsValid(s)) { - if (NULL == s->_source) { + if (NULL == s->_source0) { CFRunLoopSourceContext context; +#if defined(USE_V1_RUN_LOOP_SOURCE) + CFRunLoopSourceContext1 context1; + context1.version = 1; + context1.info = s; + context1.retain = CFRetain; + context1.release = CFRelease; + context1.copyDescription = CFCopyDescription; + context1.equal = CFEqual; + context1.hash = CFHash; + context1.getPort = __CFSocketGetPort; + context1.perform = __CFSocketPerformV1; + s->_source1 = CFRunLoopSourceCreate(allocator, order, (CFRunLoopSourceContext*)&context1); +#endif 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.info = s; + context.retain = CFRetain; + context.release = CFRelease; + context.copyDescription = CFCopyDescription; + context.equal = CFEqual; + context.hash = CFHash; context.schedule = __CFSocketSchedule; context.cancel = __CFSocketCancel; - context.perform = __CFSocketPerform; - s->_source = CFRunLoopSourceCreate(allocator, order, &context); + context.perform = __CFSocketPerformV0; + s->_source0 = CFRunLoopSourceCreate(allocator, order, &context); } - CFRetain(s->_source); /* This retain is for the receiver */ - result = s->_source; + CFRetain(s->_source0); /* This retain is for the receiver */ + result = s->_source0; } __CFSocketUnlock(s); return result; @@ -1405,14 +1914,14 @@ CFSocketError CFSocketSendData(CFSocketRef s, CFDataRef address, CFDataRef data, __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)); + setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (void *)&tv, sizeof(tv)); // cast for WinSock bad API 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); + fprintf(stdout, "wrote %ld bytes to socket %d\n", size, s->_socket); #endif __CFSocketWriteUnlock(s); CFRelease(s); @@ -1470,7 +1979,7 @@ static void __CFSocketSendNameRegistryRequest(CFSocketSignature *signature, CFDi static void __CFSocketValidateSignature(const CFSocketSignature *providedSignature, CFSocketSignature *signature, uint16_t defaultPortNumber) { struct sockaddr_in sain, *sainp; - memset(&sin, 0, sizeof(sain)); + memset(&sain, 0, sizeof(sain)); #if !defined(__WIN32__) sain.sin_len = sizeof(sain); #endif diff --git a/RunLoop.subproj/CFSocket.h b/RunLoop.subproj/CFSocket.h index c6853fa..ffc9e2a 100644 --- a/RunLoop.subproj/CFSocket.h +++ b/RunLoop.subproj/CFSocket.h @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -23,12 +21,21 @@ * @APPLE_LICENSE_HEADER_END@ */ /* CFSocket.h - Copyright (c) 1999-2003, Apple, Inc. All rights reserved. + Copyright (c) 1999-2005, Apple, Inc. All rights reserved. */ #if !defined(__COREFOUNDATION_CFSOCKET__) #define __COREFOUNDATION_CFSOCKET__ 1 +#if defined(__WIN32__) +// This needs to be early in the file, before sys/types gets included, or winsock.h complains +// about "fd_set and associated macros have been defined". +#include +typedef SOCKET CFSocketNativeHandle; +#else +typedef int CFSocketNativeHandle; +#endif + #include #include #include @@ -38,12 +45,6 @@ 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 diff --git a/RunLoop.subproj/CFWindowsMessageQueue.c b/RunLoop.subproj/CFWindowsMessageQueue.c index 73dfef9..91ad7d4 100644 --- a/RunLoop.subproj/CFWindowsMessageQueue.c +++ b/RunLoop.subproj/CFWindowsMessageQueue.c @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -32,8 +30,8 @@ #include "CFWindowsMessageQueue.h" #include "CFInternal.h" -extern unsigned long __CFRunLoopGetWindowsMessageQueueMask(CFRunLoopRef rl, CFStringRef mode); -extern void __CFRunLoopSetWindowsMessageQueueMask(CFRunLoopRef rl, unsigned long mask, CFStringRef mode); +extern uint32_t __CFRunLoopGetWindowsMessageQueueMask(CFRunLoopRef rl, CFStringRef mode); +extern void __CFRunLoopSetWindowsMessageQueueMask(CFRunLoopRef rl, uint32_t mask, CFStringRef mode); struct __CFWindowsMessageQueue { CFRuntimeBase _base; @@ -66,33 +64,33 @@ CF_INLINE void __CFWindowsMessageQueueUnlock(CFWindowsMessageQueueRef wmq) { __CFSpinUnlock(&(wmq->_lock)); } -CFTypeID CFWindowsMessageQueueGetTypeID(void) { - return __kCFWindowsMessageQueueTypeID; -} - -Boolean __CFWindowsMessageQueueEqual(CFTypeRef cf1, CFTypeRef cf2) { +static Boolean __CFWindowsMessageQueueEqual(CFTypeRef cf1, CFTypeRef cf2) { CFWindowsMessageQueueRef wmq1 = (CFWindowsMessageQueueRef)cf1; CFWindowsMessageQueueRef wmq2 = (CFWindowsMessageQueueRef)cf2; return (wmq1 == wmq2); } -CFHashCode __CFWindowsMessageQueueHash(CFTypeRef cf) { +static CFHashCode __CFWindowsMessageQueueHash(CFTypeRef cf) { CFWindowsMessageQueueRef wmq = (CFWindowsMessageQueueRef)cf; return (CFHashCode)wmq; } -CFStringRef __CFWindowsMessageQueueCopyDescription(CFTypeRef cf) { +static CFStringRef __CFWindowsMessageQueueCopyDescription(CFTypeRef cf) { +/* Some commentary, possibly as out of date as much of the rest of the file was #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); +/* More commentary, which we don't really need to see with every build #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; @@ -103,25 +101,42 @@ CFAllocatorRef __CFWindowsMessageQueueGetAllocator(CFTypeRef cf) { return wmq->_allocator; } -void __CFWindowsMessageQueueDeallocate(CFTypeRef cf) { +static void __CFWindowsMessageQueueDeallocate(CFTypeRef cf) { CFWindowsMessageQueueRef wmq = (CFWindowsMessageQueueRef)cf; CFAllocatorRef allocator = CFGetAllocator(wmq); CFAllocatorDeallocate(allocator, wmq); CFRelease(allocator); } +static CFTypeID __kCFWindowsMessageQueueTypeID = _kCFRuntimeNotATypeID; + +static const CFRuntimeClass __CFWindowsMessageQueueClass = { + 0, + "CFWindowsMessageQueue", + NULL, // init + NULL, // copy + __CFWindowsMessageQueueDeallocate, + __CFWindowsMessageQueueEqual, + __CFWindowsMessageQueueHash, + NULL, // + __CFWindowsMessageQueueCopyDescription +}; + +__private_extern__ void __CFWindowsMessageQueueInitialize(void) { + __kCFWindowsMessageQueueTypeID = _CFRuntimeRegisterClass(&__CFWindowsMessageQueueClass); +} + +CFTypeID CFWindowsMessageQueueGetTypeID(void) { + return __kCFWindowsMessageQueueTypeID; +} + 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); + UInt32 size = sizeof(struct __CFWindowsMessageQueue) - sizeof(CFRuntimeBase); + memory = (CFWindowsMessageQueueRef)_CFRuntimeCreateInstance(allocator, __kCFWindowsMessageQueueTypeID, size, NULL); if (NULL == memory) { - CFRelease(allocator); - return NULL; + return NULL; } - __CFGenericInitBase(memory, NULL, __kCFWindowsMessageQueueTypeID); - memory->_allocator = allocator; __CFWindowsMessageQueueSetValid(memory); memory->_lock = 0; memory->_mask = mask; @@ -166,7 +181,7 @@ static void __CFWindowsMessageQueueSchedule(void *info, CFRunLoopRef rl, CFStrin CFWindowsMessageQueueRef wmq = info; __CFWindowsMessageQueueLock(wmq); if (__CFWindowsMessageQueueIsValid(wmq)) { - unsigned long mask; + uint32_t mask; CFArrayAppendValue(wmq->_runLoops, rl); mask = __CFRunLoopGetWindowsMessageQueueMask(rl, mode); mask |= wmq->_mask; @@ -210,11 +225,11 @@ CFRunLoopSourceRef CFWindowsMessageQueueCreateRunLoopSource(CFAllocatorRef alloc 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.retain = CFRetain; + context.release = CFRelease; + context.copyDescription = __CFWindowsMessageQueueCopyDescription; + context.equal = __CFWindowsMessageQueueEqual; + context.hash = __CFWindowsMessageQueueHash; context.schedule = __CFWindowsMessageQueueSchedule; context.cancel = __CFWindowsMessageQueueCancel; context.perform = __CFWindowsMessageQueuePerform; diff --git a/RunLoop.subproj/CFWindowsMessageQueue.h b/RunLoop.subproj/CFWindowsMessageQueue.h index 66c24f6..521e7c6 100644 --- a/RunLoop.subproj/CFWindowsMessageQueue.h +++ b/RunLoop.subproj/CFWindowsMessageQueue.h @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -23,7 +21,7 @@ * @APPLE_LICENSE_HEADER_END@ */ /* CFWindowsMessageQueue.h - Copyright (c) 1999-2003, Apple, Inc. All rights reserved. + Copyright (c) 1999-2005, Apple, Inc. All rights reserved. */ #if !defined(__COREFOUNDATION_CFWINDOWSMESSAGEQUEUE__) diff --git a/Stream.subproj/CFConcreteStreams.c b/Stream.subproj/CFConcreteStreams.c new file mode 100644 index 0000000..cbd4ad5 --- /dev/null +++ b/Stream.subproj/CFConcreteStreams.c @@ -0,0 +1,798 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFConcreteStreams.c + Copyright 2000-2002, Apple, Inc. All rights reserved. + Responsibility: Becky Willrich +*/ + +#include +#include +#include +#include +#include +#include +#include +#include "CFStream.h" +#include "CFStreamPriv.h" +#include "CFInternal.h" +#include "CFUtilitiesPriv.h" + +// On Unix, you can schedule an fd with the RunLoop by creating a CFSocket around it. On Win32 +// files and sockets are not interchangeable, and we do cheapo scheduling, where the file is +// always readable and writable until we hit EOF (similar to the way CFData streams are scheduled). +#if !defined(__WIN32__) +#define REAL_FILE_SCHEDULING (1) +#endif + +#define SCHEDULE_AFTER_WRITE (0) +#define SCHEDULE_AFTER_READ (1) +#define APPEND (3) +#define AT_EOF (4) + +/* File callbacks */ +typedef struct { + CFURLRef url; + int fd; +#ifdef REAL_FILE_SCHEDULING + union { + CFSocketRef sock; // socket created once we open and have an fd + CFMutableArrayRef rlArray; // scheduling information prior to open + } rlInfo; // If fd > 0, sock exists. Otherwise, rlArray. +#else + uint16_t scheduled; // ref count of how many times we've been scheduled +#endif + CFOptionFlags flags; + + off_t offset; +} _CFFileStreamContext; + + +CONST_STRING_DECL(kCFStreamPropertyFileCurrentOffset, "kCFStreamPropertyFileCurrentOffset"); + + +#ifdef REAL_FILE_SCHEDULING +static void fileCallBack(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void *data, void *info); + +static void constructCFSocket(_CFFileStreamContext *fileStream, Boolean forRead, struct _CFStream *stream) { + CFSocketContext context = {0, stream, NULL, NULL, CFCopyDescription}; + CFSocketRef sock = CFSocketCreateWithNative(CFGetAllocator(stream), fileStream->fd, forRead ? kCFSocketReadCallBack : kCFSocketWriteCallBack, fileCallBack, &context); + CFSocketSetSocketFlags(sock, 0); + if (fileStream->rlInfo.rlArray) { + CFIndex i, c = CFArrayGetCount(fileStream->rlInfo.rlArray); + CFRunLoopSourceRef src = CFSocketCreateRunLoopSource(CFGetAllocator(stream), sock, 0); + for (i = 0; i+1 < c; i += 2) { + CFRunLoopRef rl = (CFRunLoopRef)CFArrayGetValueAtIndex(fileStream->rlInfo.rlArray, i); + CFStringRef mode = CFArrayGetValueAtIndex(fileStream->rlInfo.rlArray, i+1); + CFRunLoopAddSource(rl, src, mode); + } + CFRelease(fileStream->rlInfo.rlArray); + CFRelease(src); + } + fileStream->rlInfo.sock = sock; +} +#endif + +static Boolean constructFD(_CFFileStreamContext *fileStream, CFStreamError *error, Boolean forRead, struct _CFStream *stream) { + UInt8 path[1024]; + int flags = forRead ? O_RDONLY : (O_CREAT | O_TRUNC | O_WRONLY); +#if defined(__WIN32__) + flags |= (_O_BINARY|_O_NOINHERIT); +#endif + +__CFSetNastyFile(fileStream->url); + + if (CFURLGetFileSystemRepresentation(fileStream->url, TRUE, path, 1024) == FALSE) { + error->error = ENOENT; + error->domain = kCFStreamErrorDomainPOSIX; + return FALSE; + } + if (__CFBitIsSet(fileStream->flags, APPEND)) { + flags |= O_APPEND; + if(_CFExecutableLinkedOnOrAfter(CFSystemVersionPanther)) flags &= ~O_TRUNC; + } + + do { + fileStream->fd = open(path, flags, 0666); + + if (fileStream->fd < 0) + break; + + if ((fileStream->offset != -1) && (lseek(fileStream->fd, fileStream->offset, SEEK_SET) == -1)) + break; + +#ifdef REAL_FILE_SCHEDULING + if (fileStream->rlInfo.rlArray != NULL) { + constructCFSocket(fileStream, forRead, stream); + } +#endif + + return TRUE; + } while (1); + + error->error = errno; + error->domain = kCFStreamErrorDomainPOSIX; + + return FALSE; +} + +static Boolean fileOpen(struct _CFStream *stream, CFStreamError *errorCode, Boolean *openComplete, void *info) { + _CFFileStreamContext *ctxt = (_CFFileStreamContext *)info; + if (ctxt->fd >= 0) { + // Open already occurred + errorCode->error = 0; + *openComplete = TRUE; + return TRUE; + } + Boolean forRead = (CFGetTypeID(stream) == CFReadStreamGetTypeID()); + if (constructFD(ctxt, errorCode, forRead, stream)) { + *openComplete = TRUE; +#ifndef REAL_FILE_SCHEDULING + if (ctxt->scheduled > 0) { + if (forRead) + CFReadStreamSignalEvent((CFReadStreamRef)stream, kCFStreamEventHasBytesAvailable, NULL); + else + CFWriteStreamSignalEvent((CFWriteStreamRef)stream, kCFStreamEventCanAcceptBytes, NULL); + } +#endif + return TRUE; + } else { + return FALSE; + } +} + +__private_extern__ CFIndex fdRead(int fd, UInt8 *buffer, CFIndex bufferLength, CFStreamError *errorCode, Boolean *atEOF) { + CFIndex bytesRead = read(fd, buffer, bufferLength); + if (bytesRead < 0) { + errorCode->error = errno; + errorCode->domain = kCFStreamErrorDomainPOSIX; + return -1; + } else { + *atEOF = (bytesRead == 0) ? TRUE : FALSE; + errorCode->error = 0; + return bytesRead; + } +} + +static CFIndex fileRead(CFReadStreamRef stream, UInt8 *buffer, CFIndex bufferLength, CFStreamError *errorCode, Boolean *atEOF, void *info) { + _CFFileStreamContext *ctxt = (_CFFileStreamContext *)info; + CFIndex result; + result = fdRead(ctxt->fd, buffer, bufferLength, errorCode, atEOF); +#ifdef REAL_FILE_SCHEDULING + if (__CFBitIsSet(ctxt->flags, SCHEDULE_AFTER_READ)) { + __CFBitClear(ctxt->flags, SCHEDULE_AFTER_READ); + if (ctxt->rlInfo.sock) { + CFSocketEnableCallBacks(ctxt->rlInfo.sock, kCFSocketReadCallBack); + } + } +#else + if (*atEOF) + __CFBitSet(ctxt->flags, AT_EOF); + if (ctxt->scheduled > 0 && !*atEOF) { + CFReadStreamSignalEvent(stream, kCFStreamEventHasBytesAvailable, NULL); + } +#endif + return result; +} + +#ifdef REAL_FILE_SCHEDULING +__private_extern__ Boolean fdCanRead(int fd) { + struct timeval timeout = {0, 0}; + fd_set *readSetPtr; + fd_set readSet; + Boolean result; +// fd_set is not a mask in Win32, so checking for an fd that's too big is not relevant + if (fd < FD_SETSIZE) { + FD_ZERO(&readSet); + readSetPtr = &readSet; + } else { + int size = howmany(fd+1, NFDBITS) * sizeof(uint32_t); + uint32_t *fds_bits = (uint32_t *)malloc(size); + memset(fds_bits, 0, size); + readSetPtr = (fd_set *)fds_bits; + } + FD_SET(fd, readSetPtr); + result = (select(fd + 1, readSetPtr, NULL, NULL, &timeout) == 1) ? TRUE : FALSE; + if (readSetPtr != &readSet) { + free(readSetPtr); + } + return result; +} +#endif + +static Boolean fileCanRead(CFReadStreamRef stream, void *info) { + _CFFileStreamContext *ctxt = (_CFFileStreamContext *)info; +#ifdef REAL_FILE_SCHEDULING + return fdCanRead(ctxt->fd); +#else + return !__CFBitIsSet(ctxt->flags, AT_EOF); +#endif +} + +__private_extern__ CFIndex fdWrite(int fd, const UInt8 *buffer, CFIndex bufferLength, CFStreamError *errorCode) { + CFIndex bytesWritten = write(fd, buffer, bufferLength); + if (bytesWritten < 0) { + errorCode->error = errno; + errorCode->domain = kCFStreamErrorDomainPOSIX; + return -1; + } else { + errorCode->error = 0; + return bytesWritten; + } +} + +static CFIndex fileWrite(CFWriteStreamRef stream, const UInt8 *buffer, CFIndex bufferLength, CFStreamError *errorCode, void *info) { + _CFFileStreamContext *fileStream = ((_CFFileStreamContext *)info); + CFIndex result = fdWrite(fileStream->fd, buffer, bufferLength, errorCode); +#ifdef REAL_FILE_SCHEDULING + if (__CFBitIsSet(fileStream->flags, SCHEDULE_AFTER_WRITE)) { + __CFBitClear(fileStream->flags, SCHEDULE_AFTER_WRITE); + if (fileStream->rlInfo.sock) { + CFSocketEnableCallBacks(fileStream->rlInfo.sock, kCFSocketWriteCallBack); + } + } +#else + if (fileStream->scheduled > 0) { + CFWriteStreamSignalEvent(stream, kCFStreamEventCanAcceptBytes, NULL); + } +#endif + return result; +} + +#ifdef REAL_FILE_SCHEDULING +__private_extern__ Boolean fdCanWrite(int fd) { + struct timeval timeout = {0, 0}; + fd_set *writeSetPtr; + fd_set writeSet; + Boolean result; + if (fd < FD_SETSIZE) { + FD_ZERO(&writeSet); + writeSetPtr = &writeSet; + } else { + int size = howmany(fd+1, NFDBITS) * sizeof(uint32_t); + uint32_t *fds_bits = (uint32_t *)malloc(size); + memset(fds_bits, 0, size); + writeSetPtr = (fd_set *)fds_bits; + } + FD_SET(fd, writeSetPtr); + result = (select(fd + 1, NULL, writeSetPtr, NULL, &timeout) == 1) ? TRUE : FALSE; + if (writeSetPtr != &writeSet) { + free(writeSetPtr); + } + return result; +} +#endif + +static Boolean fileCanWrite(CFWriteStreamRef stream, void *info) { +#ifdef REAL_FILE_SCHEDULING + return fdCanWrite(((_CFFileStreamContext *)info)->fd); +#else + return TRUE; +#endif +} + +static void fileClose(struct _CFStream *stream, void *info) { + _CFFileStreamContext *ctxt = (_CFFileStreamContext *)info; + if (ctxt->fd >= 0) { + close(ctxt->fd); + ctxt->fd = -1; +#ifdef REAL_FILE_SCHEDULING + if (ctxt->rlInfo.sock) { + CFSocketInvalidate(ctxt->rlInfo.sock); + CFRelease(ctxt->rlInfo.sock); + ctxt->rlInfo.sock = NULL; + } + } else if (ctxt->rlInfo.rlArray) { + CFRelease(ctxt->rlInfo.rlArray); + ctxt->rlInfo.rlArray = NULL; +#endif + } +} + +#ifdef REAL_FILE_SCHEDULING +static void fileCallBack(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void *data, void *info) { + struct _CFStream *stream = (struct _CFStream *)info; + Boolean isReadStream = (CFGetTypeID(stream) == CFReadStreamGetTypeID()); + _CFFileStreamContext *fileStream = isReadStream ? CFReadStreamGetInfoPointer((CFReadStreamRef)stream) : CFWriteStreamGetInfoPointer((CFWriteStreamRef)stream); + if (type == kCFSocketWriteCallBack) { + __CFBitSet(fileStream->flags, SCHEDULE_AFTER_WRITE); + CFWriteStreamSignalEvent((CFWriteStreamRef)stream, kCFStreamEventCanAcceptBytes, NULL); + } else { + // type == kCFSocketReadCallBack + __CFBitSet(fileStream->flags, SCHEDULE_AFTER_READ); + CFReadStreamSignalEvent((CFReadStreamRef)stream, kCFStreamEventHasBytesAvailable, NULL); + } +} +#endif + +static void fileSchedule(struct _CFStream *stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info) { + _CFFileStreamContext *fileStream = (_CFFileStreamContext *)info; + Boolean isReadStream = (CFGetTypeID(stream) == CFReadStreamGetTypeID()); + CFStreamStatus status = isReadStream ? CFReadStreamGetStatus((CFReadStreamRef)stream) : CFWriteStreamGetStatus((CFWriteStreamRef)stream); + if (fileStream->fd < 0 && status != kCFStreamStatusNotOpen) { + // Stream's already closed or error-ed out + return; + } +#ifdef REAL_FILE_SCHEDULING + if (fileStream->fd < 0) { + if (!fileStream->rlInfo.rlArray) { + fileStream->rlInfo.rlArray = CFArrayCreateMutable(CFGetAllocator(stream), 0, &kCFTypeArrayCallBacks); + } + CFArrayAppendValue(fileStream->rlInfo.rlArray, runLoop); + CFArrayAppendValue(fileStream->rlInfo.rlArray, runLoopMode); + } else { + CFRunLoopSourceRef rlSrc; + if (!fileStream->rlInfo.sock) { + constructCFSocket(fileStream, isReadStream, stream); + } + rlSrc = CFSocketCreateRunLoopSource(CFGetAllocator(stream), fileStream->rlInfo.sock, 0); + CFRunLoopAddSource(runLoop, rlSrc, runLoopMode); + CFRelease(rlSrc); + } +#else + fileStream->scheduled++; + if (fileStream->scheduled == 1 && fileStream->fd > 0 && status == kCFStreamStatusOpen) { + if (isReadStream) + CFReadStreamSignalEvent((CFReadStreamRef)stream, kCFStreamEventHasBytesAvailable, NULL); + else + CFWriteStreamSignalEvent((CFWriteStreamRef)stream, kCFStreamEventCanAcceptBytes, NULL); + } +#endif +} + +static void fileUnschedule(struct _CFStream *stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info) { + _CFFileStreamContext *fileStream = (_CFFileStreamContext *)info; +#ifdef REAL_FILE_SCHEDULING + if (fileStream->fd < 0) { + // Not opened yet + if (fileStream->rlInfo.rlArray) { + CFMutableArrayRef runloops = fileStream->rlInfo.rlArray; + CFIndex i, c; + for (i = 0, c = CFArrayGetCount(runloops); i+1 < c; i += 2) { + if (CFEqual(CFArrayGetValueAtIndex(runloops, i), runLoop) && CFEqual(CFArrayGetValueAtIndex(runloops, i+1), runLoopMode)) { + CFArrayRemoveValueAtIndex(runloops, i); + CFArrayRemoveValueAtIndex(runloops, i); + break; + } + } + } + } else if (fileStream->rlInfo.sock) { + CFRunLoopSourceRef sockSource = CFSocketCreateRunLoopSource(CFGetAllocator(stream), fileStream->rlInfo.sock, 0); + CFRunLoopRemoveSource(runLoop, sockSource, runLoopMode); + CFRelease(sockSource); + } +#else + if (fileStream->scheduled > 0) + fileStream->scheduled--; +#endif +} + +static CFTypeRef fileCopyProperty(struct _CFStream *stream, CFStringRef propertyName, void *info) { + + CFTypeRef result = NULL; + _CFFileStreamContext *fileStream = (_CFFileStreamContext *)info; + + if (CFEqual(propertyName, kCFStreamPropertyFileCurrentOffset)) { + + // NOTE that this does a lseek of 0 from the current location in + // order to populate the offset field which will then be used to + // create the resulting value. + if (!__CFBitIsSet(fileStream->flags, APPEND) && fileStream->fd != -1) { + fileStream->offset = lseek(fileStream->fd, 0, SEEK_CUR); + } + + if (fileStream->offset != -1) { + result = CFNumberCreate(CFGetAllocator((CFTypeRef)stream), kCFNumberSInt64Type, &(fileStream->offset)); + } + } + + return result; +} + +static Boolean fileSetProperty(struct _CFStream *stream, CFStringRef prop, CFTypeRef val, void *info) { + + Boolean result = FALSE; + _CFFileStreamContext *fileStream = (_CFFileStreamContext *)info; + + if (CFEqual(prop, kCFStreamPropertyAppendToFile) && CFGetTypeID(stream) == CFWriteStreamGetTypeID() && + CFWriteStreamGetStatus((CFWriteStreamRef)stream) == kCFStreamStatusNotOpen) + { + if (val == kCFBooleanTrue) { + __CFBitSet(fileStream->flags, APPEND); + fileStream->offset = -1; // Can't offset and append on the stream + } else { + __CFBitClear(fileStream->flags, APPEND); + } + result = TRUE; + } + + else if (CFEqual(prop, kCFStreamPropertyFileCurrentOffset)) { + + if (!__CFBitIsSet(fileStream->flags, APPEND)) + { + result = CFNumberGetValue((CFNumberRef)val, kCFNumberSInt64Type, &(fileStream->offset)); + } + + if ((fileStream->fd != -1) && (lseek(fileStream->fd, fileStream->offset, SEEK_SET) == -1)) { + result = FALSE; + } + } + + return result; +} + +static void *fileCreate(struct _CFStream *stream, void *info) { + _CFFileStreamContext *ctxt = (_CFFileStreamContext *)info; + _CFFileStreamContext *newCtxt = CFAllocatorAllocate(CFGetAllocator(stream), sizeof(_CFFileStreamContext), 0); + if (!newCtxt) return NULL; + newCtxt->url = CFRetain(ctxt->url); + newCtxt->fd = ctxt->fd; +#ifdef REAL_FILE_SCHEDULING + newCtxt->rlInfo.sock = NULL; +#else + newCtxt->scheduled = 0; +#endif + newCtxt->flags = 0; + newCtxt->offset = -1; + return newCtxt; +} + +static void fileFinalize(struct _CFStream *stream, void *info) { + _CFFileStreamContext *ctxt = (_CFFileStreamContext *)info; + if (ctxt->fd > 0) { +#ifdef REAL_FILE_SCHEDULING + if (ctxt->rlInfo.sock) { + CFSocketInvalidate(ctxt->rlInfo.sock); + CFRelease(ctxt->rlInfo.sock); + } +#endif + close(ctxt->fd); +#ifdef REAL_FILE_SCHEDULING + } else if (ctxt->rlInfo.rlArray) { + CFRelease(ctxt->rlInfo.rlArray); +#endif + } + CFRelease(ctxt->url); + CFAllocatorDeallocate(CFGetAllocator(stream), ctxt); +} + +static CFStringRef fileCopyDescription(struct _CFStream *stream, void *info) { + // This needs work + return CFCopyDescription(((_CFFileStreamContext *)info)->url); +} + +/* CFData stream callbacks */ +typedef struct { + CFDataRef data; // Mutable if the stream was constructed writable + const UInt8 *loc; // Current location in the file + Boolean scheduled; + char _padding[3]; +} _CFReadDataStreamContext; + +#define BUF_SIZE 1024 +typedef struct _CFStreamByteBuffer { + UInt8 *bytes; + CFIndex capacity, length; + struct _CFStreamByteBuffer *next; +} _CFStreamByteBuffer; + +typedef struct { + _CFStreamByteBuffer *firstBuf, *currentBuf; + CFAllocatorRef bufferAllocator; + Boolean scheduled; + char _padding[3]; +} _CFWriteDataStreamContext; + +static Boolean readDataOpen(struct _CFStream *stream, CFStreamError *errorCode, Boolean *openComplete, void *info) { + _CFReadDataStreamContext *dataStream = (_CFReadDataStreamContext *)info; + if (dataStream->scheduled) { + if (CFDataGetLength(dataStream->data) != 0) { + CFReadStreamSignalEvent((CFReadStreamRef)stream, kCFStreamEventHasBytesAvailable, NULL); + } else { + CFReadStreamSignalEvent((CFReadStreamRef)stream, kCFStreamEventEndEncountered, NULL); + } + } + errorCode->error = 0; + *openComplete = TRUE; + return TRUE; +} + +static void readDataSchedule(struct _CFStream *stream, CFRunLoopRef rl, CFStringRef rlMode, void *info) { + _CFReadDataStreamContext *dataStream = (_CFReadDataStreamContext *)info; + if (dataStream->scheduled == FALSE) { + dataStream->scheduled = TRUE; + if (CFReadStreamGetStatus((CFReadStreamRef)stream) != kCFStreamStatusOpen) + return; + if (CFDataGetBytePtr(dataStream->data) + CFDataGetLength(dataStream->data) > dataStream->loc) { + CFReadStreamSignalEvent((CFReadStreamRef)stream, kCFStreamEventHasBytesAvailable, NULL); + } else { + CFReadStreamSignalEvent((CFReadStreamRef)stream, kCFStreamEventEndEncountered, NULL); + } + } +} + +static CFIndex dataRead(CFReadStreamRef stream, UInt8 *buffer, CFIndex bufferLength, CFStreamError *error, Boolean *atEOF, void *info) { + _CFReadDataStreamContext *dataCtxt = (_CFReadDataStreamContext *)info; + const UInt8 *bytePtr = CFDataGetBytePtr(dataCtxt->data); + CFIndex length = CFDataGetLength(dataCtxt->data); + CFIndex bytesToCopy = bytePtr + length - dataCtxt->loc; + if (bytesToCopy > bufferLength) { + bytesToCopy = bufferLength; + } + if (bytesToCopy < 0) { + bytesToCopy = 0; + } + if (bytesToCopy != 0) { + memmove(buffer, dataCtxt->loc, bytesToCopy); + dataCtxt->loc += bytesToCopy; + } + error->error = 0; + *atEOF = (dataCtxt->loc < bytePtr + length) ? FALSE : TRUE; + if (dataCtxt->scheduled && !*atEOF) { + CFReadStreamSignalEvent(stream, kCFStreamEventHasBytesAvailable, NULL); + } + return bytesToCopy; +} + +static const UInt8 *dataGetBuffer(CFReadStreamRef stream, CFIndex maxBytesToRead, CFIndex *numBytesRead, CFStreamError *error, Boolean *atEOF, void *info) { + _CFReadDataStreamContext *dataCtxt = (_CFReadDataStreamContext *)info; + const UInt8 *bytes = CFDataGetBytePtr(dataCtxt->data); + if (dataCtxt->loc - bytes > maxBytesToRead) { + *numBytesRead = maxBytesToRead; + *atEOF = FALSE; + } else { + *numBytesRead = dataCtxt->loc - bytes; + *atEOF = TRUE; + } + error->error = 0; + bytes = dataCtxt->loc; + dataCtxt->loc += *numBytesRead; + if (dataCtxt->scheduled && !*atEOF) { + CFReadStreamSignalEvent(stream, kCFStreamEventHasBytesAvailable, NULL); + } + return bytes; +} + +static Boolean dataCanRead(CFReadStreamRef stream, void *info) { + _CFReadDataStreamContext *dataCtxt = (_CFReadDataStreamContext *)info; + return (CFDataGetBytePtr(dataCtxt->data) + CFDataGetLength(dataCtxt->data) > dataCtxt->loc) ? TRUE : FALSE; +} + +static Boolean writeDataOpen(struct _CFStream *stream, CFStreamError *errorCode, Boolean *openComplete, void *info) { + _CFWriteDataStreamContext *dataStream = (_CFWriteDataStreamContext *)info; + if (dataStream->scheduled) { + if (dataStream->bufferAllocator != kCFAllocatorNull || dataStream->currentBuf->capacity > dataStream->currentBuf->length) { + CFWriteStreamSignalEvent((CFWriteStreamRef)stream, kCFStreamEventCanAcceptBytes, NULL); + } else { + CFWriteStreamSignalEvent((CFWriteStreamRef)stream, kCFStreamEventEndEncountered, NULL); + } + } + errorCode->error = 0; + *openComplete = TRUE; + return TRUE; +} + +static void writeDataSchedule(struct _CFStream *stream, CFRunLoopRef rl, CFStringRef rlMode, void *info) { + _CFWriteDataStreamContext *dataStream = (_CFWriteDataStreamContext *)info; + if (dataStream->scheduled == FALSE) { + dataStream->scheduled = TRUE; + if (CFWriteStreamGetStatus((CFWriteStreamRef)stream) != kCFStreamStatusOpen) + return; + if (dataStream->bufferAllocator != kCFAllocatorNull || dataStream->currentBuf->capacity > dataStream->currentBuf->length) { + CFWriteStreamSignalEvent((CFWriteStreamRef)stream, kCFStreamEventCanAcceptBytes, NULL); + } else { + CFWriteStreamSignalEvent((CFWriteStreamRef)stream, kCFStreamEventEndEncountered, NULL); + } + } +} + +static CFIndex dataWrite(CFWriteStreamRef stream, const UInt8 *buffer, CFIndex bufferLength, CFStreamError *errorCode, void *info) { + _CFWriteDataStreamContext *dataStream = (_CFWriteDataStreamContext *)info; + CFIndex result; + CFIndex freeSpace = dataStream->currentBuf->capacity - dataStream->currentBuf->length; + if (dataStream->bufferAllocator == kCFAllocatorNull && bufferLength > freeSpace) { + errorCode->error = ENOMEM; + errorCode->domain = kCFStreamErrorDomainPOSIX; + return -1; + } else { + result = bufferLength; + while (bufferLength > 0) { + CFIndex amountToCopy = (bufferLength > freeSpace) ? freeSpace : bufferLength; + if (freeSpace > 0) { + memmove(dataStream->currentBuf->bytes + dataStream->currentBuf->length, buffer, amountToCopy); + buffer += amountToCopy; + bufferLength -= amountToCopy; + dataStream->currentBuf->length += amountToCopy; + } + if (bufferLength > 0) { + CFIndex bufSize = BUF_SIZE > bufferLength ? BUF_SIZE : bufferLength; + _CFStreamByteBuffer *newBuf = (_CFStreamByteBuffer *)CFAllocatorAllocate(dataStream->bufferAllocator, sizeof(_CFStreamByteBuffer) + bufSize, 0); + newBuf->bytes = (UInt8 *)(newBuf + 1); + newBuf->capacity = bufSize; + newBuf->length = 0; + newBuf->next = NULL; + dataStream->currentBuf->next = newBuf; + dataStream->currentBuf = newBuf; + freeSpace = bufSize; + } + } + errorCode->error = 0; + } + if (dataStream->scheduled && (dataStream->bufferAllocator != kCFAllocatorNull || dataStream->currentBuf->capacity > dataStream->currentBuf->length)) { + CFWriteStreamSignalEvent(stream, kCFStreamEventCanAcceptBytes, NULL); + } + return result; +} + +static Boolean dataCanWrite(CFWriteStreamRef stream, void *info) { + _CFWriteDataStreamContext *dataStream = (_CFWriteDataStreamContext *)info; + if (dataStream->bufferAllocator != kCFAllocatorNull) return TRUE; + if (dataStream->currentBuf->capacity > dataStream->currentBuf->length) return TRUE; + return FALSE; +} + +static CFPropertyListRef dataCopyProperty(struct _CFStream *stream, CFStringRef propertyName, void *info) { + _CFWriteDataStreamContext *dataStream = (_CFWriteDataStreamContext *)info; + CFIndex size = 0; + _CFStreamByteBuffer *buf; + CFAllocatorRef alloc; + UInt8 *bytes, *currByte; + if (!CFEqual(propertyName, kCFStreamPropertyDataWritten)) return NULL; + if (dataStream->bufferAllocator == kCFAllocatorNull) return NULL; + alloc = dataStream->bufferAllocator; + for (buf = dataStream->firstBuf; buf != NULL; buf = buf->next) { + size += buf->length; + } + if (size == 0) return NULL; + bytes = CFAllocatorAllocate(alloc, size, 0); + currByte = bytes; + for (buf = dataStream->firstBuf; buf != NULL; buf = buf->next) { + memmove(currByte, buf->bytes, buf->length); + currByte += buf->length; + } + return CFDataCreateWithBytesNoCopy(alloc, bytes, size, alloc); +} + +static void *readDataCreate(struct _CFStream *stream, void *info) { + _CFReadDataStreamContext *ctxt = (_CFReadDataStreamContext *)info; + _CFReadDataStreamContext *newCtxt = (_CFReadDataStreamContext *)CFAllocatorAllocate(CFGetAllocator(stream), sizeof(_CFReadDataStreamContext), 0); + if (!newCtxt) return NULL; + newCtxt->data = CFRetain(ctxt->data); + newCtxt->loc = CFDataGetBytePtr(newCtxt->data); + newCtxt->scheduled = FALSE; + return (void *)newCtxt; +} + +static void readDataFinalize(struct _CFStream *stream, void *info) { + _CFReadDataStreamContext *ctxt = (_CFReadDataStreamContext *)info; + CFRelease(ctxt->data); + CFAllocatorDeallocate(CFGetAllocator(stream), ctxt); +} + +static CFStringRef readDataCopyDescription(struct _CFStream *stream, void *info) { + return CFCopyDescription(((_CFReadDataStreamContext *)info)->data); +} + +static void *writeDataCreate(struct _CFStream *stream, void *info) { + _CFWriteDataStreamContext *ctxt = (_CFWriteDataStreamContext *)info; + _CFWriteDataStreamContext *newCtxt; + if (ctxt->bufferAllocator != kCFAllocatorNull) { + if (ctxt->bufferAllocator == NULL) ctxt->bufferAllocator = CFAllocatorGetDefault(); + CFRetain(ctxt->bufferAllocator); + newCtxt = CFAllocatorAllocate(CFGetAllocator(stream), sizeof(_CFWriteDataStreamContext) + sizeof(_CFStreamByteBuffer) + BUF_SIZE, 0); + newCtxt->firstBuf = (_CFStreamByteBuffer *)(newCtxt + 1); + newCtxt->firstBuf->bytes = (UInt8 *)(newCtxt->firstBuf + 1); + newCtxt->firstBuf->capacity = BUF_SIZE; + newCtxt->firstBuf->length = 0; + newCtxt->firstBuf->next = NULL; + newCtxt->currentBuf = newCtxt->firstBuf; + newCtxt->bufferAllocator = ctxt->bufferAllocator; + newCtxt->scheduled = FALSE; + } else { + newCtxt = (_CFWriteDataStreamContext *)CFAllocatorAllocate(CFGetAllocator(stream), sizeof(_CFWriteDataStreamContext) + sizeof(_CFStreamByteBuffer), 0); + newCtxt->firstBuf = (_CFStreamByteBuffer *)(newCtxt+1); + newCtxt->firstBuf->bytes = ctxt->firstBuf->bytes; + newCtxt->firstBuf->capacity = ctxt->firstBuf->capacity; + newCtxt->firstBuf->length = 0; + newCtxt->firstBuf->next = NULL; + newCtxt->currentBuf = newCtxt->firstBuf; + newCtxt->bufferAllocator = kCFAllocatorNull; + newCtxt->scheduled = FALSE; + } + return (void *)newCtxt; +} + +static void writeDataFinalize(struct _CFStream *stream, void *info) { + _CFWriteDataStreamContext *ctxt = (_CFWriteDataStreamContext *)info; + if (ctxt->bufferAllocator != kCFAllocatorNull) { + _CFStreamByteBuffer *buf = ctxt->firstBuf->next, *next; + while (buf != NULL) { + next = buf->next; + CFAllocatorDeallocate(ctxt->bufferAllocator, buf); + buf = next; + } + CFRelease(ctxt->bufferAllocator); + } + CFAllocatorDeallocate(CFGetAllocator(stream), ctxt); +} + +static CFStringRef writeDataCopyDescription(struct _CFStream *stream, void *info) { + return CFStringCreateWithFormat(NULL, NULL, CFSTR(""), (int)info); +} + +static const struct _CFStreamCallBacks fileCallBacks = {1, fileCreate, fileFinalize, fileCopyDescription, fileOpen, NULL, fileRead, NULL, fileCanRead, fileWrite, fileCanWrite, fileClose, fileCopyProperty, fileSetProperty, NULL, fileSchedule, fileUnschedule}; + +static struct _CFStream *_CFStreamCreateWithFile(CFAllocatorRef alloc, CFURLRef fileURL, Boolean forReading) { + _CFFileStreamContext fileContext; + CFStringRef scheme = fileURL ? CFURLCopyScheme(fileURL) : NULL; + if (!scheme || !CFEqual(scheme, CFSTR("file"))) { + if (scheme) CFRelease(scheme); + return NULL; + } + CFRelease(scheme); + fileContext.url = fileURL; + fileContext.fd = -1; + return _CFStreamCreateWithConstantCallbacks(alloc, &fileContext, &fileCallBacks, forReading); +} + +CF_EXPORT CFReadStreamRef CFReadStreamCreateWithFile(CFAllocatorRef alloc, CFURLRef fileURL) { + return (CFReadStreamRef)_CFStreamCreateWithFile(alloc, fileURL, TRUE); +} + +CF_EXPORT CFWriteStreamRef CFWriteStreamCreateWithFile(CFAllocatorRef alloc, CFURLRef fileURL) { + return (CFWriteStreamRef)_CFStreamCreateWithFile(alloc, fileURL, FALSE); +} + +static const struct _CFStreamCallBacks readDataCallBacks = {1, readDataCreate, readDataFinalize, readDataCopyDescription, readDataOpen, NULL, dataRead, dataGetBuffer, dataCanRead, NULL, NULL, NULL, NULL, NULL, NULL, readDataSchedule, NULL}; +static const struct _CFStreamCallBacks writeDataCallBacks = {1, writeDataCreate, writeDataFinalize, writeDataCopyDescription, writeDataOpen, NULL, NULL, NULL, NULL, dataWrite, dataCanWrite, NULL, dataCopyProperty, NULL, NULL, writeDataSchedule, NULL}; + +CF_EXPORT CFReadStreamRef CFReadStreamCreateWithBytesNoCopy(CFAllocatorRef alloc, const UInt8 *bytes, CFIndex length, CFAllocatorRef bytesDeallocator) { + _CFReadDataStreamContext ctxt; + CFReadStreamRef result; + ctxt.data = CFDataCreateWithBytesNoCopy(alloc, bytes, length, bytesDeallocator); + result = (CFReadStreamRef)_CFStreamCreateWithConstantCallbacks(alloc, &ctxt, &readDataCallBacks, TRUE); + CFRelease(ctxt.data); + return result; +} + +CFWriteStreamRef CFWriteStreamCreateWithBuffer(CFAllocatorRef alloc, UInt8 *buffer, CFIndex bufferCapacity) { + _CFStreamByteBuffer buf; + _CFWriteDataStreamContext ctxt; + buf.bytes = buffer; + buf.capacity = bufferCapacity; + buf.length = 0; + buf.next = NULL; + ctxt.firstBuf = &buf; + ctxt.currentBuf = ctxt.firstBuf; + ctxt.bufferAllocator = kCFAllocatorNull; + return (CFWriteStreamRef)_CFStreamCreateWithConstantCallbacks(alloc, &ctxt, &writeDataCallBacks, FALSE); +} + +CF_EXPORT CFWriteStreamRef CFWriteStreamCreateWithAllocatedBuffers(CFAllocatorRef alloc, CFAllocatorRef bufferAllocator) { + _CFWriteDataStreamContext ctxt; + ctxt.firstBuf = NULL; + ctxt.currentBuf = NULL; + ctxt.bufferAllocator = bufferAllocator; + return (CFWriteStreamRef)_CFStreamCreateWithConstantCallbacks(alloc, &ctxt, &writeDataCallBacks, FALSE); +} + +#undef BUF_SIZE diff --git a/Stream.subproj/CFSocketStream.c b/Stream.subproj/CFSocketStream.c new file mode 100644 index 0000000..e43ea15 --- /dev/null +++ b/Stream.subproj/CFSocketStream.c @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFSocketStream.c + Copyright 2000-2002, Apple, Inc. All rights reserved. + Responsibility: Jeremy Wyld +*/ +// Original Author: Becky Willrich + + +#include +#include "CFInternal.h" +#include "CFStreamPriv.h" + +#if defined(__WIN32__) +#include +#elif defined(__MACH__) +#endif + +#if defined(__MACH__) +// On Mach these live in CF for historical reasons, even though they are declared in CFNetwork + +const int kCFStreamErrorDomainSSL = 3; +const int kCFStreamErrorDomainSOCKS = 5; + +CONST_STRING_DECL(kCFStreamPropertyShouldCloseNativeSocket, "kCFStreamPropertyShouldCloseNativeSocket") +CONST_STRING_DECL(kCFStreamPropertyAutoErrorOnSystemChange, "kCFStreamPropertyAutoErrorOnSystemChange"); + +CONST_STRING_DECL(kCFStreamPropertySOCKSProxy, "kCFStreamPropertySOCKSProxy") +CONST_STRING_DECL(kCFStreamPropertySOCKSProxyHost, "SOCKSProxy") +CONST_STRING_DECL(kCFStreamPropertySOCKSProxyPort, "SOCKSPort") +CONST_STRING_DECL(kCFStreamPropertySOCKSVersion, "kCFStreamPropertySOCKSVersion") +CONST_STRING_DECL(kCFStreamSocketSOCKSVersion4, "kCFStreamSocketSOCKSVersion4") +CONST_STRING_DECL(kCFStreamSocketSOCKSVersion5, "kCFStreamSocketSOCKSVersion5") +CONST_STRING_DECL(kCFStreamPropertySOCKSUser, "kCFStreamPropertySOCKSUser") +CONST_STRING_DECL(kCFStreamPropertySOCKSPassword, "kCFStreamPropertySOCKSPassword") + +CONST_STRING_DECL(kCFStreamPropertySocketSecurityLevel, "kCFStreamPropertySocketSecurityLevel"); +CONST_STRING_DECL(kCFStreamSocketSecurityLevelNone, "kCFStreamSocketSecurityLevelNone"); +CONST_STRING_DECL(kCFStreamSocketSecurityLevelSSLv2, "kCFStreamSocketSecurityLevelSSLv2"); +CONST_STRING_DECL(kCFStreamSocketSecurityLevelSSLv3, "kCFStreamSocketSecurityLevelSSLv3"); +CONST_STRING_DECL(kCFStreamSocketSecurityLevelTLSv1, "kCFStreamSocketSecurityLevelTLSv1"); +CONST_STRING_DECL(kCFStreamSocketSecurityLevelNegotiatedSSL, "kCFStreamSocketSecurityLevelNegotiatedSSL"); +#endif // !__MACH__ + +// These are duplicated in CFNetwork, who actually externs them in its headers +CONST_STRING_DECL(kCFStreamPropertySocketSSLContext, "kCFStreamPropertySocketSSLContext") +CONST_STRING_DECL(_kCFStreamPropertySocketSecurityAuthenticatesServerCertificate, "_kCFStreamPropertySocketSecurityAuthenticatesServerCertificate"); + + +CF_EXPORT +void _CFSocketStreamSetAuthenticatesServerCertificateDefault(Boolean shouldAuthenticate) { + CFLog(__kCFLogAssertion, CFSTR("_CFSocketStreamSetAuthenticatesServerCertificateDefault(): This call has been deprecated. Use SetProperty(_kCFStreamPropertySocketSecurityAuthenticatesServerCertificate, kCFBooleanTrue/False)\n")); +} + + +/* CF_EXPORT */ Boolean +_CFSocketStreamGetAuthenticatesServerCertificateDefault(void) { + CFLog(__kCFLogAssertion, CFSTR("_CFSocketStreamGetAuthenticatesServerCertificateDefault(): This call has been removed as a security risk. Use security properties on individual streams instead.\n")); + return FALSE; +} + + +/* CF_EXPORT */ void +_CFSocketStreamPairSetAuthenticatesServerCertificate(CFReadStreamRef rStream, CFWriteStreamRef wStream, Boolean authenticates) { + + CFBooleanRef value = (!authenticates ? kCFBooleanFalse : kCFBooleanTrue); + + if (rStream) + CFReadStreamSetProperty(rStream, _kCFStreamPropertySocketSecurityAuthenticatesServerCertificate, value); + else + CFWriteStreamSetProperty(wStream, _kCFStreamPropertySocketSecurityAuthenticatesServerCertificate, value); +} + + +// Flags for dyld loading of libraries. +enum { + kTriedToLoad = 0, + kInitialized +}; + +static struct { + CFSpinLock_t lock; + UInt32 flags; + + const char* const path; +#if defined(__MACH__) +#elif defined(__WIN32__) + HMODULE image; +#endif + + void (*_CFSocketStreamCreatePair)(CFAllocatorRef, CFStringRef, UInt32, CFSocketNativeHandle, const CFSocketSignature*, CFReadStreamRef*, CFWriteStreamRef*); +} CFNetworkSupport = { + 0, + 0x0, + "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/CFNetwork.framework/Versions/A/CFNetwork", + NULL, + NULL +}; + +#define CFNETWORK_CALL(sym, args) ((CFNetworkSupport.sym)args) +#if defined(__MACH__) + #define CFNETWORK_LOAD_SYM(sym) \ + __CFLookupCFNetworkFunction(#sym) +#elif defined(__WIN32__) + #define CFNETWORK_LOAD_SYM(sym) \ + (void *)GetProcAddress(CFNetworkSupport.image, #sym) +#endif + +static void +createPair(CFAllocatorRef alloc, CFStringRef host, UInt32 port, CFSocketNativeHandle sock, const CFSocketSignature* sig, CFReadStreamRef *readStream, CFWriteStreamRef *writeStream) +{ + if (readStream) + *readStream = NULL; + + if (writeStream) + *writeStream = NULL; + + __CFSpinLock(&(CFNetworkSupport.lock)); + + if (!__CFBitIsSet(CFNetworkSupport.flags, kTriedToLoad)) { + + __CFBitSet(CFNetworkSupport.flags, kTriedToLoad); + +#if defined(__MACH__) + CFNetworkSupport._CFSocketStreamCreatePair = CFNETWORK_LOAD_SYM(_CFSocketStreamCreatePair); + +#elif defined(__WIN32__) + + // See if we can already find it in our address space. This let's us check without + // having to specify a filename. +#if defined(DEBUG) + CFNetworkSupport.image = GetModuleHandle("CFNetwork_debug.dll"); +#elif defined(PROFILE) + CFNetworkSupport.image = GetModuleHandle("CFNetwork_profile.dll"); +#endif + // In any case, look for the release version + if (!CFNetworkSupport.image) { + CFNetworkSupport.image = GetModuleHandle("CFNetwork.dll"); + } + + if (!CFNetworkSupport.image) { + // not loaded yet, try to load from the filesystem + char path[MAX_PATH+1]; +#if defined(DEBUG) + strcpy(path, _CFDLLPath()); + strcat(path, "\\CFNetwork_debug.dll"); + CFNetworkSupport.image = LoadLibrary(path); +#elif defined(PROFILE) + strcpy(path, _CFDLLPath()); + strcat(path, "\\CFNetwork_profile.dll"); + CFNetworkSupport.image = LoadLibrary(path); +#endif + if (!CFNetworkSupport.image) { + strcpy(path, _CFDLLPath()); + strcat(path, "\\CFNetwork.dll"); + CFNetworkSupport.image = LoadLibrary(path); + } + } + + if (!CFNetworkSupport.image) + CFLog(__kCFLogAssertion, CFSTR("_CFSocketStreamCreatePair(): failed to dynamically load CFNetwork")); + if (CFNetworkSupport.image) + CFNetworkSupport._CFSocketStreamCreatePair = CFNETWORK_LOAD_SYM(_CFSocketStreamCreatePair); +#else +#warning _CFSocketStreamCreatePair unimplemented +#endif + + if (!CFNetworkSupport._CFSocketStreamCreatePair) + CFLog(__kCFLogAssertion, CFSTR("_CFSocketStreamCreatePair(): failed to dynamically link symbol _CFSocketStreamCreatePair")); + + __CFBitSet(CFNetworkSupport.flags, kInitialized); + } + + __CFSpinUnlock(&(CFNetworkSupport.lock)); + + CFNETWORK_CALL(_CFSocketStreamCreatePair, (alloc, host, port, sock, sig, readStream, writeStream)); +} + + +extern void CFStreamCreatePairWithSocket(CFAllocatorRef alloc, CFSocketNativeHandle sock, CFReadStreamRef *readStream, CFWriteStreamRef *writeStream) { + createPair(alloc, NULL, 0, sock, NULL, readStream, writeStream); +} + +extern void CFStreamCreatePairWithSocketToHost(CFAllocatorRef alloc, CFStringRef host, UInt32 port, CFReadStreamRef *readStream, CFWriteStreamRef *writeStream) { + createPair(alloc, host, port, 0, NULL, readStream, writeStream); +} + +extern void CFStreamCreatePairWithPeerSocketSignature(CFAllocatorRef alloc, const CFSocketSignature* sig, CFReadStreamRef *readStream, CFWriteStreamRef *writeStream) { + createPair(alloc, NULL, 0, 0, sig, readStream, writeStream); +} + diff --git a/Stream.subproj/CFStream.c b/Stream.subproj/CFStream.c new file mode 100644 index 0000000..b02abb5 --- /dev/null +++ b/Stream.subproj/CFStream.c @@ -0,0 +1,1407 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFStream.c + Copyright 2000-2002, Apple, Inc. All rights reserved. + Responsibility: Becky Willrich +*/ + +#include +#include +#include "CFStream.h" +#include "CFInternal.h" +#include "CFStreamPriv.h" +#include + + +enum { + MIN_STATUS_CODE_BIT = 0, + // ..status bits... + MAX_STATUS_CODE_BIT = 4, + + CONSTANT_CALLBACKS = 5, + CALLING_CLIENT = 6, // MUST remain 6 since it's value is used elsewhere. + + HAVE_CLOSED = 7, + + // Values above used to be defined and others may rely on their values + + // Values below should not matter if they are re-ordered or shift + + SHARED_SOURCE +}; + + +/* CALLING_CLIENT really determines whether stream events will be sent to the client immediately, or posted for the next time through the runloop. Since the client may not be prepared for re-entrancy, we must always set/clear this bit around public entry points. -- REW, 9/5/2001 + Also, CALLING_CLIENT is now used from CFFilteredStream.c (which has a copy of the #define above). Really gross. We should find a way to avoid that.... -- REW, 3/27/2002 */ +// Used in CFNetwork too + +/* sSharesSources holds two mappings, one the inverse of the other, between a stream and the + RunLoop+RunLoopMode pair that it's scheduled in. If the stream is scheduled in more than + one loop or mode, it can't share RunLoopSources with others, and is not in this dict. +*/ +static CFSpinLock_t sSourceLock = 0; +static CFMutableDictionaryRef sSharedSources = NULL; + +static CFTypeID __kCFReadStreamTypeID = _kCFRuntimeNotATypeID; +static CFTypeID __kCFWriteStreamTypeID = _kCFRuntimeNotATypeID; + +// Just reads the bits, for those cases where we don't want to go through any callback checking +#define __CFStreamGetStatus(x) __CFBitfieldGetValue((x)->flags, MAX_STATUS_CODE_BIT, MIN_STATUS_CODE_BIT) + +static void _CFStreamSignalEventSynch(void* info); +__private_extern__ CFStreamStatus _CFStreamGetStatus(struct _CFStream *stream); +static Boolean _CFStreamRemoveRunLoopAndModeFromArray(CFMutableArrayRef runLoopsAndModes, CFRunLoopRef rl, CFStringRef mode); +static void _wakeUpRunLoop(struct _CFStream *stream); + +CF_INLINE const struct _CFStreamCallBacks *_CFStreamGetCallBackPtr(struct _CFStream *stream) { + return stream->callBacks; +} + +CF_INLINE void _CFStreamSetStatusCode(struct _CFStream *stream, CFStreamStatus newStatus) { + CFStreamStatus status = __CFStreamGetStatus(stream); + if (((status != kCFStreamStatusClosed) && (status != kCFStreamStatusError)) || + ((status == kCFStreamStatusClosed) && (newStatus == kCFStreamStatusError))) + { + __CFBitfieldSetValue(stream->flags, MAX_STATUS_CODE_BIT, MIN_STATUS_CODE_BIT, newStatus); + } +} + +CF_INLINE void _CFStreamScheduleEvent(struct _CFStream *stream, CFStreamEventType event) { + if (stream->client && (stream->client->when & event) && stream->client->rlSource) { + stream->client->whatToSignal |= event; + CFRunLoopSourceSignal(stream->client->rlSource); + _wakeUpRunLoop(stream); + } +} + + +static CFHashCode __CFStreamHash(CFTypeRef cf) { + return (((int)cf) >> 5); +} + +static CFStringRef __CFStreamCopyDescription(CFTypeRef cf) { + struct _CFStream *stream = (struct _CFStream *)cf; + const struct _CFStreamCallBacks *cb = _CFStreamGetCallBackPtr(stream); + CFStringRef contextDescription; + CFStringRef desc; + if (cb->copyDescription) { + if (cb->version == 0) { + contextDescription = ((CFStringRef(*)(void *))cb->copyDescription)(_CFStreamGetInfoPointer(stream)); + } else { + contextDescription = cb->copyDescription(stream, _CFStreamGetInfoPointer(stream)); + } + } else { + contextDescription = CFStringCreateWithFormat(CFGetAllocator(stream), NULL, CFSTR("info = 0x%lx"), (UInt32)((const void*)_CFStreamGetInfoPointer(stream))); + } + if (CFGetTypeID(cf) == __kCFReadStreamTypeID) { + desc = CFStringCreateWithFormat(CFGetAllocator(stream), NULL, CFSTR("{%@}"), (UInt32)stream, contextDescription); + } else { + desc = CFStringCreateWithFormat(CFGetAllocator(stream), NULL, CFSTR("{%@}"), (UInt32)stream, contextDescription); + } + CFRelease(contextDescription); + return desc; +} + +__private_extern__ void _CFStreamClose(struct _CFStream *stream) { + CFStreamStatus status = _CFStreamGetStatus(stream); + const struct _CFStreamCallBacks *cb = _CFStreamGetCallBackPtr(stream); + if (status == kCFStreamStatusNotOpen || status == kCFStreamStatusClosed || (status == kCFStreamStatusError && __CFBitIsSet(stream->flags, HAVE_CLOSED))) { + // Stream is not open + return; + } + __CFBitSet(stream->flags, HAVE_CLOSED); + __CFBitSet(stream->flags, CALLING_CLIENT); + if (cb->close) { + cb->close(stream, _CFStreamGetInfoPointer(stream)); + } + if (stream->client && stream->client->rlSource) { + + if (!__CFBitIsSet(stream->flags, SHARED_SOURCE)) { + CFRunLoopSourceInvalidate(stream->client->rlSource); + CFRelease(stream->client->rlSource); + stream->client->rlSource = NULL; + } + else { + + CFArrayRef key; + CFMutableArrayRef list; + CFIndex c, i; + + __CFSpinLock(&sSourceLock); + + key = (CFArrayRef)CFDictionaryGetValue(sSharedSources, stream); + list = (CFMutableArrayRef)CFDictionaryGetValue(sSharedSources, key); + + c = CFArrayGetCount(list); + i = CFArrayGetFirstIndexOfValue(list, CFRangeMake(0, c), stream); + if (i != kCFNotFound) { + CFArrayRemoveValueAtIndex(list, i); + c--; + } + + if (!c) { + CFRunLoopRemoveSource((CFRunLoopRef)CFArrayGetValueAtIndex(key, 0), stream->client->rlSource, (CFStringRef)CFArrayGetValueAtIndex(key, 1)); + CFRunLoopSourceInvalidate(stream->client->rlSource); + CFDictionaryRemoveValue(sSharedSources, key); + } + + CFDictionaryRemoveValue(sSharedSources, stream); + + CFRelease(stream->client->rlSource); + stream->client->rlSource = NULL; + __CFBitClear(stream->flags, SHARED_SOURCE); + + __CFSpinUnlock(&sSourceLock); + } + } + _CFStreamSetStatusCode(stream, kCFStreamStatusClosed); + __CFBitClear(stream->flags, CALLING_CLIENT); +} + +//static int numStreamInstances = 0; + +static void __CFStreamDeallocate(CFTypeRef cf) { + struct _CFStream *stream = (struct _CFStream *)cf; + const struct _CFStreamCallBacks *cb = _CFStreamGetCallBackPtr(stream); + CFStreamStatus status = _CFStreamGetStatus(stream); + CFAllocatorRef alloc = CFGetAllocator(stream); +// numStreamInstances --; + if ((status != kCFStreamStatusError || __CFBitIsSet(stream->flags, HAVE_CLOSED)) && status != kCFStreamStatusClosed && status != kCFStreamStatusNotOpen) { + // Close the stream + _CFStreamClose(stream); + } + if (stream->client) { + CFStreamClientContext *cbContext; + cbContext = &(stream->client->cbContext); + if (cbContext->info && cbContext->release) { + cbContext->release(cbContext->info); + } + if (stream->client->rlSource) { + if (!__CFBitIsSet(stream->flags, SHARED_SOURCE)) { + CFRunLoopSourceInvalidate(stream->client->rlSource); + CFRelease(stream->client->rlSource); + stream->client->rlSource = NULL; + } + else { + + CFArrayRef key; + CFMutableArrayRef list; + CFIndex c, i; + + __CFSpinLock(&sSourceLock); + + key = (CFArrayRef)CFDictionaryGetValue(sSharedSources, stream); + list = (CFMutableArrayRef)CFDictionaryGetValue(sSharedSources, key); + + c = CFArrayGetCount(list); + i = CFArrayGetFirstIndexOfValue(list, CFRangeMake(0, c), stream); + if (i != kCFNotFound) { + CFArrayRemoveValueAtIndex(list, i); + c--; + } + + if (!c) { + CFRunLoopRemoveSource((CFRunLoopRef)CFArrayGetValueAtIndex(key, 0), stream->client->rlSource, (CFStringRef)CFArrayGetValueAtIndex(key, 1)); + CFRunLoopSourceInvalidate(stream->client->rlSource); + CFDictionaryRemoveValue(sSharedSources, key); + } + + CFDictionaryRemoveValue(sSharedSources, stream); + + CFRelease(stream->client->rlSource); + stream->client->rlSource = NULL; + __CFBitClear(stream->flags, SHARED_SOURCE); + + __CFSpinUnlock(&sSourceLock); + } + } + if (stream->client->runLoopsAndModes) { + CFRelease(stream->client->runLoopsAndModes); + } + + CFAllocatorDeallocate(alloc, stream->client); + stream->client = NULL; // Just in case finalize, below, calls back in to us + } + if (cb->finalize) { + if (cb->version == 0) { + ((void(*)(void *))cb->finalize)(_CFStreamGetInfoPointer(stream)); + } else { + cb->finalize(stream, _CFStreamGetInfoPointer(stream)); + } + } + if (!__CFBitIsSet(stream->flags, CONSTANT_CALLBACKS)) { + CFAllocatorDeallocate(alloc, (void *)stream->callBacks); + } +} + +static const CFRuntimeClass __CFReadStreamClass = { + 0, + "CFReadStream", + NULL, // init + NULL, // copy + __CFStreamDeallocate, + NULL, + NULL, + NULL, // copyHumanDesc + __CFStreamCopyDescription +}; + +static const CFRuntimeClass __CFWriteStreamClass = { + 0, + "CFWriteStream", + NULL, // init + NULL, // copy + __CFStreamDeallocate, + NULL, + NULL, + NULL, // copyHumanDesc + __CFStreamCopyDescription +}; + +CONST_STRING_DECL(kCFStreamPropertySocketNativeHandle, "kCFStreamPropertySocketNativeHandle") +CONST_STRING_DECL(kCFStreamPropertySocketRemoteHostName, "kCFStreamPropertySocketRemoteHostName") +CONST_STRING_DECL(kCFStreamPropertySocketRemotePortNumber, "kCFStreamPropertySocketRemotePortNumber") +CONST_STRING_DECL(kCFStreamPropertyDataWritten, "kCFStreamPropertyDataWritten") +CONST_STRING_DECL(kCFStreamPropertyAppendToFile, "kCFStreamPropertyAppendToFile") + +__private_extern__ void __CFStreamInitialize(void) { + __kCFReadStreamTypeID = _CFRuntimeRegisterClass(&__CFReadStreamClass); + __kCFWriteStreamTypeID = _CFRuntimeRegisterClass(&__CFWriteStreamClass); +} + + +CF_EXPORT CFTypeID CFReadStreamGetTypeID(void) { + return __kCFReadStreamTypeID; +} + +CF_EXPORT CFTypeID CFWriteStreamGetTypeID(void) { + return __kCFWriteStreamTypeID; +} + +static struct _CFStream *_CFStreamCreate(CFAllocatorRef allocator, Boolean isReadStream) { + struct _CFStream *newStream = (struct _CFStream *)_CFRuntimeCreateInstance(allocator, isReadStream ? __kCFReadStreamTypeID : __kCFWriteStreamTypeID, sizeof(struct _CFStream) - sizeof(CFRuntimeBase), NULL); + if (newStream) { +// numStreamInstances ++; + newStream->flags = 0; + _CFStreamSetStatusCode(newStream, kCFStreamStatusNotOpen); + newStream->error.domain = 0; + newStream->error.error = 0; + newStream->client = NULL; + newStream->info = NULL; + newStream->callBacks = NULL; + } + return newStream; +} + +__private_extern__ struct _CFStream *_CFStreamCreateWithConstantCallbacks(CFAllocatorRef alloc, void *info, const struct _CFStreamCallBacks *cb, Boolean isReading) { + struct _CFStream *newStream; + if (cb->version != 1) return NULL; + newStream = _CFStreamCreate(alloc, isReading); + if (newStream) { + __CFBitSet(newStream->flags, CONSTANT_CALLBACKS); + newStream->callBacks = cb; + if (cb->create) { + newStream->info = cb->create(newStream, info); + } else { + newStream->info = info; + } + } + return newStream; +} + +CF_EXPORT void _CFStreamSetInfoPointer(struct _CFStream *stream, void *info, const struct _CFStreamCallBacks *cb) { + if (info != stream->info) { + if (stream->callBacks->finalize) { + stream->callBacks->finalize(stream, stream->info); + } + if (cb->create) { + stream->info = cb->create(stream, info); + } else { + stream->info = info; + } + } + stream->callBacks = cb; +} + + +CF_EXPORT CFReadStreamRef CFReadStreamCreate(CFAllocatorRef alloc, const CFReadStreamCallBacks *callbacks, void *info) { + struct _CFStream *newStream = _CFStreamCreate(alloc, TRUE); + struct _CFStreamCallBacks *cb; + if (!newStream) return NULL; + cb = CFAllocatorAllocate(alloc, sizeof(struct _CFStreamCallBacks), 0); + if (!cb) { + CFRelease(newStream); + return NULL; + } + if (callbacks->version == 0) { + CFReadStreamCallBacksV0 *cbV0 = (CFReadStreamCallBacksV0 *)callbacks; + CFStreamClientContext *ctxt = (CFStreamClientContext *)info; + newStream->info = ctxt->retain ? (void *)ctxt->retain(ctxt->info) : ctxt->info; + cb->version = 0; + cb->create = (void *(*)(struct _CFStream *, void *))ctxt->retain; + cb->finalize = (void(*)(struct _CFStream *, void *))ctxt->release; + cb->copyDescription = (CFStringRef(*)(struct _CFStream *, void *))ctxt->copyDescription; + cb->open = (Boolean(*)(struct _CFStream *, CFStreamError *, Boolean *, void *))cbV0->open; + cb->openCompleted = (Boolean (*)(struct _CFStream *, CFStreamError *, void *))cbV0->openCompleted; + cb->read = cbV0->read; + cb->getBuffer = cbV0->getBuffer; + cb->canRead = cbV0->canRead; + cb->write = NULL; + cb->canWrite = NULL; + cb->close = (void (*)(struct _CFStream *, void *))cbV0->close; + cb->copyProperty = (CFTypeRef (*)(struct _CFStream *, CFStringRef, void *))cbV0->copyProperty; + cb->setProperty = NULL; + cb->requestEvents = NULL; + cb->schedule = (void (*)(struct _CFStream *, CFRunLoopRef, CFStringRef, void *))cbV0->schedule; + cb->unschedule = (void (*)(struct _CFStream *, CFRunLoopRef, CFStringRef, void *))cbV0->unschedule; + } else { + newStream->info = callbacks->create ? callbacks->create((CFReadStreamRef)newStream, info) : info; + cb->version = 1; + cb->create = (void *(*)(struct _CFStream *, void *))callbacks->create; + cb->finalize = (void(*)(struct _CFStream *, void *))callbacks->finalize; + cb->copyDescription = (CFStringRef(*)(struct _CFStream *, void *))callbacks->copyDescription; + cb->open = (Boolean(*)(struct _CFStream *, CFStreamError *, Boolean *, void *))callbacks->open; + cb->openCompleted = (Boolean (*)(struct _CFStream *, CFStreamError *, void *))callbacks->openCompleted; + cb->read = callbacks->read; + cb->getBuffer = callbacks->getBuffer; + cb->canRead = callbacks->canRead; + cb->write = NULL; + cb->canWrite = NULL; + cb->close = (void (*)(struct _CFStream *, void *))callbacks->close; + cb->copyProperty = (CFTypeRef (*)(struct _CFStream *, CFStringRef, void *))callbacks->copyProperty; + cb->setProperty = (Boolean(*)(struct _CFStream *, CFStringRef, CFTypeRef, void *))callbacks->setProperty; + cb->requestEvents = (void(*)(struct _CFStream *, CFOptionFlags, void *))callbacks->requestEvents; + cb->schedule = (void (*)(struct _CFStream *, CFRunLoopRef, CFStringRef, void *))callbacks->schedule; + cb->unschedule = (void (*)(struct _CFStream *, CFRunLoopRef, CFStringRef, void *))callbacks->unschedule; + } + newStream->callBacks = cb; + return (CFReadStreamRef)newStream; +} + +CF_EXPORT CFWriteStreamRef CFWriteStreamCreate(CFAllocatorRef alloc, const CFWriteStreamCallBacks *callbacks, void *info) { + struct _CFStream *newStream = _CFStreamCreate(alloc, FALSE); + struct _CFStreamCallBacks *cb; + if (!newStream) return NULL; + cb = CFAllocatorAllocate(alloc, sizeof(struct _CFStreamCallBacks), 0); + if (!cb) { + CFRelease(newStream); + return NULL; + } + if (callbacks->version == 0) { + CFWriteStreamCallBacksV0 *cbV0 = (CFWriteStreamCallBacksV0 *)callbacks; + CFStreamClientContext *ctxt = (CFStreamClientContext *)info; + newStream->info = ctxt->retain ? (void *)ctxt->retain(ctxt->info) : ctxt->info; + cb->version = 0; + cb->create = (void *(*)(struct _CFStream *, void *))ctxt->retain; + cb->finalize = (void(*)(struct _CFStream *, void *))ctxt->release; + cb->copyDescription = (CFStringRef(*)(struct _CFStream *, void *))ctxt->copyDescription; + cb->open = (Boolean(*)(struct _CFStream *, CFStreamError *, Boolean *, void *))cbV0->open; + cb->openCompleted = (Boolean (*)(struct _CFStream *, CFStreamError *, void *))cbV0->openCompleted; + cb->read = NULL; + cb->getBuffer = NULL; + cb->canRead = NULL; + cb->write = cbV0->write; + cb->canWrite = cbV0->canWrite; + cb->close = (void (*)(struct _CFStream *, void *))cbV0->close; + cb->copyProperty = (CFTypeRef (*)(struct _CFStream *, CFStringRef, void *))cbV0->copyProperty; + cb->setProperty = NULL; + cb->requestEvents = NULL; + cb->schedule = (void (*)(struct _CFStream *, CFRunLoopRef, CFStringRef, void *))cbV0->schedule; + cb->unschedule = (void (*)(struct _CFStream *, CFRunLoopRef, CFStringRef, void *))cbV0->unschedule; + } else { + cb->version = callbacks->version; + newStream->info = callbacks->create ? callbacks->create((CFWriteStreamRef)newStream, info) : info; + cb->create = (void *(*)(struct _CFStream *, void *))callbacks->create; + cb->finalize = (void(*)(struct _CFStream *, void *))callbacks->finalize; + cb->copyDescription = (CFStringRef(*)(struct _CFStream *, void *))callbacks->copyDescription; + cb->open = (Boolean(*)(struct _CFStream *, CFStreamError *, Boolean *, void *))callbacks->open; + cb->openCompleted = (Boolean (*)(struct _CFStream *, CFStreamError *, void *))callbacks->openCompleted; + cb->read = NULL; + cb->getBuffer = NULL; + cb->canRead = NULL; + cb->write = callbacks->write; + cb->canWrite = callbacks->canWrite; + cb->close = (void (*)(struct _CFStream *, void *))callbacks->close; + cb->copyProperty = (CFTypeRef (*)(struct _CFStream *, CFStringRef, void *))callbacks->copyProperty; + cb->setProperty = (Boolean (*)(struct _CFStream *, CFStringRef, CFTypeRef, void *))callbacks->setProperty; + cb->requestEvents = (void(*)(struct _CFStream *, CFOptionFlags, void *))callbacks->requestEvents; + cb->schedule = (void (*)(struct _CFStream *, CFRunLoopRef, CFStringRef, void *))callbacks->schedule; + cb->unschedule = (void (*)(struct _CFStream *, CFRunLoopRef, CFStringRef, void *))callbacks->unschedule; + } + newStream->callBacks = cb; + return (CFWriteStreamRef)newStream; +} + +static void _CFStreamSignalEventSynch(void* info) { + + struct _CFStream *stream = NULL; + CFOptionFlags eventMask, whatToSignal = 0; + + if (CFGetTypeID((CFTypeRef)info) != CFArrayGetTypeID()) { + stream = (struct _CFStream*)info; + whatToSignal = stream->client->whatToSignal; + stream->client->whatToSignal = 0; + } + else { + + CFMutableArrayRef list = (CFMutableArrayRef)info; + CFIndex c, i; + + __CFSpinLock(&sSourceLock); + + c = CFArrayGetCount(list); + for (i = 0; i < c; i++) { + struct _CFStream* s = (struct _CFStream*)CFArrayGetValueAtIndex(list, i); + if (s->client->whatToSignal) { + stream = s; + whatToSignal = stream->client->whatToSignal; + s->client->whatToSignal = 0; + break; + } + } + + while (i < c) { + struct _CFStream* s = (struct _CFStream*)CFArrayGetValueAtIndex(list, i); + if (s->client->whatToSignal) { + CFRunLoopSourceSignal(s->client->rlSource); + break; + } + i++; + } + + __CFSpinUnlock(&sSourceLock); + } + + if (!stream) + return; + + CFRetain(stream); // In case the client releases the stream while we're still in the for loop.... + + __CFBitSet(stream->flags, CALLING_CLIENT); + + for (eventMask = 1; eventMask <= whatToSignal; eventMask = eventMask << 1) { + if ((eventMask & whatToSignal) && (stream->client->when & eventMask)) { + stream->client->cb(stream, eventMask, stream->client->cbContext.info); + } + } + + __CFBitClear(stream->flags, CALLING_CLIENT); + + CFRelease(stream); +} + +// Largely cribbed from CFSocket.c; find a run loop where our source is scheduled and wake it up. We skip the runloop cycling, so we +// are likely to signal the same run loop over and over again. Don't know if we should worry about that. +static void _wakeUpRunLoop(struct _CFStream *stream) { + CFRunLoopRef rl = NULL; + SInt32 idx, cnt; + CFArrayRef rlArray; + if (!stream->client || !stream->client->runLoopsAndModes) return; + rlArray = stream->client->runLoopsAndModes; + cnt = CFArrayGetCount(rlArray); + if (cnt == 0) return; + if (cnt == 2) { + rl = (CFRunLoopRef)CFArrayGetValueAtIndex(rlArray, 0); + } else { + rl = (CFRunLoopRef)CFArrayGetValueAtIndex(rlArray, 0); + for (idx = 2; NULL != rl && idx < cnt; idx+=2) { + CFRunLoopRef value = (CFRunLoopRef)CFArrayGetValueAtIndex(rlArray, idx); + if (value != rl) rl = NULL; + } + if (NULL == rl) { /* more than one different rl, so we must pick one */ + for (idx = 0; idx < cnt; idx+=2) { + CFRunLoopRef value = (CFRunLoopRef)CFArrayGetValueAtIndex(rlArray, idx); + CFStringRef currentMode = CFRunLoopCopyCurrentMode(value); + if (NULL != currentMode && CFEqual(currentMode, CFArrayGetValueAtIndex(rlArray, idx+1)) && CFRunLoopIsWaiting(value)) { + CFRelease(currentMode); + rl = value; + break; + } + if (NULL != currentMode) CFRelease(currentMode); + } + if (NULL == rl) { /* didn't choose one above, so choose first */ + rl = (CFRunLoopRef)CFArrayGetValueAtIndex(rlArray, 0); + } + } + } + if (NULL != rl && CFRunLoopIsWaiting(rl)) CFRunLoopWakeUp(rl); +} + +__private_extern__ void _CFStreamSignalEvent(struct _CFStream *stream, CFStreamEventType event, CFStreamError *error, Boolean synchronousAllowed) { + // Update our internal status; we must use the primitive __CFStreamGetStatus(), because CFStreamGetStatus() calls us, and we can end up in an infinite loop. + CFStreamStatus status = __CFStreamGetStatus(stream); + // Sanity check the event + if (status == kCFStreamStatusNotOpen) { + // No events allowed; this is almost certainly a bug in the stream's implementation + CFLog(__kCFLogAssertion, CFSTR("Stream 0x%x is sending an event before being opened"), stream); + event = 0; + } else if (status == kCFStreamStatusClosed || status == kCFStreamStatusError) { + // no further events are allowed + event = 0; + } else if (status == kCFStreamStatusAtEnd) { + // Only error events are allowed + event &= kCFStreamEventErrorOccurred; + } else if (status != kCFStreamStatusOpening) { + // cannot send open completed; that happened already + event &= ~kCFStreamEventOpenCompleted; + } + + // Change status if appropriate + if (event & kCFStreamEventOpenCompleted && status == kCFStreamStatusOpening) { + _CFStreamSetStatusCode(stream, kCFStreamStatusOpen); + } + if (event & kCFStreamEventEndEncountered && status < kCFStreamStatusAtEnd) { + _CFStreamSetStatusCode(stream, kCFStreamStatusAtEnd); + } + if (event & kCFStreamEventErrorOccurred) { + stream->error.domain = error->domain; + stream->error.error = error->error; + _CFStreamSetStatusCode(stream, kCFStreamStatusError); + } + + // Now signal any pertinent event + if (stream->client && stream->client->rlSource && (stream->client->when & event)) { + + Boolean signalNow = FALSE; + + stream->client->whatToSignal |= event; + + if (synchronousAllowed && !__CFBitIsSet(stream->flags, CALLING_CLIENT)) { + + CFRunLoopRef rl = CFRunLoopGetCurrent(); + CFStringRef mode = CFRunLoopCopyCurrentMode(rl); + + if (mode) { + if (CFRunLoopContainsSource(rl, stream->client->rlSource, mode)) + signalNow = TRUE; + CFRelease(mode); + } + } + + if (signalNow) { + // Can call out safely right now + _CFStreamSignalEventSynch(stream); + } else { + // Schedule for later delivery + CFRunLoopSourceSignal(stream->client->rlSource); + _wakeUpRunLoop(stream); + } + } +} + +__private_extern__ CFStreamStatus _CFStreamGetStatus(struct _CFStream *stream) { + CFStreamStatus status = __CFStreamGetStatus(stream); + // Status code just represents the value when last we checked; if we were in the middle of doing work at that time, we need to find out if the work has completed, now. If we find out about a status change, we need to inform the client as well, so we call _CFStreamSignalEvent. This will take care of updating our internal status correctly, too. + __CFBitSet(stream->flags, CALLING_CLIENT); + if (status == kCFStreamStatusOpening) { + const struct _CFStreamCallBacks *cb = _CFStreamGetCallBackPtr(stream); + if (cb->openCompleted && cb->openCompleted(stream, &(stream->error), _CFStreamGetInfoPointer(stream))) { + if (stream->error.error == 0) { + status = kCFStreamStatusOpen; + } else { + status = kCFStreamStatusError; + } + _CFStreamSetStatusCode(stream, status); + if (status == kCFStreamStatusOpen) { + _CFStreamScheduleEvent(stream, kCFStreamEventOpenCompleted); + } else { + _CFStreamScheduleEvent(stream, kCFStreamEventErrorOccurred); + } + } + } + __CFBitClear(stream->flags, CALLING_CLIENT); + return status; +} + +CF_EXPORT CFStreamStatus CFReadStreamGetStatus(CFReadStreamRef stream) { + CF_OBJC_FUNCDISPATCH0(__kCFReadStreamTypeID, CFStreamStatus, stream, "streamStatus"); + return _CFStreamGetStatus((struct _CFStream *)stream); +} + +CF_EXPORT CFStreamStatus CFWriteStreamGetStatus(CFWriteStreamRef stream) { + CF_OBJC_FUNCDISPATCH0(__kCFWriteStreamTypeID, CFStreamStatus, stream, "streamStatus"); + return _CFStreamGetStatus((struct _CFStream *)stream); +} + +CF_EXPORT CFStreamError CFReadStreamGetError(CFReadStreamRef stream) { + CF_OBJC_FUNCDISPATCH0(__kCFReadStreamTypeID, CFStreamError, stream, "_cfStreamError"); + return ((struct _CFStream *)stream)->error; +} + +CF_EXPORT CFStreamError CFWriteStreamGetError(CFWriteStreamRef stream) { + CF_OBJC_FUNCDISPATCH0(__kCFWriteStreamTypeID, CFStreamError, stream, "_cfStreamError"); + return ((struct _CFStream *)stream)->error; +} + +__private_extern__ Boolean _CFStreamOpen(struct _CFStream *stream) { + const struct _CFStreamCallBacks *cb = _CFStreamGetCallBackPtr(stream); + Boolean success, openComplete; + if (_CFStreamGetStatus(stream) != kCFStreamStatusNotOpen) { + return FALSE; + } + __CFBitSet(stream->flags, CALLING_CLIENT); + _CFStreamSetStatusCode(stream, kCFStreamStatusOpening); + if (cb->open) { + success = cb->open(stream, &(stream->error), &openComplete, _CFStreamGetInfoPointer(stream)); + } else { + success = TRUE; + openComplete = TRUE; + } + if (openComplete) { + if (success) { + // 2957690 - Guard against the possibility that the stream has already signalled itself in to a later state (like AtEnd) + if (__CFStreamGetStatus(stream) == kCFStreamStatusOpening) { + _CFStreamSetStatusCode(stream, kCFStreamStatusOpen); + } + _CFStreamScheduleEvent(stream, kCFStreamEventOpenCompleted); + } else { + _CFStreamSetStatusCode(stream, kCFStreamStatusError); + _CFStreamScheduleEvent(stream, kCFStreamEventErrorOccurred); + __CFBitSet(stream->flags, HAVE_CLOSED); + } + } + __CFBitClear(stream->flags, CALLING_CLIENT); + return success; +} + +CF_EXPORT Boolean CFReadStreamOpen(CFReadStreamRef stream) { + if(CF_IS_OBJC(__kCFReadStreamTypeID, stream)) { + CF_OBJC_VOIDCALL0(stream, "open"); + return TRUE; + } + return _CFStreamOpen((struct _CFStream *)stream); +} + +CF_EXPORT Boolean CFWriteStreamOpen(CFWriteStreamRef stream) { + if(CF_IS_OBJC(__kCFWriteStreamTypeID, stream)) { + CF_OBJC_VOIDCALL0(stream, "open"); + return TRUE; + } + return _CFStreamOpen((struct _CFStream *)stream); +} + +CF_EXPORT void CFReadStreamClose(CFReadStreamRef stream) { + CF_OBJC_FUNCDISPATCH0(__kCFReadStreamTypeID, void, stream, "close"); + _CFStreamClose((struct _CFStream *)stream); +} + +CF_EXPORT void CFWriteStreamClose(CFWriteStreamRef stream) { + CF_OBJC_FUNCDISPATCH0(__kCFWriteStreamTypeID, void, stream, "close"); + _CFStreamClose((struct _CFStream *)stream); +} + +CF_EXPORT Boolean CFReadStreamHasBytesAvailable(CFReadStreamRef readStream) { + CF_OBJC_FUNCDISPATCH0(__kCFReadStreamTypeID, Boolean, readStream, "hasBytesAvailable"); + struct _CFStream *stream = (struct _CFStream *)readStream; + CFStreamStatus status = _CFStreamGetStatus(stream); + const struct _CFStreamCallBacks *cb; + if (status != kCFStreamStatusOpen && status != kCFStreamStatusReading) { + return FALSE; + } + cb = _CFStreamGetCallBackPtr(stream); + if (cb->canRead == NULL) { + return TRUE; // No way to know without trying.... + } else { + Boolean result; + __CFBitSet(stream->flags, CALLING_CLIENT); + result = cb->canRead((CFReadStreamRef)stream, _CFStreamGetInfoPointer(stream)); + __CFBitClear(stream->flags, CALLING_CLIENT); + return result; + } +} + +static void waitForOpen(struct _CFStream *stream); +CFIndex CFReadStreamRead(CFReadStreamRef readStream, UInt8 *buffer, CFIndex bufferLength) { + CF_OBJC_FUNCDISPATCH2(__kCFReadStreamTypeID, CFIndex, readStream, "read:maxLength:", buffer, bufferLength); + struct _CFStream *stream = (struct _CFStream *)readStream; + CFStreamStatus status = _CFStreamGetStatus(stream); + const struct _CFStreamCallBacks *cb = _CFStreamGetCallBackPtr(stream); + if (status == kCFStreamStatusOpening) { + __CFBitSet(stream->flags, CALLING_CLIENT); + waitForOpen(stream); + __CFBitClear(stream->flags, CALLING_CLIENT); + status = _CFStreamGetStatus(stream); + } + + if (status != kCFStreamStatusOpen && status != kCFStreamStatusReading && status != kCFStreamStatusAtEnd) { + return -1; + } else if (status == kCFStreamStatusAtEnd) { + return 0; + } else { + Boolean atEOF; + CFIndex bytesRead; + __CFBitSet(stream->flags, CALLING_CLIENT); + if (stream->client) { + stream->client->whatToSignal &= ~kCFStreamEventHasBytesAvailable; + } + _CFStreamSetStatusCode(stream, kCFStreamStatusReading); + bytesRead = cb->read((CFReadStreamRef)stream, buffer, bufferLength, &(stream->error), &atEOF, _CFStreamGetInfoPointer(stream)); + if (stream->error.error != 0) { + bytesRead = -1; + _CFStreamSetStatusCode(stream, kCFStreamStatusError); + _CFStreamScheduleEvent(stream, kCFStreamEventErrorOccurred); + } else if (atEOF) { + _CFStreamSetStatusCode(stream, kCFStreamStatusAtEnd); + _CFStreamScheduleEvent(stream, kCFStreamEventEndEncountered); + } else { + _CFStreamSetStatusCode(stream, kCFStreamStatusOpen); + } + __CFBitClear(stream->flags, CALLING_CLIENT); + return bytesRead; + } +} + +CF_EXPORT const UInt8 *CFReadStreamGetBuffer(CFReadStreamRef readStream, CFIndex maxBytesToRead, CFIndex *numBytesRead) { + if (CF_IS_OBJC(__kCFReadStreamTypeID, readStream)) { + uint8_t *bufPtr = NULL; + Boolean gotBytes; + CF_OBJC_CALL2(Boolean, gotBytes, readStream, "getBuffer:length:", &bufPtr, numBytesRead); + if(gotBytes) { + return (const UInt8 *)bufPtr; + } else { + return NULL; + } + } + struct _CFStream *stream = (struct _CFStream *)readStream; + CFStreamStatus status = _CFStreamGetStatus(stream); + const struct _CFStreamCallBacks *cb = _CFStreamGetCallBackPtr(stream); + const UInt8 *buffer; + if (status == kCFStreamStatusOpening) { + __CFBitSet(stream->flags, CALLING_CLIENT); + waitForOpen(stream); + __CFBitClear(stream->flags, CALLING_CLIENT); + status = _CFStreamGetStatus(stream); + } + if (status != kCFStreamStatusOpen && status != kCFStreamStatusReading && status != kCFStreamStatusAtEnd) { + *numBytesRead = -1; + buffer = NULL; + } else if (status == kCFStreamStatusAtEnd || cb->getBuffer == NULL) { + *numBytesRead = 0; + buffer = NULL; + } else { + Boolean atEOF; + Boolean hadBytes = stream->client && (stream->client->whatToSignal & kCFStreamEventHasBytesAvailable); + __CFBitSet(stream->flags, CALLING_CLIENT); + if (hadBytes) { + stream->client->whatToSignal &= ~kCFStreamEventHasBytesAvailable; + } + _CFStreamSetStatusCode(stream, kCFStreamStatusReading); + buffer = cb->getBuffer((CFReadStreamRef)stream, maxBytesToRead, numBytesRead, &(stream->error), &atEOF, _CFStreamGetInfoPointer(stream)); + if (stream->error.error != 0) { + *numBytesRead = -1; + _CFStreamSetStatusCode(stream, kCFStreamStatusError); + buffer = NULL; + _CFStreamScheduleEvent(stream, kCFStreamEventErrorOccurred); + } else if (atEOF) { + _CFStreamSetStatusCode(stream, kCFStreamStatusAtEnd); + _CFStreamScheduleEvent(stream, kCFStreamEventEndEncountered); + } else { + if (!buffer && hadBytes) { + stream->client->whatToSignal |= kCFStreamEventHasBytesAvailable; + } + _CFStreamSetStatusCode(stream, kCFStreamStatusOpen); + } + __CFBitClear(stream->flags, CALLING_CLIENT); + } + return buffer; +} + +CF_EXPORT Boolean CFWriteStreamCanAcceptBytes(CFWriteStreamRef writeStream) { + CF_OBJC_FUNCDISPATCH0(__kCFWriteStreamTypeID, Boolean, writeStream, "hasSpaceAvailable"); + struct _CFStream *stream = (struct _CFStream *)writeStream; + CFStreamStatus status = _CFStreamGetStatus(stream); + const struct _CFStreamCallBacks *cb; + if (status != kCFStreamStatusOpen && status != kCFStreamStatusWriting) { + return FALSE; + } + cb = _CFStreamGetCallBackPtr(stream); + if (cb->canWrite == NULL) { + return TRUE; // No way to know without trying.... + } else { + Boolean result; + __CFBitSet(stream->flags, CALLING_CLIENT); + result = cb->canWrite((CFWriteStreamRef)stream, _CFStreamGetInfoPointer(stream)); + __CFBitClear(stream->flags, CALLING_CLIENT); + return result; + } +} + +CF_EXPORT CFIndex CFWriteStreamWrite(CFWriteStreamRef writeStream, const UInt8 *buffer, CFIndex bufferLength) { + CF_OBJC_FUNCDISPATCH2(__kCFWriteStreamTypeID, CFIndex, writeStream, "write:maxLength:", buffer, bufferLength); + struct _CFStream *stream = (struct _CFStream *)writeStream; + CFStreamStatus status = _CFStreamGetStatus(stream); + const struct _CFStreamCallBacks *cb = _CFStreamGetCallBackPtr(stream); + if (status == kCFStreamStatusOpening) { + __CFBitSet(stream->flags, CALLING_CLIENT); + waitForOpen(stream); + __CFBitClear(stream->flags, CALLING_CLIENT); + status = _CFStreamGetStatus(stream); + } + if (status != kCFStreamStatusOpen && status != kCFStreamStatusWriting) { + return -1; + } else { + CFIndex result; + __CFBitSet(stream->flags, CALLING_CLIENT); + _CFStreamSetStatusCode(stream, kCFStreamStatusWriting); + if (stream->client) { + stream->client->whatToSignal &= ~kCFStreamEventCanAcceptBytes; + } + result = cb->write((CFWriteStreamRef)stream, buffer, bufferLength, &(stream->error), _CFStreamGetInfoPointer(stream)); + if (stream->error.error != 0) { + _CFStreamSetStatusCode(stream, kCFStreamStatusError); + _CFStreamScheduleEvent(stream, kCFStreamEventErrorOccurred); + } else if (result == 0) { + _CFStreamSetStatusCode(stream, kCFStreamStatusAtEnd); + _CFStreamScheduleEvent(stream, kCFStreamEventEndEncountered); + } else { + _CFStreamSetStatusCode(stream, kCFStreamStatusOpen); + } + __CFBitClear(stream->flags, CALLING_CLIENT); + return result; + } +} + +__private_extern__ CFTypeRef _CFStreamCopyProperty(struct _CFStream *stream, CFStringRef propertyName) { + const struct _CFStreamCallBacks *cb = _CFStreamGetCallBackPtr(stream); + if (cb->copyProperty == NULL) { + return NULL; + } else { + CFTypeRef result; + __CFBitSet(stream->flags, CALLING_CLIENT); + result = cb->copyProperty(stream, propertyName, _CFStreamGetInfoPointer(stream)); + __CFBitClear(stream->flags, CALLING_CLIENT); + return result; + } +} + +CF_EXPORT CFTypeRef CFReadStreamCopyProperty(CFReadStreamRef stream, CFStringRef propertyName) { + CF_OBJC_FUNCDISPATCH1(__kCFReadStreamTypeID, CFTypeRef, stream, "propertyForKey:", propertyName); + return _CFStreamCopyProperty((struct _CFStream *)stream, propertyName); +} + +CF_EXPORT CFTypeRef CFWriteStreamCopyProperty(CFWriteStreamRef stream, CFStringRef propertyName) { + CF_OBJC_FUNCDISPATCH1(__kCFWriteStreamTypeID, CFTypeRef, stream, "propertyForKey:", propertyName); + return _CFStreamCopyProperty((struct _CFStream *)stream, propertyName); +} + +__private_extern__ Boolean _CFStreamSetProperty(struct _CFStream *stream, CFStringRef prop, CFTypeRef val) { + const struct _CFStreamCallBacks *cb = _CFStreamGetCallBackPtr(stream); + if (cb->setProperty == NULL) { + return FALSE; + } else { + Boolean result; + __CFBitSet(stream->flags, CALLING_CLIENT); + result = cb->setProperty(stream, prop, val, _CFStreamGetInfoPointer(stream)); + __CFBitClear(stream->flags, CALLING_CLIENT); + return result; + } +} + +CF_EXPORT +Boolean CFReadStreamSetProperty(CFReadStreamRef stream, CFStringRef propertyName, CFTypeRef propertyValue) { + CF_OBJC_FUNCDISPATCH2(__kCFReadStreamTypeID, Boolean, stream, "setProperty:forKey:", propertyValue, propertyName); + return _CFStreamSetProperty((struct _CFStream *)stream, propertyName, propertyValue); +} + +CF_EXPORT +Boolean CFWriteStreamSetProperty(CFWriteStreamRef stream, CFStringRef propertyName, CFTypeRef propertyValue) { + CF_OBJC_FUNCDISPATCH2(__kCFWriteStreamTypeID, Boolean, stream, "setProperty:forKey:", propertyValue, propertyName); + return _CFStreamSetProperty((struct _CFStream *)stream, propertyName, propertyValue); +} + +static void _initializeClient(struct _CFStream *stream) { + const struct _CFStreamCallBacks *cb = _CFStreamGetCallBackPtr(stream); + if (!cb->schedule) return; // Do we wish to allow this? + stream->client = CFAllocatorAllocate(CFGetAllocator(stream), sizeof(struct _CFStreamClient), 0); + memset(stream->client, 0, sizeof(struct _CFStreamClient)); +} + +/* If we add a setClient callback to the concrete stream callbacks, we must set/clear CALLING_CLIENT around it */ +__private_extern__ Boolean _CFStreamSetClient(struct _CFStream *stream, CFOptionFlags streamEvents, void (*clientCB)(struct _CFStream *, CFStreamEventType, void *), CFStreamClientContext *clientCallBackContext) { + + Boolean removingClient = (streamEvents == kCFStreamEventNone || clientCB == NULL || clientCallBackContext == NULL); + if (removingClient) { + clientCB = NULL; + streamEvents = kCFStreamEventNone; + clientCallBackContext = NULL; + } + if (!stream->client) { + if (removingClient) { + // We have no client now, and we've been asked to add none??? + return TRUE; + } + _initializeClient(stream); + if (!stream->client) { + // Asynch not supported + return FALSE; + } + } + if (stream->client->cb && stream->client->cbContext.release) { + stream->client->cbContext.release(stream->client->cbContext.info); + } + stream->client->cb = clientCB; + if (clientCallBackContext) { + stream->client->cbContext.version = clientCallBackContext->version; + stream->client->cbContext.retain = clientCallBackContext->retain; + stream->client->cbContext.release = clientCallBackContext->release; + stream->client->cbContext.copyDescription = clientCallBackContext->copyDescription; + stream->client->cbContext.info = (clientCallBackContext->retain && clientCallBackContext->info) ? clientCallBackContext->retain(clientCallBackContext->info) : clientCallBackContext->info; + } else { + stream->client->cbContext.retain = NULL; + stream->client->cbContext.release = NULL; + stream->client->cbContext.copyDescription = NULL; + stream->client->cbContext.info = NULL; + } + if (stream->client->when != streamEvents) { + const struct _CFStreamCallBacks *cb = _CFStreamGetCallBackPtr(stream); + stream->client->when = streamEvents; + if (cb->requestEvents) { + cb->requestEvents(stream, streamEvents, _CFStreamGetInfoPointer(stream)); + } + } + return TRUE; +} + +CF_EXPORT Boolean CFReadStreamSetClient(CFReadStreamRef readStream, CFOptionFlags streamEvents, CFReadStreamClientCallBack clientCB, CFStreamClientContext *clientContext) { + CF_OBJC_FUNCDISPATCH3(__kCFReadStreamTypeID, Boolean, readStream, "_setCFClientFlags:callback:context:", streamEvents, clientCB, clientContext); + streamEvents &= ~kCFStreamEventCanAcceptBytes; + return _CFStreamSetClient((struct _CFStream *)readStream, streamEvents, (void (*)(struct _CFStream *, CFStreamEventType, void *))clientCB, clientContext); +} + +CF_EXPORT Boolean CFWriteStreamSetClient(CFWriteStreamRef writeStream, CFOptionFlags streamEvents, CFWriteStreamClientCallBack clientCB, CFStreamClientContext *clientContext) { + CF_OBJC_FUNCDISPATCH3(__kCFWriteStreamTypeID, Boolean, writeStream, "_setCFClientFlags:callback:context:", streamEvents, clientCB, clientContext); + streamEvents &= ~kCFStreamEventHasBytesAvailable; + return _CFStreamSetClient((struct _CFStream *)writeStream, streamEvents, (void (*)(struct _CFStream *, CFStreamEventType, void *))clientCB, clientContext); +} + +static inline void *_CFStreamGetClient(struct _CFStream *stream) { + if (stream->client) return stream->client->cbContext.info; + else return NULL; +} + +CF_EXPORT void *_CFReadStreamGetClient(CFReadStreamRef readStream) { + return _CFStreamGetClient((struct _CFStream *)readStream); +} + +CF_EXPORT void *_CFWriteStreamGetClient(CFWriteStreamRef writeStream) { + return _CFStreamGetClient((struct _CFStream *)writeStream); +} + + +__private_extern__ void _CFStreamScheduleWithRunLoop(struct _CFStream *stream, CFRunLoopRef runLoop, CFStringRef runLoopMode) { + const struct _CFStreamCallBacks *cb = _CFStreamGetCallBackPtr(stream); + + if (!stream->client) { + _initializeClient(stream); + if (!stream->client) return; // we don't support asynch. + } + + if (!stream->client->rlSource) { + + CFArrayRef key; + CFMutableArrayRef list; + CFTypeRef a[2]; + + a[0] = runLoop; + a[1] = runLoopMode; + + key = CFArrayCreate(kCFAllocatorDefault, a, sizeof(a) / sizeof(a[0]), &kCFTypeArrayCallBacks); + + __CFSpinLock(&sSourceLock); + + if (!sSharedSources) + sSharedSources = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + + list = (CFMutableArrayRef)CFDictionaryGetValue(sSharedSources, key); + if (list) { + stream->client->rlSource = (CFRunLoopSourceRef)CFRetain(((struct _CFStream*)CFArrayGetValueAtIndex(list, 0))->client->rlSource); + CFRetain(list); + } + else { + CFRunLoopSourceContext ctxt = { + 0, + NULL, + NULL, // Do not use CFRetain/CFRelease callbacks here; that will cause a retain loop + NULL, // Do not use CFRetain/CFRelease callbacks here; that will cause a retain loop + (CFStringRef(*)(const void *))CFCopyDescription, + /*(Boolean(*)(const void *, const void *))CFEqual*/ NULL, + (CFHashCode(*)(const void *))__CFStreamHash, + NULL, + NULL, + (void(*)(void *))_CFStreamSignalEventSynch + }; + + list = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); + CFDictionaryAddValue(sSharedSources, key, list); + + ctxt.info = list; + + stream->client->rlSource = CFRunLoopSourceCreate(CFGetAllocator(stream), 0, &ctxt); + stream->client->whatToSignal = 0; + + CFRunLoopAddSource(runLoop, stream->client->rlSource, runLoopMode); + } + + CFArrayAppendValue(list, stream); + CFDictionaryAddValue(sSharedSources, stream, key); + + CFRelease(key); + CFRelease(list); + + __CFBitSet(stream->flags, SHARED_SOURCE); + + __CFSpinUnlock(&sSourceLock); + } + else if (__CFBitIsSet(stream->flags, SHARED_SOURCE)) { + + CFArrayRef key; + CFMutableArrayRef list; + CFIndex c, i; + + CFAllocatorRef alloc = CFGetAllocator(stream); + CFRunLoopSourceContext ctxt = { + 0, + (void *)stream, + NULL, // Do not use CFRetain/CFRelease callbacks here; that will cause a retain loop + NULL, // Do not use CFRetain/CFRelease callbacks here; that will cause a retain loop + (CFStringRef(*)(const void *))CFCopyDescription, + /*(Boolean(*)(const void *, const void *))CFEqual*/ NULL, + (CFHashCode(*)(const void *))__CFStreamHash, + NULL, + NULL, + (void(*)(void *))_CFStreamSignalEventSynch + }; + + __CFSpinLock(&sSourceLock); + + key = (CFArrayRef)CFRetain((CFTypeRef)CFDictionaryGetValue(sSharedSources, stream)); + list = (CFMutableArrayRef)CFDictionaryGetValue(sSharedSources, key); + + c = CFArrayGetCount(list); + i = CFArrayGetFirstIndexOfValue(list, CFRangeMake(0, c), stream); + if (i != kCFNotFound) { + CFArrayRemoveValueAtIndex(list, i); + c--; + } + + if (!c) { + CFRunLoopRemoveSource((CFRunLoopRef)CFArrayGetValueAtIndex(key, 0), stream->client->rlSource, (CFStringRef)CFArrayGetValueAtIndex(key, 1)); + CFRunLoopSourceInvalidate(stream->client->rlSource); + CFDictionaryRemoveValue(sSharedSources, key); + } + + CFDictionaryRemoveValue(sSharedSources, stream); + + CFRelease(stream->client->rlSource); + __CFBitClear(stream->flags, SHARED_SOURCE); + + __CFSpinUnlock(&sSourceLock); + + stream->client->rlSource = CFRunLoopSourceCreate(alloc, 0, &ctxt); + + CFRunLoopAddSource((CFRunLoopRef)CFArrayGetValueAtIndex(key, 0), stream->client->rlSource, (CFStringRef)CFArrayGetValueAtIndex(key, 1)); + + CFRelease(key); + + CFRunLoopAddSource(runLoop, stream->client->rlSource, runLoopMode); + } else { + CFRunLoopAddSource(runLoop, stream->client->rlSource, runLoopMode); + } + + if (!stream->client->runLoopsAndModes) { + stream->client->runLoopsAndModes = CFArrayCreateMutable(CFGetAllocator(stream), 0, &kCFTypeArrayCallBacks); + } + CFArrayAppendValue(stream->client->runLoopsAndModes, runLoop); + CFArrayAppendValue(stream->client->runLoopsAndModes, runLoopMode); + + if (cb->schedule) { + __CFBitSet(stream->flags, CALLING_CLIENT); + cb->schedule(stream, runLoop, runLoopMode, _CFStreamGetInfoPointer(stream)); + __CFBitClear(stream->flags, CALLING_CLIENT); + } +} + +CF_EXPORT void CFReadStreamScheduleWithRunLoop(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode) { + CF_OBJC_FUNCDISPATCH2(__kCFReadStreamTypeID, void, stream, "_scheduleInCFRunLoop:forMode:", runLoop, runLoopMode); + _CFStreamScheduleWithRunLoop((struct _CFStream *)stream, runLoop, runLoopMode); +} + +CF_EXPORT void CFWriteStreamScheduleWithRunLoop(CFWriteStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode) { + CF_OBJC_FUNCDISPATCH2(__kCFWriteStreamTypeID, void, stream, "_scheduleInCFRunLoop:forMode:", runLoop, runLoopMode); + _CFStreamScheduleWithRunLoop((struct _CFStream *)stream, runLoop, runLoopMode); +} + + +__private_extern__ void _CFStreamUnscheduleFromRunLoop(struct _CFStream *stream, CFRunLoopRef runLoop, CFStringRef runLoopMode) { + const struct _CFStreamCallBacks *cb = _CFStreamGetCallBackPtr(stream); + if (!stream->client) return; + if (!stream->client->rlSource) return; + + if (!__CFBitIsSet(stream->flags, SHARED_SOURCE)) { + CFRunLoopRemoveSource(runLoop, stream->client->rlSource, runLoopMode); + } else { + CFArrayRef key; + CFMutableArrayRef list; + CFIndex c, i; + + __CFSpinLock(&sSourceLock); + + key = (CFArrayRef)CFDictionaryGetValue(sSharedSources, stream); + list = (CFMutableArrayRef)CFDictionaryGetValue(sSharedSources, key); + + c = CFArrayGetCount(list); + i = CFArrayGetFirstIndexOfValue(list, CFRangeMake(0, c), stream); + if (i != kCFNotFound) { + CFArrayRemoveValueAtIndex(list, i); + c--; + } + + if (!c) { + CFRunLoopRemoveSource(runLoop, stream->client->rlSource, runLoopMode); + CFRunLoopSourceInvalidate(stream->client->rlSource); + CFDictionaryRemoveValue(sSharedSources, key); + } + + CFDictionaryRemoveValue(sSharedSources, stream); + + CFRelease(stream->client->rlSource); + stream->client->rlSource = NULL; + __CFBitClear(stream->flags, SHARED_SOURCE); + + __CFSpinUnlock(&sSourceLock); + } + + _CFStreamRemoveRunLoopAndModeFromArray(stream->client->runLoopsAndModes, runLoop, runLoopMode); + + if (cb->unschedule) { + cb->unschedule(stream, runLoop, runLoopMode, _CFStreamGetInfoPointer(stream)); + } +} + +CF_EXPORT void CFReadStreamUnscheduleFromRunLoop(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode) { + CF_OBJC_FUNCDISPATCH2(__kCFReadStreamTypeID, void, stream, "_unscheduleFromCFRunLoop:forMode:", runLoop, runLoopMode); + _CFStreamUnscheduleFromRunLoop((struct _CFStream *)stream, runLoop, runLoopMode); +} + +void CFWriteStreamUnscheduleFromRunLoop(CFWriteStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode) { + CF_OBJC_FUNCDISPATCH2(__kCFWriteStreamTypeID, void, stream, "_unscheduleFromCFRunLoop:forMode:", runLoop, runLoopMode); + _CFStreamUnscheduleFromRunLoop((struct _CFStream *)stream, runLoop, runLoopMode); +} + +static void waitForOpen(struct _CFStream *stream) { + CFRunLoopRef runLoop = CFRunLoopGetCurrent(); + CFStringRef privateMode = CFSTR("_kCFStreamBlockingOpenMode"); + _CFStreamScheduleWithRunLoop(stream, runLoop, privateMode); + // We cannot call _CFStreamGetStatus, because that tries to set/clear CALLING_CLIENT, which should be set around this entire call (we're within a call from the client). This should be o.k., because we're running the run loop, so our status code should be being updated in a timely fashion.... + while (__CFStreamGetStatus(stream) == kCFStreamStatusOpening) { + CFRunLoopRunInMode(privateMode, 1e+20, TRUE); + } + _CFStreamUnscheduleFromRunLoop(stream, runLoop, privateMode); +} + +static inline CFArrayRef _CFStreamGetRunLoopsAndModes(struct _CFStream *stream) { + if (stream->client) return stream->client->runLoopsAndModes; + else return NULL; +} + +CF_EXPORT CFArrayRef _CFReadStreamGetRunLoopsAndModes(CFReadStreamRef readStream) { + return _CFStreamGetRunLoopsAndModes((struct _CFStream *)readStream); +} + +CF_EXPORT CFArrayRef _CFWriteStreamGetRunLoopsAndModes(CFWriteStreamRef writeStream) { + return _CFStreamGetRunLoopsAndModes((struct _CFStream *)writeStream); +} + +CF_EXPORT void CFReadStreamSignalEvent(CFReadStreamRef stream, CFStreamEventType event, CFStreamError *error) { + _CFStreamSignalEvent((struct _CFStream *)stream, event, error, TRUE); +} + +CF_EXPORT void CFWriteStreamSignalEvent(CFWriteStreamRef stream, CFStreamEventType event, CFStreamError *error) { + _CFStreamSignalEvent((struct _CFStream *)stream, event, error, TRUE); +} + +CF_EXPORT void _CFReadStreamSignalEventDelayed(CFReadStreamRef stream, CFStreamEventType event, CFStreamError *error) { + _CFStreamSignalEvent((struct _CFStream *)stream, event, error, FALSE); +} + +CF_EXPORT void _CFWriteStreamSignalEventDelayed(CFWriteStreamRef stream, CFStreamEventType event, CFStreamError *error) { + _CFStreamSignalEvent((struct _CFStream *)stream, event, error, FALSE); +} + +CF_EXPORT void *CFReadStreamGetInfoPointer(CFReadStreamRef stream) { + return _CFStreamGetInfoPointer((struct _CFStream *)stream); +} + +CF_EXPORT void *CFWriteStreamGetInfoPointer(CFWriteStreamRef stream) { + return _CFStreamGetInfoPointer((struct _CFStream *)stream); +} + +#if defined(__MACH__) +#pragma mark - +#pragma mark Scheduling Convenience Routines +#endif + +/* CF_EXPORT */ +void _CFStreamSourceScheduleWithRunLoop(CFRunLoopSourceRef source, CFMutableArrayRef runLoopsAndModes, CFRunLoopRef runLoop, CFStringRef runLoopMode) +{ + CFIndex count; + CFRange range; + + count = CFArrayGetCount(runLoopsAndModes); + range = CFRangeMake(0, count); + + while (range.length) { + + CFIndex i = CFArrayGetFirstIndexOfValue(runLoopsAndModes, range, runLoop); + + if (i == kCFNotFound) + break; + + if (CFEqual(CFArrayGetValueAtIndex(runLoopsAndModes, i + 1), runLoopMode)) + return; + + range.location = i + 2; + range.length = count - range.location; + } + + // Add the new values. + CFArrayAppendValue(runLoopsAndModes, runLoop); + CFArrayAppendValue(runLoopsAndModes, runLoopMode); + + // Schedule the source on the new loop and mode. + if (source) + CFRunLoopAddSource(runLoop, source, runLoopMode); +} + + +/* CF_EXPORT */ +void _CFStreamSourceUnscheduleFromRunLoop(CFRunLoopSourceRef source, CFMutableArrayRef runLoopsAndModes, CFRunLoopRef runLoop, CFStringRef runLoopMode) +{ + CFIndex count; + CFRange range; + + count = CFArrayGetCount(runLoopsAndModes); + range = CFRangeMake(0, count); + + while (range.length) { + + CFIndex i = CFArrayGetFirstIndexOfValue(runLoopsAndModes, range, runLoop); + + // If not found, it's not scheduled on it. + if (i == kCFNotFound) + return; + + // Make sure it is scheduled in this mode. + if (CFEqual(CFArrayGetValueAtIndex(runLoopsAndModes, i + 1), runLoopMode)) { + + // Remove mode and runloop from the list. + CFArrayReplaceValues(runLoopsAndModes, CFRangeMake(i, 2), NULL, 0); + + // Remove it from the runloop. + if (source) + CFRunLoopRemoveSource(runLoop, source, runLoopMode); + + return; + } + + range.location = i + 2; + range.length = count - range.location; + } +} + + +/* CF_EXPORT */ +void _CFStreamSourceScheduleWithAllRunLoops(CFRunLoopSourceRef source, CFArrayRef runLoopsAndModes) +{ + CFIndex i, count = CFArrayGetCount(runLoopsAndModes); + + if (!source) + return; + + for (i = 0; i < count; i += 2) { + + // Make sure it's scheduled on all the right loops and modes. + // Go through the array adding the source to all loops and modes. + CFRunLoopAddSource((CFRunLoopRef)CFArrayGetValueAtIndex(runLoopsAndModes, i), + source, + (CFStringRef)CFArrayGetValueAtIndex(runLoopsAndModes, i + 1)); + } +} + + +/* CF_EXPORT */ +void _CFStreamSourceUncheduleFromAllRunLoops(CFRunLoopSourceRef source, CFArrayRef runLoopsAndModes) +{ + CFIndex i, count = CFArrayGetCount(runLoopsAndModes); + + if (!source) + return; + + for (i = 0; i < count; i += 2) { + + // Go through the array removing the source from all loops and modes. + CFRunLoopRemoveSource((CFRunLoopRef)CFArrayGetValueAtIndex(runLoopsAndModes, i), + source, + (CFStringRef)CFArrayGetValueAtIndex(runLoopsAndModes, i + 1)); + } +} + +Boolean _CFStreamRemoveRunLoopAndModeFromArray(CFMutableArrayRef runLoopsAndModes, CFRunLoopRef rl, CFStringRef mode) { + CFIndex idx, cnt; + Boolean found = FALSE; + + if (!runLoopsAndModes) return FALSE; + + cnt = CFArrayGetCount(runLoopsAndModes); + for (idx = 0; idx + 1 < cnt; idx += 2) { + if (CFEqual(CFArrayGetValueAtIndex(runLoopsAndModes, idx), rl) && CFEqual(CFArrayGetValueAtIndex(runLoopsAndModes, idx + 1), mode)) { + CFArrayRemoveValueAtIndex(runLoopsAndModes, idx); + CFArrayRemoveValueAtIndex(runLoopsAndModes, idx); + found = TRUE; + break; + } + } + return found; +} + + +#if defined(__WIN32__) + +void __CFStreamCleanup(void) { + __CFSpinLock(&sSourceLock); + if (sSharedSources) { + CFIndex count = CFDictionaryGetCount(sSharedSources); + if (count == 0) { + // Only release if empty. If it's still holding streams (which would be a client + // bug leak), freeing this dict would free the streams, which then need to access the + // dict to remove themselves, which leads to a deadlock. + CFRelease(sSharedSources); + sSharedSources = NULL; + } else + fprintf(stderr, "*** CFNetwork is shutting down, but %ld streams are still scheduled.\n", count); + } + __CFSpinUnlock(&sSourceLock); +} + +#endif + diff --git a/Stream.subproj/CFStream.h b/Stream.subproj/CFStream.h new file mode 100644 index 0000000..471deaf --- /dev/null +++ b/Stream.subproj/CFStream.h @@ -0,0 +1,280 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFStream.h + Copyright (c) 2000-2005, Apple, Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFSTREAM__) +#define __COREFOUNDATION_CFSTREAM__ 1 + +#include +#include +#include +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +typedef enum { + kCFStreamStatusNotOpen = 0, + kCFStreamStatusOpening, /* open is in-progress */ + kCFStreamStatusOpen, + kCFStreamStatusReading, + kCFStreamStatusWriting, + kCFStreamStatusAtEnd, /* no further bytes can be read/written */ + kCFStreamStatusClosed, + kCFStreamStatusError +} CFStreamStatus; + +typedef enum { + kCFStreamErrorDomainCustom = -1, /* custom to the kind of stream in question */ + kCFStreamErrorDomainPOSIX = 1, /* POSIX errno; interpret using */ + kCFStreamErrorDomainMacOSStatus /* OSStatus type from Carbon APIs; interpret using */ +} CFStreamErrorDomain; + +typedef struct { + CFStreamErrorDomain domain; + SInt32 error; +} CFStreamError; + +typedef enum { + kCFStreamEventNone = 0, + kCFStreamEventOpenCompleted = 1, + kCFStreamEventHasBytesAvailable = 2, + kCFStreamEventCanAcceptBytes = 4, + kCFStreamEventErrorOccurred = 8, + kCFStreamEventEndEncountered = 16 +} CFStreamEventType; + +typedef struct { + CFIndex version; + void *info; + void *(*retain)(void *info); + void (*release)(void *info); + CFStringRef (*copyDescription)(void *info); +} CFStreamClientContext; + +typedef struct __CFReadStream * CFReadStreamRef; +typedef struct __CFWriteStream * CFWriteStreamRef; + +typedef void (*CFReadStreamClientCallBack)(CFReadStreamRef stream, CFStreamEventType type, void *clientCallBackInfo); +typedef void (*CFWriteStreamClientCallBack)(CFWriteStreamRef stream, CFStreamEventType type, void *clientCallBackInfo); + +CF_EXPORT +CFTypeID CFReadStreamGetTypeID(void); +CF_EXPORT +CFTypeID CFWriteStreamGetTypeID(void); + +/* Memory streams */ + +/* Value will be a CFData containing all bytes thusfar written; used to recover the data written to a memory write stream. */ +CF_EXPORT +const CFStringRef kCFStreamPropertyDataWritten; + +/* Pass kCFAllocatorNull for bytesDeallocator to prevent CFReadStream from deallocating bytes; otherwise, CFReadStream will deallocate bytes when the stream is destroyed */ +CF_EXPORT +CFReadStreamRef CFReadStreamCreateWithBytesNoCopy(CFAllocatorRef alloc, const UInt8 *bytes, CFIndex length, CFAllocatorRef bytesDeallocator); + +/* The stream writes into the buffer given; when bufferCapacity is exhausted, the stream is exhausted (status becomes kCFStreamStatusAtEnd) */ +CF_EXPORT +CFWriteStreamRef CFWriteStreamCreateWithBuffer(CFAllocatorRef alloc, UInt8 *buffer, CFIndex bufferCapacity); + +/* New buffers are allocated from bufferAllocator as bytes are written to the stream. At any point, you can recover the bytes thusfar written by asking for the property kCFStreamPropertyDataWritten, above */ +CF_EXPORT +CFWriteStreamRef CFWriteStreamCreateWithAllocatedBuffers(CFAllocatorRef alloc, CFAllocatorRef bufferAllocator); + +/* File streams */ +CF_EXPORT +CFReadStreamRef CFReadStreamCreateWithFile(CFAllocatorRef alloc, CFURLRef fileURL); +CF_EXPORT +CFWriteStreamRef CFWriteStreamCreateWithFile(CFAllocatorRef alloc, CFURLRef fileURL); + +#if MAC_OS_X_VERSION_10_2 <= MAC_OS_X_VERSION_MAX_ALLOWED +/* Property for file write streams; value should be a CFBoolean. Set to TRUE to append to a file, rather than to replace its contents */ +CF_EXPORT +const CFStringRef kCFStreamPropertyAppendToFile; +#endif + +#if MAC_OS_X_VERSION_10_3 <= MAC_OS_X_VERSION_MAX_ALLOWED + +CF_EXPORT const CFStringRef kCFStreamPropertyFileCurrentOffset AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; // Value is a CFNumber + +#endif + +/* Socket stream properties */ + +/* Value will be a CFData containing the native handle */ +CF_EXPORT +const CFStringRef kCFStreamPropertySocketNativeHandle; + +/* Value will be a CFString, or NULL if unknown */ +CF_EXPORT +const CFStringRef kCFStreamPropertySocketRemoteHostName; + +/* Value will be a CFNumber, or NULL if unknown */ +CF_EXPORT +const CFStringRef kCFStreamPropertySocketRemotePortNumber; + +/* Socket streams; the returned streams are paired such that they use the same socket; pass NULL if you want only the read stream or the write stream */ +CF_EXPORT +void CFStreamCreatePairWithSocket(CFAllocatorRef alloc, CFSocketNativeHandle sock, CFReadStreamRef *readStream, CFWriteStreamRef *writeStream); +CF_EXPORT +void CFStreamCreatePairWithSocketToHost(CFAllocatorRef alloc, CFStringRef host, UInt32 port, CFReadStreamRef *readStream, CFWriteStreamRef *writeStream); +#if MAC_OS_X_VERSION_10_2 <= MAC_OS_X_VERSION_MAX_ALLOWED +CF_EXPORT +void CFStreamCreatePairWithPeerSocketSignature(CFAllocatorRef alloc, const CFSocketSignature *signature, CFReadStreamRef *readStream, CFWriteStreamRef *writeStream); +#endif + + +/* Returns the current state of the stream */ +CF_EXPORT +CFStreamStatus CFReadStreamGetStatus(CFReadStreamRef stream); +CF_EXPORT +CFStreamStatus CFWriteStreamGetStatus(CFWriteStreamRef stream); + +/* 0 is returned if no error has occurred. errorDomain specifies the domain + in which the error code should be interpretted; pass NULL if you are not + interested. */ +CF_EXPORT +CFStreamError CFReadStreamGetError(CFReadStreamRef stream); +CF_EXPORT +CFStreamError CFWriteStreamGetError(CFWriteStreamRef stream); + +/* Returns success/failure. Opening a stream causes it to reserve all the system + resources it requires. If the stream can open non-blocking, this will always + return TRUE; listen to the run loop source to find out when the open completes + and whether it was successful, or poll using CFRead/WriteStreamGetStatus(), waiting + for a status of kCFStreamStatusOpen or kCFStreamStatusError. */ +CF_EXPORT +Boolean CFReadStreamOpen(CFReadStreamRef stream); +CF_EXPORT +Boolean CFWriteStreamOpen(CFWriteStreamRef stream); + +/* Terminates the flow of bytes; releases any system resources required by the + stream. The stream may not fail to close. You may call CFStreamClose() to + effectively abort a stream. */ +CF_EXPORT +void CFReadStreamClose(CFReadStreamRef stream); +CF_EXPORT +void CFWriteStreamClose(CFWriteStreamRef stream); + +/* Whether there is data currently available for reading; returns TRUE if it's + impossible to tell without trying */ +CF_EXPORT +Boolean CFReadStreamHasBytesAvailable(CFReadStreamRef stream); + +/* Returns the number of bytes read, or -1 if an error occurs preventing any + bytes from being read, or 0 if the stream's end was encountered. + It is an error to try and read from a stream that hasn't been opened first. + This call will block until at least one byte is available; it will NOT block + until the entire buffer can be filled. To avoid blocking, either poll using + CFReadStreamHasBytesAvailable() or use the run loop and listen for the + kCFStreamCanRead event for notification of data available. */ +CF_EXPORT +CFIndex CFReadStreamRead(CFReadStreamRef stream, UInt8 *buffer, CFIndex bufferLength); + +/* Returns a pointer to an internal buffer if possible (setting *numBytesRead + to the length of the returned buffer), otherwise returns NULL; guaranteed + to return in O(1). Bytes returned in the buffer are considered read from + the stream; if maxBytesToRead is greater than 0, not more than maxBytesToRead + will be returned. If maxBytesToRead is less than or equal to zero, as many bytes + as are readily available will be returned. The returned buffer is good only + until the next stream operation called on the stream. Caller should neither + change the contents of the returned buffer nor attempt to deallocate the buffer; + it is still owned by the stream. */ +CF_EXPORT +const UInt8 *CFReadStreamGetBuffer(CFReadStreamRef stream, CFIndex maxBytesToRead, CFIndex *numBytesRead); + +/* Whether the stream can currently be written to without blocking; + returns TRUE if it's impossible to tell without trying */ +CF_EXPORT +Boolean CFWriteStreamCanAcceptBytes(CFWriteStreamRef stream); + +/* Returns the number of bytes successfully written, -1 if an error has + occurred, or 0 if the stream has been filled to capacity (for fixed-length + streams). If the stream is not full, this call will block until at least + one byte is written. To avoid blocking, either poll via CFWriteStreamCanAcceptBytes + or use the run loop and listen for the kCFStreamCanWrite event. */ +CF_EXPORT +CFIndex CFWriteStreamWrite(CFWriteStreamRef stream, const UInt8 *buffer, CFIndex bufferLength); + +/* Particular streams can name properties and assign meanings to them; you + access these properties through the following calls. A property is any interesting + information about the stream other than the data being transmitted itself. + Examples include the headers from an HTTP transmission, or the expected + number of bytes, or permission information, etc. Properties that can be set + configure the behavior of the stream, and may only be settable at particular times + (like before the stream has been opened). See the documentation for particular + properties to determine their get- and set-ability. */ +CF_EXPORT +CFTypeRef CFReadStreamCopyProperty(CFReadStreamRef stream, CFStringRef propertyName); +CF_EXPORT +CFTypeRef CFWriteStreamCopyProperty(CFWriteStreamRef stream, CFStringRef propertyName); + +#if MAC_OS_X_VERSION_10_2 <= MAC_OS_X_VERSION_MAX_ALLOWED +/* Returns TRUE if the stream recognizes and accepts the given property-value pair; + FALSE otherwise. */ +CF_EXPORT +Boolean CFReadStreamSetProperty(CFReadStreamRef stream, CFStringRef propertyName, CFTypeRef propertyValue); +CF_EXPORT +Boolean CFWriteStreamSetProperty(CFWriteStreamRef stream, CFStringRef propertyName, CFTypeRef propertyValue); +#endif + +/* Asynchronous processing - If you wish to neither poll nor block, you may register + a client to hear about interesting events that occur on a stream. Only one client + per stream is allowed; registering a new client replaces the previous one. + + Once you have set a client, you need to schedule a run loop on which that client + can be notified. You may schedule multiple run loops (for instance, if you are + using a thread pool). The client callback will be triggered via one of the scheduled + run loops; It is the caller's responsibility to ensure that at least one of the + scheduled run loops is being run. + + NOTE: not all streams provide these notifications. If a stream does not support + asynchronous notification, CFStreamSetClient() will return NO; typically, such + streams will never block for device I/O (e.g. a stream on memory) +*/ + +CF_EXPORT +Boolean CFReadStreamSetClient(CFReadStreamRef stream, CFOptionFlags streamEvents, CFReadStreamClientCallBack clientCB, CFStreamClientContext *clientContext); +CF_EXPORT +Boolean CFWriteStreamSetClient(CFWriteStreamRef stream, CFOptionFlags streamEvents, CFWriteStreamClientCallBack clientCB, CFStreamClientContext *clientContext); + +CF_EXPORT +void CFReadStreamScheduleWithRunLoop(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode); +CF_EXPORT +void CFWriteStreamScheduleWithRunLoop(CFWriteStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode); + +CF_EXPORT +void CFReadStreamUnscheduleFromRunLoop(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode); +CF_EXPORT +void CFWriteStreamUnscheduleFromRunLoop(CFWriteStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode); + +#if defined(__cplusplus) +} +#endif + +#endif /* ! __COREFOUNDATION_CFSTREAM__ */ diff --git a/Stream.subproj/CFStreamAbstract.h b/Stream.subproj/CFStreamAbstract.h new file mode 100644 index 0000000..d023244 --- /dev/null +++ b/Stream.subproj/CFStreamAbstract.h @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFStreamAbstract.h + Copyright (c) 2000-2005, Apple, Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFSTREAMABSTRACT__) +#define __COREFOUNDATION_CFSTREAMABSTRACT__ 1 + +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +typedef struct { + CFIndex version; /* == 1 */ + void *(*create)(CFReadStreamRef stream, void *info); + void (*finalize)(CFReadStreamRef stream, void *info); + CFStringRef (*copyDescription)(CFReadStreamRef stream, void *info); + Boolean (*open)(CFReadStreamRef stream, CFStreamError *error, Boolean *openComplete, void *info); + Boolean (*openCompleted)(CFReadStreamRef stream, CFStreamError *error, void *info); + CFIndex (*read)(CFReadStreamRef stream, UInt8 *buffer, CFIndex bufferLength, CFStreamError *error, Boolean *atEOF, void *info); + const UInt8 *(*getBuffer)(CFReadStreamRef stream, CFIndex maxBytesToRead, CFIndex *numBytesRead, CFStreamError *error, Boolean *atEOF, void *info); + Boolean (*canRead)(CFReadStreamRef stream, void *info); + void (*close)(CFReadStreamRef stream, void *info); + CFTypeRef (*copyProperty)(CFReadStreamRef stream, CFStringRef propertyName, void *info); + Boolean (*setProperty)(CFReadStreamRef stream, CFStringRef propertyName, CFTypeRef propertyValue, void *info); + void (*requestEvents)(CFReadStreamRef stream, CFOptionFlags streamEvents, void *info); + void (*schedule)(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info); + void (*unschedule)(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info); +} CFReadStreamCallBacks; + +typedef struct { + CFIndex version; /* == 1 */ + + void *(*create)(CFWriteStreamRef stream, void *info); + void (*finalize)(CFWriteStreamRef stream, void *info); + CFStringRef (*copyDescription)(CFWriteStreamRef stream, void *info); + + Boolean (*open)(CFWriteStreamRef stream, CFStreamError *error, Boolean *openComplete, void *info); + Boolean (*openCompleted)(CFWriteStreamRef stream, CFStreamError *error, void *info); + CFIndex (*write)(CFWriteStreamRef stream, const UInt8 *buffer, CFIndex bufferLength, CFStreamError *error, void *info); + Boolean (*canWrite)(CFWriteStreamRef stream, void *info); + void (*close)(CFWriteStreamRef stream, void *info); + CFTypeRef (*copyProperty)(CFWriteStreamRef stream, CFStringRef propertyName, void *info); + Boolean (*setProperty)(CFWriteStreamRef stream, CFStringRef propertyName, CFTypeRef propertyValue, void *info); + void (*requestEvents)(CFWriteStreamRef stream, CFOptionFlags streamEvents, void *info); + void (*schedule)(CFWriteStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info); + void (*unschedule)(CFWriteStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info); +} CFWriteStreamCallBacks; + +/* During a stream's lifetime, the open callback will be called once, followed by any number of openCompleted calls (until openCompleted returns TRUE). Then any number of read/canRead or write/canWrite calls, then a single close call. copyProperty can be called at any time. prepareAsynch will be called exactly once when the stream's client is first configured. + + Expected semantics: + - open reserves any system resources that are needed. The stream may start the process of opening, returning TRUE immediately and setting openComplete to FALSE. When the open completes, _CFStreamSignalEvent should be called passing kCFStreamOpenCompletedEvent. openComplete should be set to TRUE only if the open operation completed in its entirety. + - openCompleted will only be called after open has been called, but before any kCFStreamOpenCompletedEvent has been received. Return TRUE, setting error.code to 0, if the open operation has completed. Return TRUE, setting error to the correct error code and domain if the open operation completed, but failed. Return FALSE if the open operation is still in-progress. If your open ever fails to complete (i.e. sets openComplete to FALSE), you must be implement the openCompleted callback. + - read should read into the given buffer, returning the number of bytes successfully read. read must block until at least one byte is available, but should not block until the entire buffer is filled; zero should only be returned if end-of-stream is encountered. atEOF should be set to true if the EOF is encountered, false otherwise. error.code should be set to zero if no error occurs; otherwise, error should be set to the appropriate values. + - getBuffer is an optimization to return an internal buffer of bytes read from the stream, and may return NULL. getBuffer itself may be NULL if the concrete implementation does not wish to provide an internal buffer. If implemented, it should set numBytesRead to the number of bytes available in the internal buffer (but should not exceed maxBytesToRead) and return a pointer to the base of the bytes. + - canRead will only be called once openCompleted reports that the stream has been successfully opened (or the initial open call succeeded). It should return whether there are bytes that can be read without blocking. + - write should write the bytes in the given buffer to the device, returning the number of bytes successfully written. write must block until at least one byte is written. error.code should be set to zero if no error occurs; otherwise, error should be set to the appropriate values. + - close should close the device, releasing any reserved system resources. close cannot fail (it may be called to abort the stream), and may be called at any time after open has been called. It will only be called once. + - copyProperty should return the value for the given property, or NULL if none exists. Composite streams (streams built on top of other streams) should take care to call CFStreamCopyProperty on the base stream if they do not recognize the property given, to give the underlying stream a chance to respond. +*/ + +// Primitive creation mechanisms. +CF_EXPORT +CFReadStreamRef CFReadStreamCreate(CFAllocatorRef alloc, const CFReadStreamCallBacks *callbacks, void *info); +CF_EXPORT +CFWriteStreamRef CFWriteStreamCreate(CFAllocatorRef alloc, const CFWriteStreamCallBacks *callbacks, void *info); + +/* All the functions below can only be called when you are sure the stream in question was created via + CFReadStreamCreate() or CFWriteStreamCreate(), above. They are NOT safe for toll-free bridged objects, + so the caller must be sure the argument passed is not such an object. */ + +// To be called by the concrete stream implementation (the callbacks) when an event occurs. error may be NULL if event != kCFStreamEventErrorOccurred +CF_EXPORT +void CFReadStreamSignalEvent(CFReadStreamRef stream, CFStreamEventType event, CFStreamError *error); +CF_EXPORT +void CFWriteStreamSignalEvent(CFWriteStreamRef stream, CFStreamEventType event, CFStreamError *error); +CF_EXPORT + +// These require that the stream allow the run loop to run once before delivering the event to its client. +void _CFReadStreamSignalEventDelayed(CFReadStreamRef stream, CFStreamEventType event, CFStreamError *error); +CF_EXPORT +void _CFWriteStreamSignalEventDelayed(CFWriteStreamRef stream, CFStreamEventType event, CFStreamError *error); + +// Convenience for concrete implementations to extract the info pointer given the stream. +CF_EXPORT +void *CFReadStreamGetInfoPointer(CFReadStreamRef stream); +CF_EXPORT +void *CFWriteStreamGetInfoPointer(CFWriteStreamRef stream); + +// Returns the client info pointer currently set on the stream. These should probably be made public one day. +CF_EXPORT +void *_CFReadStreamGetClient(CFReadStreamRef readStream); +CF_EXPORT +void *_CFWriteStreamGetClient(CFWriteStreamRef writeStream); + +// Returns an array of the runloops and modes on which the stream is currently scheduled +CF_EXPORT +CFArrayRef _CFReadStreamGetRunLoopsAndModes(CFReadStreamRef readStream); +CF_EXPORT +CFArrayRef _CFWriteStreamGetRunLoopsAndModes(CFWriteStreamRef writeStream); + +/* Deprecated; here for backwards compatibility. Will be removed by Jaguar6D. */ +typedef struct { + CFIndex version; /* == 0 */ + Boolean (*open)(CFReadStreamRef stream, CFStreamError *error, Boolean *openComplete, void *info); + Boolean (*openCompleted)(CFReadStreamRef stream, CFStreamError *error, void *info); + CFIndex (*read)(CFReadStreamRef stream, UInt8 *buffer, CFIndex bufferLength, CFStreamError *error, Boolean *atEOF, void *info); + const UInt8 *(*getBuffer)(CFReadStreamRef stream, CFIndex maxBytesToRead, CFIndex *numBytesRead, CFStreamError *error, Boolean *atEOF, void *info); + Boolean (*canRead)(CFReadStreamRef stream, void *info); + void (*close)(CFReadStreamRef stream, void *info); + CFTypeRef (*copyProperty)(CFReadStreamRef stream, CFStringRef propertyName, void *info); + void (*schedule)(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info); + void (*unschedule)(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info); +} CFReadStreamCallBacksV0; + +typedef struct { + CFIndex version; /* == 0 */ + Boolean (*open)(CFWriteStreamRef stream, CFStreamError *error, Boolean *openComplete, void *info); + Boolean (*openCompleted)(CFWriteStreamRef stream, CFStreamError *error, void *info); + CFIndex (*write)(CFWriteStreamRef stream, const UInt8 *buffer, CFIndex bufferLength, CFStreamError *error, void *info); + Boolean (*canWrite)(CFWriteStreamRef stream, void *info); + void (*close)(CFWriteStreamRef stream, void *info); + CFTypeRef (*copyProperty)(CFWriteStreamRef stream, CFStringRef propertyName, void *info); + void (*schedule)(CFWriteStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info); + void (*unschedule)(CFWriteStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info); +} CFWriteStreamCallBacksV0; + +#if defined(__cplusplus) +} +#endif + +#endif /* ! __COREFOUNDATION_CFSTREAMABSTRACT__ */ diff --git a/Stream.subproj/CFStreamPriv.h b/Stream.subproj/CFStreamPriv.h new file mode 100644 index 0000000..3106dc5 --- /dev/null +++ b/Stream.subproj/CFStreamPriv.h @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFStreamPriv.h + Copyright (c) 2000-2005, Apple, Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFSTREAMPRIV__) +#define __COREFOUNDATION_CFSTREAMPRIV__ 1 + +#include +#include +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +struct _CFStream; +struct _CFStreamClient { + CFStreamClientContext cbContext; + void (*cb)(struct _CFStream *, CFStreamEventType, void *); + CFOptionFlags when; + CFRunLoopSourceRef rlSource; + CFMutableArrayRef runLoopsAndModes; + CFOptionFlags whatToSignal; +}; + +// A unified set of callbacks so we can use a single structure for all struct _CFStreams. +struct _CFStreamCallBacks { + CFIndex version; + void *(*create)(struct _CFStream *stream, void *info); + void (*finalize)(struct _CFStream *stream, void *info); + CFStringRef (*copyDescription)(struct _CFStream *stream, void *info); + Boolean (*open)(struct _CFStream *stream, CFStreamError *error, Boolean *openComplete, void *info); + Boolean (*openCompleted)(struct _CFStream *stream, CFStreamError *error, void *info); + CFIndex (*read)(CFReadStreamRef stream, UInt8 *buffer, CFIndex bufferLength, CFStreamError *error, Boolean *atEOF, void *info); + const UInt8 *(*getBuffer)(CFReadStreamRef sream, CFIndex maxBytesToRead, CFIndex *numBytesRead, CFStreamError *error, Boolean *atEOF, void *info); + Boolean (*canRead)(CFReadStreamRef, void *info); + CFIndex (*write)(CFWriteStreamRef, const UInt8 *buffer, CFIndex bufferLength, CFStreamError *error, void *info); + Boolean (*canWrite)(CFWriteStreamRef, void *info); + void (*close)(struct _CFStream *stream, void *info); + CFTypeRef (*copyProperty)(struct _CFStream *stream, CFStringRef propertyName, void *info); + Boolean (*setProperty)(struct _CFStream *stream, CFStringRef propertyName, CFTypeRef propertyValue, void *info); + void (*requestEvents)(struct _CFStream *stream, CFOptionFlags events, void *info); + void (*schedule)(struct _CFStream *stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info); + void (*unschedule)(struct _CFStream *stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info); +}; + +struct _CFStream { + CFRuntimeBase _cfBase; + CFOptionFlags flags; + CFStreamError error; + struct _CFStreamClient *client; + void *info; + const struct _CFStreamCallBacks *callBacks; // This will not exist (will not be allocated) if the callbacks are from our known, "blessed" set. + void *_reserved1; +}; + +CF_INLINE void *_CFStreamGetInfoPointer(struct _CFStream *stream) { + return stream->info; +} + +// cb version must be 1 +CF_EXPORT struct _CFStream *_CFStreamCreateWithConstantCallbacks(CFAllocatorRef alloc, void *info, const struct _CFStreamCallBacks *cb, Boolean isReading); + +// Only available for streams created with _CFStreamCreateWithConstantCallbacks, above. cb's version must be 1 +CF_EXPORT void _CFStreamSetInfoPointer(struct _CFStream *stream, void *info, const struct _CFStreamCallBacks *cb); + + +/* +** _CFStreamSourceScheduleWithRunLoop +** +** Schedules the given run loop source on the given run loop and mode. It then +** adds the loop and mode pair to the runLoopsAndModes list. The list is +** simply a linear list of a loop reference followed by a mode reference. +** +** source Run loop source to be scheduled +** +** runLoopsAndModes List of run loop/mode pairs on which the source is scheduled +** +** runLoop Run loop on which the source is being scheduled +** +** runLoopMode Run loop mode on which the source is being scheduled +*/ +CF_EXPORT +void _CFStreamSourceScheduleWithRunLoop(CFRunLoopSourceRef source, CFMutableArrayRef runLoopsAndModes, CFRunLoopRef runLoop, CFStringRef runLoopMode); + + +/* +** _CFStreamSourceUnscheduleFromRunLoop +** +** Unschedule the given source from the given run loop and mode. It then will +** guarantee that the source remains scheduled on the list of run loop and mode +** pairs in the runLoopsAndModes list. The list is simply a linear list of a +** loop reference followed by a mode reference. +** +** source Run loop source to be unscheduled +** +** runLoopsAndModes List of run loop/mode pairs on which the source is scheduled +** +** runLoop Run loop from which the source is being unscheduled +** +** runLoopMode Run loop mode from which the source is being unscheduled +*/ +CF_EXPORT +void _CFStreamSourceUnscheduleFromRunLoop(CFRunLoopSourceRef source, CFMutableArrayRef runLoopsAndModes, CFRunLoopRef runLoop, CFStringRef runLoopMode); + + +/* +** _CFStreamSourceScheduleWithAllRunLoops +** +** Schedules the given run loop source on all the run loops and modes in the list. +** The list is simply a linear list of a loop reference followed by a mode reference. +** +** source Run loop source to be unscheduled +** +** runLoopsAndModes List of run loop/mode pairs on which the source is scheduled +*/ +CF_EXPORT +void _CFStreamSourceScheduleWithAllRunLoops(CFRunLoopSourceRef source, CFArrayRef runLoopsAndModes); + + +/* +** _CFStreamSourceUnscheduleFromRunLoop +** +** Unschedule the given source from all the run loops and modes in the list. +** The list is simply a linear list of a loop reference followed by a mode +** reference. +** +** source Run loop source to be unscheduled +** +** runLoopsAndModes List of run loop/mode pairs on which the source is scheduled +*/ +CF_EXPORT +void _CFStreamSourceUncheduleFromAllRunLoops(CFRunLoopSourceRef source, CFArrayRef runLoopsAndModes); + + +#define SECURITY_NONE (0) +#define SECURITY_SSLv2 (1) +#define SECURITY_SSLv3 (2) +#define SECURITY_SSLv32 (3) +#define SECURITY_TLS (4) + +extern const int kCFStreamErrorDomainSSL; + +#if defined(__cplusplus) +} +#endif + +#endif /* ! __COREFOUNDATION_CFSTREAMPRIV__ */ + diff --git a/String.subproj/CFCharacterSet.c b/String.subproj/CFCharacterSet.c index 4cbd4e6..1cbe9d3 100644 --- a/String.subproj/CFCharacterSet.c +++ b/String.subproj/CFCharacterSet.c @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -82,7 +80,7 @@ typedef struct { unsigned int _validEntriesBitmap; unsigned char _numOfAllocEntries; unsigned char _isAnnexInverted; - unsigned short _padding; + uint16_t _padding; } CFCharSetAnnexStruct; struct __CFCharacterSet { @@ -236,7 +234,7 @@ static Boolean __CFCSetIsBitmapEqualToRange(const UInt32 *bits, UniChar firstCha UInt32 lastCharMask; length = firstCharIndex % sizeof(UInt32); - firstCharMask = ((((UInt32)0xFF) << (firstChar & (BITSPERBYTE - 1))) << (((sizeof(UInt32) - 1) - length) * BITSPERBYTE)) | (0xFFFFFFFF >> ((length + 1) * BITSPERBYTE)); + firstCharMask = (((((UInt32)0xFF) << (firstChar & (BITSPERBYTE - 1))) & 0xFF) << (((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)); @@ -707,7 +705,7 @@ static uint8_t *__CFCreateCompactBitmap(CFAllocatorRef allocator, const uint8_t src += __kCFCompactBitmapPageSize; } - dst = (uint8_t *)CFAllocatorAllocate(allocator, __kCFCompactBitmapNumPages + (__kCFCompactBitmapPageSize * numPages), 0); + dst = (uint8_t *)CFAllocatorAllocate(allocator, __kCFCompactBitmapNumPages + (__kCFCompactBitmapPageSize * numPages), AUTO_MEMORY_UNSCANNED); if (numPages > 0) { uint8_t *dstBody = dst + __kCFCompactBitmapNumPages; @@ -800,7 +798,8 @@ static void __CFCSetRemoveNonBMPPlanesInRange(CFMutableCharacterSetRef cset, CFR static void __CFCSetMakeBitmap(CFMutableCharacterSetRef cset) { if (!__CFCSetIsBitmap(cset) || !__CFCSetBitmapBits(cset)) { - uint8_t *bitmap = CFAllocatorAllocate(CFGetAllocator(cset), __kCFBitmapSize, 0); + CFAllocatorRef allocator = CFGetAllocator(cset); + uint8_t *bitmap = CFAllocatorAllocate(allocator, __kCFBitmapSize, AUTO_MEMORY_UNSCANNED); __CFCSetGetBitmap(cset, bitmap); if (__CFCSetIsBuiltin(cset)) { @@ -814,7 +813,9 @@ static void __CFCSetMakeBitmap(CFMutableCharacterSetRef cset) { __CFCSetAllocateAnnexForPlane(cset, numPlanes - 1); for (idx = 1;idx < numPlanes;idx++) { - if (NULL == annexBitmap) annexBitmap = CFAllocatorAllocate(CFGetAllocator(cset), __kCFBitmapSize, 0); + if (NULL == annexBitmap) { + annexBitmap = CFAllocatorAllocate(allocator, __kCFBitmapSize, AUTO_MEMORY_UNSCANNED); + } result = CFUniCharGetBitmapForPlane(__CFCSetBuiltinType(cset), idx, annexBitmap, false); if (result == kCFUniCharBitmapEmpty) continue; if (result == kCFUniCharBitmapAll) { @@ -829,13 +830,13 @@ static void __CFCSetMakeBitmap(CFMutableCharacterSetRef cset) { __CFCSetPutHasHashValue(annexSet, false); annexBitmap = NULL; } - if (annexBitmap) CFAllocatorDeallocate(CFGetAllocator(cset), annexBitmap); + if (annexBitmap) CFAllocatorDeallocate(allocator, annexBitmap); } } else if (__CFCSetIsCompactBitmap(cset) && __CFCSetCompactBitmapBits(cset)) { - CFAllocatorDeallocate(CFGetAllocator(cset), __CFCSetCompactBitmapBits(cset)); + CFAllocatorDeallocate(allocator, __CFCSetCompactBitmapBits(cset)); __CFCSetPutCompactBitmapBits(cset, NULL); } else if (__CFCSetIsString(cset) && __CFCSetStringBuffer(cset)) { - CFAllocatorDeallocate(CFGetAllocator(cset), __CFCSetStringBuffer(cset)); + CFAllocatorDeallocate(allocator, __CFCSetStringBuffer(cset)); __CFCSetPutStringBuffer(cset, NULL); } else if (__CFCSetIsRange(cset)) { // We may have to allocate annex here Boolean needsToInvert = (!__CFCSetHasNonBMPPlane(cset) && __CFCSetIsInverted(cset) ? true : false); @@ -1418,7 +1419,7 @@ CFCharacterSetRef CFCharacterSetCreateWithCharactersInString(CFAllocatorRef allo CFMutableCharacterSetRef cset; if (!(cset = __CFCSetGenericCreate(allocator, __kCFCharSetClassString))) return NULL; - __CFCSetPutStringBuffer(cset, CFAllocatorAllocate(CFGetAllocator(cset), __kCFStringCharSetMax * sizeof(UniChar), 0)); + __CFCSetPutStringBuffer(cset, CFAllocatorAllocate(CFGetAllocator(cset), __kCFStringCharSetMax * sizeof(UniChar), AUTO_MEMORY_UNSCANNED)); __CFCSetPutStringLength(cset, length); CFStringGetCharacters(theString, CFRangeMake(0, length), __CFCSetStringBuffer(cset)); qsort(__CFCSetStringBuffer(cset), length, sizeof(UniChar), chcompar); @@ -1453,7 +1454,7 @@ CFCharacterSetRef CFCharacterSetCreateWithBitmapRepresentation(CFAllocatorRef al uint8_t *cBitmap; if (length < __kCFBitmapSize) { - bitmap = (uint8_t *)CFAllocatorAllocate(allocator, __kCFBitmapSize, 0); + bitmap = (uint8_t *)CFAllocatorAllocate(allocator, __kCFBitmapSize, AUTO_MEMORY_UNSCANNED); memmove(bitmap, CFDataGetBytePtr(theData), length); memset(bitmap + length, 0, __kCFBitmapSize - length); @@ -1470,7 +1471,7 @@ CFCharacterSetRef CFCharacterSetCreateWithBitmapRepresentation(CFAllocatorRef al cBitmap = __CFCreateCompactBitmap(allocator, CFDataGetBytePtr(theData)); if (cBitmap == NULL) { - bitmap = (uint8_t *)CFAllocatorAllocate(allocator, __kCFBitmapSize, 0); + bitmap = (uint8_t *)CFAllocatorAllocate(allocator, __kCFBitmapSize, AUTO_MEMORY_UNSCANNED); memmove(bitmap, CFDataGetBytePtr(theData), __kCFBitmapSize); __CFCSetPutBitmapBits(cset, bitmap); @@ -1490,7 +1491,7 @@ CFCharacterSetRef CFCharacterSetCreateWithBitmapRepresentation(CFAllocatorRef al --length; // Decrement the plane no byte if (length < __kCFBitmapSize) { - bitmap = (uint8_t *)CFAllocatorAllocate(allocator, __kCFBitmapSize, 0); + bitmap = (uint8_t *)CFAllocatorAllocate(allocator, __kCFBitmapSize, AUTO_MEMORY_UNSCANNED); memmove(bitmap, bytes, length); memset(bitmap + length, 0, __kCFBitmapSize - length); @@ -1507,7 +1508,7 @@ CFCharacterSetRef CFCharacterSetCreateWithBitmapRepresentation(CFAllocatorRef al cBitmap = __CFCreateCompactBitmap(allocator, bytes); if (cBitmap == NULL) { - bitmap = (uint8_t *)CFAllocatorAllocate(allocator, __kCFBitmapSize, 0); + bitmap = (uint8_t *)CFAllocatorAllocate(allocator, __kCFBitmapSize, AUTO_MEMORY_UNSCANNED); memmove(bitmap, bytes, __kCFBitmapSize); __CFCSetPutBitmapBits(annexSet, bitmap); @@ -1582,7 +1583,7 @@ CFMutableCharacterSetRef __CFCharacterSetCreateCopy(CFAllocatorRef alloc, CFChar break; case __kCFCharSetClassString: - __CFCSetPutStringBuffer(cset, CFAllocatorAllocate(alloc, __kCFStringCharSetMax * sizeof(UniChar), 0)); + __CFCSetPutStringBuffer(cset, CFAllocatorAllocate(alloc, __kCFStringCharSetMax * sizeof(UniChar), AUTO_MEMORY_UNSCANNED)); __CFCSetPutStringLength(cset, __CFCSetStringLength(theSet)); memmove(__CFCSetStringBuffer(cset), __CFCSetStringBuffer(theSet), __CFCSetStringLength(theSet) * sizeof(UniChar)); break; @@ -1592,7 +1593,7 @@ CFMutableCharacterSetRef __CFCharacterSetCreateCopy(CFAllocatorRef alloc, CFChar uint8_t * bitmap = (isMutable ? NULL : __CFCreateCompactBitmap(alloc, __CFCSetBitmapBits(theSet))); if (bitmap == NULL) { - bitmap = (uint8_t *)CFAllocatorAllocate(alloc, sizeof(uint8_t) * __kCFBitmapSize, 0); + bitmap = (uint8_t *)CFAllocatorAllocate(alloc, sizeof(uint8_t) * __kCFBitmapSize, AUTO_MEMORY_UNSCANNED); memmove(bitmap, __CFCSetBitmapBits(theSet), __kCFBitmapSize); __CFCSetPutBitmapBits(cset, bitmap); } else { @@ -1609,7 +1610,7 @@ CFMutableCharacterSetRef __CFCharacterSetCreateCopy(CFAllocatorRef alloc, CFChar if (compactBitmap) { uint32_t size = __CFCSetGetCompactBitmapSize(compactBitmap); - uint8_t *newBitmap = (uint8_t *)CFAllocatorAllocate(alloc, size, 0); + uint8_t *newBitmap = (uint8_t *)CFAllocatorAllocate(alloc, size, AUTO_MEMORY_UNSCANNED); memmove(newBitmap, compactBitmap, size); __CFCSetPutCompactBitmapBits(cset, newBitmap); @@ -2103,7 +2104,7 @@ void CFCharacterSetAddCharactersInRange(CFMutableCharacterSetRef theSet, CFRange } else if (__CFCSetIsString(theSet) && __CFCSetStringLength(theSet) + theRange.length < __kCFStringCharSetMax) { UniChar *buffer; if (!__CFCSetStringBuffer(theSet)) - __CFCSetPutStringBuffer(theSet, CFAllocatorAllocate(CFGetAllocator(theSet), __kCFStringCharSetMax * sizeof(UniChar), 0)); + __CFCSetPutStringBuffer(theSet, CFAllocatorAllocate(CFGetAllocator(theSet), __kCFStringCharSetMax * sizeof(UniChar), AUTO_MEMORY_UNSCANNED)); buffer = __CFCSetStringBuffer(theSet) + __CFCSetStringLength(theSet); __CFCSetPutStringLength(theSet, __CFCSetStringLength(theSet) + theRange.length); while (theRange.length--) *buffer++ = theRange.location++; @@ -2161,7 +2162,7 @@ void CFCharacterSetRemoveCharactersInRange(CFMutableCharacterSetRef theSet, CFRa } else if (__CFCSetIsString(theSet) && __CFCSetStringLength(theSet) + theRange.length < __kCFStringCharSetMax) { UniChar *buffer; if (!__CFCSetStringBuffer(theSet)) - __CFCSetPutStringBuffer(theSet, CFAllocatorAllocate(CFGetAllocator(theSet), __kCFStringCharSetMax * sizeof(UniChar), 0)); + __CFCSetPutStringBuffer(theSet, CFAllocatorAllocate(CFGetAllocator(theSet), __kCFStringCharSetMax * sizeof(UniChar), AUTO_MEMORY_UNSCANNED)); buffer = __CFCSetStringBuffer(theSet) + __CFCSetStringLength(theSet); __CFCSetPutStringLength(theSet, __CFCSetStringLength(theSet) + theRange.length); while (theRange.length--) *buffer++ = theRange.location++; @@ -2205,7 +2206,7 @@ void CFCharacterSetAddCharactersInString(CFMutableCharacterSetRef theSet, CFStr if (__CFCSetIsEmpty(theSet)) __CFCSetPutStringLength(theSet, 0); // Make sure to reset this if (!__CFCSetStringBuffer(theSet)) - __CFCSetPutStringBuffer(theSet, CFAllocatorAllocate(CFGetAllocator(theSet), __kCFStringCharSetMax * sizeof(UniChar), 0)); + __CFCSetPutStringBuffer(theSet, CFAllocatorAllocate(CFGetAllocator(theSet), __kCFStringCharSetMax * sizeof(UniChar), AUTO_MEMORY_UNSCANNED)); buffer = __CFCSetStringBuffer(theSet) + __CFCSetStringLength(theSet); __CFCSetPutClassType(theSet, __kCFCharSetClassString); @@ -2249,7 +2250,7 @@ void CFCharacterSetRemoveCharactersInString(CFMutableCharacterSetRef theSet, CFS if (__CFCSetIsEmpty(theSet)) __CFCSetPutStringLength(theSet, 0); // Make sure to reset this if (!__CFCSetStringBuffer(theSet)) - __CFCSetPutStringBuffer(theSet, CFAllocatorAllocate(CFGetAllocator(theSet), __kCFStringCharSetMax * sizeof(UniChar), 0)); + __CFCSetPutStringBuffer(theSet, CFAllocatorAllocate(CFGetAllocator(theSet), __kCFStringCharSetMax * sizeof(UniChar), AUTO_MEMORY_UNSCANNED)); buffer = __CFCSetStringBuffer(theSet) + __CFCSetStringLength(theSet); __CFCSetPutClassType(theSet, __kCFCharSetClassString); @@ -2455,12 +2456,12 @@ void CFCharacterSetIntersect(CFMutableCharacterSetRef theSet, CFCharacterSetRef case __kCFCharSetClassString: __CFCSetPutStringLength(theSet, __CFCSetStringLength(theOtherSet)); if (!__CFCSetStringBuffer(theSet)) - __CFCSetPutStringBuffer(theSet, CFAllocatorAllocate(CFGetAllocator(theSet), __kCFStringCharSetMax * sizeof(UniChar), 0)); + __CFCSetPutStringBuffer(theSet, CFAllocatorAllocate(CFGetAllocator(theSet), __kCFStringCharSetMax * sizeof(UniChar), AUTO_MEMORY_UNSCANNED)); memmove(__CFCSetStringBuffer(theSet), __CFCSetStringBuffer(theOtherSet), __CFCSetStringLength(theSet) * sizeof(UniChar)); break; case __kCFCharSetClassBitmap: - __CFCSetPutBitmapBits(theSet, CFAllocatorAllocate(CFGetAllocator(theSet), sizeof(uint8_t) * __kCFBitmapSize, 0)); + __CFCSetPutBitmapBits(theSet, CFAllocatorAllocate(CFGetAllocator(theSet), sizeof(uint8_t) * __kCFBitmapSize, AUTO_MEMORY_UNSCANNED)); memmove(__CFCSetBitmapBits(theSet), __CFCSetBitmapBits(theOtherSet), __kCFBitmapSize); break; @@ -2468,7 +2469,7 @@ void CFCharacterSetIntersect(CFMutableCharacterSetRef theSet, CFCharacterSetRef 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); + newBitmap = (uint8_t *)CFAllocatorAllocate(CFGetAllocator(theSet), sizeof(uint8_t) * size, AUTO_MEMORY_UNSCANNED); __CFCSetPutBitmapBits(theSet, newBitmap); memmove(newBitmap, cBitmap, size); } @@ -2593,7 +2594,7 @@ void CFCharacterSetInvert(CFMutableCharacterSetRef theSet) { UInt32 *bitmap = (UInt32*) __CFCSetBitmapBits(theSet); if (NULL == bitmap) { - bitmap = (UInt32 *)CFAllocatorAllocate(CFGetAllocator(theSet), __kCFBitmapSize, 0); + bitmap = (UInt32 *)CFAllocatorAllocate(CFGetAllocator(theSet), __kCFBitmapSize, AUTO_MEMORY_UNSCANNED); __CFCSetPutBitmapBits(theSet, (uint8_t *)bitmap); for (idx = 0;idx < count;idx++) bitmap[idx] = 0xFFFFFFFF; } else { @@ -2670,7 +2671,6 @@ CFCharacterSetKeyedCodingType _CFCharacterSetGetKeyedCodingType(CFCharacterSetRe } } -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)); } diff --git a/String.subproj/CFCharacterSet.h b/String.subproj/CFCharacterSet.h index 39adc8a..a2aab31 100644 --- a/String.subproj/CFCharacterSet.h +++ b/String.subproj/CFCharacterSet.h @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -23,7 +21,7 @@ * @APPLE_LICENSE_HEADER_END@ */ /* CFCharacterSet.h - Copyright (c) 1999-2003, Apple, Inc. All rights reserved. + Copyright (c) 1999-2005, Apple, Inc. All rights reserved. */ /*! diff --git a/String.subproj/CFCharacterSetPriv.h b/String.subproj/CFCharacterSetPriv.h index 503121b..d343c69 100644 --- a/String.subproj/CFCharacterSetPriv.h +++ b/String.subproj/CFCharacterSetPriv.h @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -23,7 +21,7 @@ * @APPLE_LICENSE_HEADER_END@ */ /* CFCharacterSetPriv.h - Copyright (c) 1998-2003, Apple, Inc. All rights reserved. + Copyright (c) 1998-2005, Apple, Inc. All rights reserved. */ #if !defined(__COREFOUNDATION_CFCHARACTERSET_PRIV__) @@ -86,7 +84,6 @@ CF_EXPORT CFCharacterSetKeyedCodingType _CFCharacterSetGetKeyedCodingType(CFChar 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); diff --git a/String.subproj/CFString.c b/String.subproj/CFString.c index ff6ed41..881f27a 100644 --- a/String.subproj/CFString.c +++ b/String.subproj/CFString.c @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -25,6 +23,8 @@ /* CFString.c Copyright 1998-2002, Apple, Inc. All rights reserved. Responsibility: Ali Ozer + +!!! For performance reasons, it's important that all functions marked CF_INLINE in this file are inlined. */ #include @@ -34,20 +34,27 @@ #include "CFUniChar.h" #include "CFUnicodeDecomposition.h" #include "CFUnicodePrecomposition.h" -#include "CFUtilities.h" +#include "CFUtilitiesPriv.h" #include "CFInternal.h" #include #include -/* strncmp, etc */ #include -#if defined(__MACH__) || defined(__LINUX__) || defined(__FREEBSD__) +#if defined (__MACOS8__) + #include // For GetScriptManagerVariable + #include // For logging + #include +#include +#include +#elif defined(__MACH__) || defined(__LINUX__) || defined(__FREEBSD__) #include #endif #if defined(__WIN32__) #include #endif /* __WIN32__ */ +#if defined(__MACH__) extern size_t malloc_good_size(size_t size); +#endif extern void __CFStrConvertBytesToUnicode(const uint8_t *bytes, UniChar *buffer, CFIndex numChars); #if defined(DEBUG) @@ -64,6 +71,7 @@ static Boolean __CFConstantStringTableBeingFreed = false; #endif + // This section is for CFString compatibility and other behaviors... static CFOptionFlags _CFStringCompatibilityMask = 0; @@ -98,18 +106,18 @@ struct __CFString { void *buffer; UInt32 length; CFAllocatorRef contentsDeallocator; // Just the dealloc func is used - } externalImmutable1; + } notInlineImmutable1; struct { void *buffer; CFAllocatorRef contentsDeallocator; // Just the dealloc func is used - } externalImmutable2; + } notInlineImmutable2; 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; + } notInlineMutable; } variants; }; @@ -120,7 +128,7 @@ U = is Unicode N = has NULL byte L = has length byte D = explicit deallocator for contents (for mutable objects, allocator) -X = is external mutable +X = UNUSED Also need (only for mutable) F = is fixed @@ -135,16 +143,21 @@ B6 B5 0 1 E (freed with default allocator) 1 0 E (not freed) 1 1 E D + +!!! Note: Constant CFStrings use the bit patterns: +C8 (11001000 = default allocator, not inline, not freed contents; 8-bit; has NULL byte; doesn't have length; is immutable) +D0 (11010000 = default allocator, not inline, not freed contents; Unicode; is immutable) +The bit usages should not be modified in a way that would effect these bit patterns. */ enum { __kCFFreeContentsWhenDoneMask = 0x020, __kCFFreeContentsWhenDone = 0x020, __kCFContentsMask = 0x060, - __kCFHasInlineData = 0x000, - __kCFHasExternalDataNoFree = 0x040, // Don't free - __kCFHasExternalDataDefaultFree = 0x020, // Use allocator's free function - __kCFHasExternalDataCustomFree = 0x060, // Use a specially provided free function + __kCFHasInlineContents = 0x000, + __kCFNotInlineContentsNoFree = 0x040, // Don't free + __kCFNotInlineContentsDefaultFree = 0x020, // Use allocator's free function + __kCFNotInlineContentsCustomFree = 0x060, // Use a specially provided free function __kCFHasContentsAllocatorMask = 0x060, __kCFHasContentsAllocator = 0x060, // (For mutable strings) use a specially provided allocator __kCFHasContentsDeallocatorMask = 0x060, @@ -157,9 +170,8 @@ enum { __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 + // !!! Bit 0x02 has been freed up + // These are in variants.notInlineMutable.gapEtc __kCFGapMask = 0x00ffffff, __kCFGapBitNumber = 24, __kCFDesiredCapacityMask = 0x00ffffff, // Currently gap and fixed share same bits as gap not implemented @@ -168,8 +180,11 @@ enum { __kCFIsFixed = 0x80000000, __kCFHasGapMask = 0x40000000, __kCFHasGap = 0x40000000, - __kCFCapacityProvidedExternallyMask = 0x20000000, // Set if the external buffer is set explicitly by the developer - __kCFCapacityProvidedExternally = 0x20000000 + __kCFCapacityProvidedExternallyMask = 0x20000000, // Set if the external buffer capacity is set explicitly by the developer + __kCFCapacityProvidedExternally = 0x20000000, + __kCFIsExternalMutableMask = 0x10000000, // Determines whether the buffer is controlled by the developer + __kCFIsExternalMutable = 0x10000000 + // 0x0f000000: 4 additional bits available for use in mutable strings }; @@ -183,8 +198,7 @@ enum { /* 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 __CFStrIsInline(CFStringRef str) {return (str->base._info & __kCFContentsMask) == __kCFHasInlineContents;} 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;} @@ -192,6 +206,7 @@ CF_INLINE Boolean __CFStrIsEightBit(CFStringRef str) {return (str->base._info & 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 Boolean __CFStrIsConstant(CFStringRef str) {return (str->base._rc) == 0;} 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 @@ -200,13 +215,13 @@ CF_INLINE SInt32 __CFStrSkipAnyLengthByte(CFStringRef str) {return ((str->base._ 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; + } else { // Not inline; pointer is always word 2 + return str->variants.notInlineImmutable1.buffer; } } static CFAllocatorRef *__CFStrContentsDeallocatorPtr(CFStringRef str) { - return __CFStrHasExplicitLength(str) ? &(((CFMutableStringRef)str)->variants.externalImmutable1.contentsDeallocator) : &(((CFMutableStringRef)str)->variants.externalImmutable2.contentsDeallocator); } + return __CFStrHasExplicitLength(str) ? &(((CFMutableStringRef)str)->variants.notInlineImmutable1.contentsDeallocator) : &(((CFMutableStringRef)str)->variants.notInlineImmutable2.contentsDeallocator); } // Assumption: Called with immutable strings only, and on strings that are known to have a contentsDeallocator CF_INLINE CFAllocatorRef __CFStrContentsDeallocator(CFStringRef str) { @@ -221,7 +236,7 @@ CF_INLINE void __CFStrSetContentsDeallocator(CFStringRef str, CFAllocatorRef con 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); + return (CFAllocatorRef *)&(str->variants.notInlineMutable.contentsAllocator); } // Assumption: Called with strings that have a contents allocator; also, contents allocator follows custom @@ -241,9 +256,7 @@ CF_INLINE CFIndex __CFStrLength(CFStringRef 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; + return str->variants.notInlineImmutable1.length; } } else { return (CFIndex)(*((uint8_t *)__CFStrContents(str))); @@ -255,48 +268,49 @@ CF_INLINE CFIndex __CFStrLength2(CFStringRef str, const void *buffer) { 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; + return str->variants.notInlineImmutable1.length; } } else { return (CFIndex)(*((uint8_t *)buffer)); } } -Boolean __CFStringIsMutable(CFStringRef str) { - return __CFStrIsMutable(str); -} Boolean __CFStringIsEightBit(CFStringRef str) { return __CFStrIsEightBit(str); } -/* Sets the external content pointer for immutable or mutable strings. +/* Sets the 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 __CFStrSetContentPtr(CFStringRef str, const void *p) +{ + // XXX_PCB catch all writes for mutable string case. + CF_WRITE_BARRIER_BASE_ASSIGN(__CFGetAllocator(str), str, ((CFMutableStringRef)str)->variants.notInlineImmutable1.buffer, (void *)p); +} CF_INLINE void __CFStrSetInfoBits(CFStringRef str, UInt32 v) {__CFBitfieldSetValue(((CFMutableStringRef)str)->base._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; + ((CFMutableStringRef)str)->variants.notInlineImmutable1.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;} +CF_INLINE Boolean __CFStrIsFixed(CFStringRef str) {return (str->variants.notInlineMutable.gapEtc & __kCFIsFixedMask) == __kCFIsFixed;} +CF_INLINE Boolean __CFStrHasContentsAllocator(CFStringRef str) {return (str->base._info & __kCFHasContentsAllocatorMask) == __kCFHasContentsAllocator;} +CF_INLINE Boolean __CFStrIsExternalMutable(CFStringRef str) {return (str->variants.notInlineMutable.gapEtc & __kCFIsExternalMutableMask) == __kCFIsExternalMutable;} // 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 Boolean __CFStrCapacityProvidedExternally(CFStringRef str) {return (str->variants.notInlineMutable.gapEtc & __kCFCapacityProvidedExternallyMask) == __kCFCapacityProvidedExternally;} +CF_INLINE void __CFStrSetCapacityProvidedExternally(CFMutableStringRef str) {str->variants.notInlineMutable.gapEtc |= __kCFCapacityProvidedExternally;} +CF_INLINE void __CFStrClearCapacityProvidedExternally(CFMutableStringRef str) {str->variants.notInlineMutable.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 __CFStrSetIsFixed(CFMutableStringRef str) {str->variants.notInlineMutable.gapEtc |= __kCFIsFixed;} +CF_INLINE void __CFStrSetIsExternalMutable(CFMutableStringRef str) {str->variants.notInlineMutable.gapEtc |= __kCFIsExternalMutable;} +CF_INLINE void __CFStrSetHasGap(CFMutableStringRef str) {str->variants.notInlineMutable.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);} @@ -313,7 +327,12 @@ static void *__CFStrAllocateMutableContents(CFMutableStringRef str, CFIndex size static void __CFStrDeallocateMutableContents(CFMutableStringRef str, void *buffer) { CFAllocatorRef alloc = (__CFStrHasContentsAllocator(str)) ? __CFStrContentsAllocator(str) : __CFGetAllocator(str); - CFAllocatorDeallocate(alloc, buffer); + if (CF_IS_COLLECTABLE_ALLOCATOR(alloc)) { + // GC: for finalization safety, let collector reclaim the buffer in the next GC cycle. + auto_zone_release(__CFCollectableZone, buffer); + } else { + CFAllocatorDeallocate(alloc, buffer); + } } @@ -322,10 +341,10 @@ static void __CFStrDeallocateMutableContents(CFMutableStringRef str, void *buffe /* "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);} +CF_INLINE CFIndex __CFStrCapacity(CFStringRef str) {return str->variants.notInlineMutable.capacityFields;} +CF_INLINE void __CFStrSetCapacity(CFMutableStringRef str, CFIndex cap) {str->variants.notInlineMutable.capacityFields = cap;} +CF_INLINE CFIndex __CFStrDesiredCapacity(CFStringRef str) {return __CFBitfieldGetValue(str->variants.notInlineMutable.gapEtc, __kCFDesiredCapacityBitNumber, 0);} +CF_INLINE void __CFStrSetDesiredCapacity(CFMutableStringRef str, CFIndex size) {__CFBitfieldSetValue(str->variants.notInlineMutable.gapEtc, __kCFDesiredCapacityBitNumber, 0, size);} @@ -341,11 +360,6 @@ enum { 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; @@ -397,6 +411,8 @@ CFStringEncoding CFStringFileSystemEncoding(void) { CFIndex CFStringGetMaximumSizeForEncoding(CFIndex length, CFStringEncoding encoding) { if (encoding == kCFStringEncodingUTF8) { return _CFExecutableLinkedOnOrAfter(CFSystemVersionPanther) ? (length * 3) : (length * 6); // 1 Unichar could expand to 3 bytes; we return 6 for older apps for compatibility + } else if ((encoding == kCFStringEncodingUTF32) || (encoding == kCFStringEncodingUTF32BE) || (encoding == kCFStringEncodingUTF32LE)) { // UTF-32 + return length * sizeof(UTF32Char); } else { encoding &= 0xFFF; // Mask off non-base part } @@ -423,10 +439,9 @@ CFIndex CFStringGetMaximumSizeForEncoding(CFIndex length, CFStringEncoding encod /* Returns whether the indicated encoding can be stored in 8-bit chars */ CF_INLINE Boolean __CFStrEncodingCanBeStoredInEightBit(CFStringEncoding encoding) { - switch (encoding) { + switch (encoding & 0xFFF) { // just use encoding base case kCFStringEncodingInvalidId: case kCFStringEncodingUnicode: - case kCFStringEncodingUTF8: case kCFStringEncodingNonLossyASCII: return false; @@ -489,7 +504,7 @@ CF_INLINE Boolean __CFCanUseLengthByte(CFIndex len) { #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 __CFAssertIsStringAndExternalMutable(cf) {__CFGenericValidateType(cf, __kCFStringTypeID); CFAssert1(__CFStrIsMutable(cf) && __CFStrIsExternalMutable(cf), __kCFLogAssertion, "%s(): string not external mutable", __PRETTY_FUNCTION__);} #define __CFAssertIsNotNegative(idx) CFAssert2(idx >= 0, __kCFLogAssertion, "%s(): index %d is negative", __PRETTY_FUNCTION__, idx) #define __CFAssertIfFixedLengthIsOK(cf, reqLen) CFAssert2(!__CFStrIsFixed(cf) || (reqLen <= __CFStrDesiredCapacity(cf)), __kCFLogAssertion, "%s(): length %d too large", __PRETTY_FUNCTION__, reqLen) @@ -518,8 +533,10 @@ CF_INLINE CFIndex __CFStrNewCapacity(CFMutableStringRef str, CFIndex reqCapacity } if (__CFStrHasContentsAllocator(str)) { /* Also apply any preferred size from the allocator; should we do something for */ newCapacity = CFAllocatorGetPreferredSizeForSize(__CFStrContentsAllocator(str), newCapacity, 0); +#if defined(__MACH__) } else { newCapacity = malloc_good_size(newCapacity); +#endif } return newCapacity; // If packing: __CFStrUnpackNumber(__CFStrPackNumber(newCapacity)); } @@ -857,62 +874,157 @@ static Boolean __CFStringEqual(CFTypeRef cf1, CFTypeRef cf2) { /* 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 +If the length is less than or equal to 24, 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; +If the length is greater than 24, then the above algorithm applies to +characters 0..7 and length-16..length-1; thus the first 8 and last 16 characters. - /* !!! We do not need an IsString assertion here, as this is called by the CFBase runtime only */ +Note that the loops below are unrolled; and: 257^2 = 66049; 257^3 = 16974593; 257^4 = 4362470401; 67503105 is 257^4 - 256^4 +If hashcode is changed from UInt32 to something else, this last piece needs to be readjusted. - 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]]; +NOTE: The hash algorithm used to be duplicated in CF and Foundation; but now it should only be in the four functions below. +*/ + +/* In this function, actualLen is the length of the original string; but len is the number of characters in buffer. The buffer is expected to contain the parts of the string relevant to hashing. +*/ +CF_INLINE CFHashCode __CFStrHashCharacters(const UniChar *uContents, CFIndex len, CFIndex actualLen) { + CFHashCode result = actualLen; + if (len < 24) { + const UniChar *end4 = uContents + (len & ~3); + const UniChar *end = uContents + len; + while (uContents < end4) { // First count in fours + result = result * 67503105 + uContents[0] * 16974593 + uContents[1] * 66049 + uContents[2] * 257 + uContents[3]; + uContents += 4; } -#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"); - } + while (uContents < end) { // Then for the last <4 chars, count in ones... + result = result * 257 + *uContents++; } + } else { + result = result * 67503105 + uContents[0] * 16974593 + uContents[1] * 66049 + uContents[2] * 257 + uContents[3]; + result = result * 67503105 + uContents[4] * 16974593 + uContents[5] * 66049 + uContents[6] * 257 + uContents[7]; + uContents += (len - 16); + result = result * 67503105 + uContents[0] * 16974593 + uContents[1] * 66049 + uContents[2] * 257 + uContents[3]; + result = result * 67503105 + uContents[4] * 16974593 + uContents[5] * 66049 + uContents[6] * 257 + uContents[7]; + result = result * 67503105 + uContents[8] * 16974593 + uContents[9] * 66049 + uContents[10] * 257 + uContents[11]; + result = result * 67503105 + uContents[12] * 16974593 + uContents[13] * 66049 + uContents[14] * 257 + uContents[15]; + } + return result + (result << (actualLen & 31)); +} + +/* This hashes cString in the eight bit string encoding. It also includes the little debug-time sanity check. +*/ +CF_INLINE CFHashCode __CFStrHashEightBit(const uint8_t *contents, CFIndex len) { +#if defined(DEBUG) + const uint8_t *origContents = contents; #endif + CFHashCode result = len; + if (len < 24) { + const uint8_t *end4 = contents + (len & ~3); + const uint8_t *end = contents + len; + while (contents < end4) { // First count in fours + result = result * 67503105 + __CFCharToUniCharTable[contents[0]] * 16974593 + __CFCharToUniCharTable[contents[1]] * 66049 + __CFCharToUniCharTable[contents[2]] * 257 + __CFCharToUniCharTable[contents[3]]; + contents += 4; + } + while (contents < end) { // Then for the last <4 chars, count single chars + result = result * 257 + __CFCharToUniCharTable[*contents++]; + } } else { - const UniChar *uContents = (UniChar *)contents; - if (len <= 16) { - for (cnt = 0; cnt < len; cnt++) result = result * 257 + uContents[cnt]; + result = result * 67503105 + __CFCharToUniCharTable[contents[0]] * 16974593 + __CFCharToUniCharTable[contents[1]] * 66049 + __CFCharToUniCharTable[contents[2]] * 257 + __CFCharToUniCharTable[contents[3]]; + result = result * 67503105 + __CFCharToUniCharTable[contents[4]] * 16974593 + __CFCharToUniCharTable[contents[5]] * 66049 + __CFCharToUniCharTable[contents[6]] * 257 + __CFCharToUniCharTable[contents[7]]; + contents += (len - 16); + result = result * 67503105 + __CFCharToUniCharTable[contents[0]] * 16974593 + __CFCharToUniCharTable[contents[1]] * 66049 + __CFCharToUniCharTable[contents[2]] * 257 + __CFCharToUniCharTable[contents[3]]; + result = result * 67503105 + __CFCharToUniCharTable[contents[4]] * 16974593 + __CFCharToUniCharTable[contents[5]] * 66049 + __CFCharToUniCharTable[contents[6]] * 257 + __CFCharToUniCharTable[contents[7]]; + result = result * 67503105 + __CFCharToUniCharTable[contents[8]] * 16974593 + __CFCharToUniCharTable[contents[9]] * 66049 + __CFCharToUniCharTable[contents[10]] * 257 + __CFCharToUniCharTable[contents[11]]; + result = result * 67503105 + __CFCharToUniCharTable[contents[12]] * 16974593 + __CFCharToUniCharTable[contents[13]] * 66049 + __CFCharToUniCharTable[contents[14]] * 257 + __CFCharToUniCharTable[contents[15]]; + } +#if defined(DEBUG) + if (!__CFCharToUniCharFunc) { // A little sanity verification: If this is not set, trying to hash high byte chars would be a bad idea + CFIndex cnt; + Boolean err = false; + contents = origContents; + if (len <= 24) { + for (cnt = 0; cnt < len; cnt++) if (contents[cnt] >= 128) err = true; } else { - for (cnt = 0; cnt < 8; cnt++) result = result * 257 + uContents[cnt]; - for (cnt = len - 8; cnt < len; cnt++) result = result * 257 + uContents[cnt]; + for (cnt = 0; cnt < 8; cnt++) if (contents[cnt] >= 128) err = true; + for (cnt = len - 16; cnt < len; cnt++) if (contents[cnt] >= 128) err = true; + } + if (err) { + // Can't do log here, as it might be too early + fprintf(stderr, "Warning: CFHash() attempting to hash CFString containing high bytes before properly initialized to do so\n"); } } - result += (result << (len & 31)); - return result; +#endif + return result + (result << (len & 31)); +} + +CFHashCode CFStringHashISOLatin1CString(const uint8_t *bytes, CFIndex len) { + CFHashCode result = len; + if (len < 24) { + const uint8_t *end4 = bytes + (len & ~3); + const uint8_t *end = bytes + len; + while (bytes < end4) { // First count in fours + result = result * 67503105 + bytes[0] * 16974593 + bytes[1] * 66049 + bytes[2] * 257 + bytes[3]; + bytes += 4; + } + while (bytes < end) { // Then for the last <4 chars, count in ones... + result = result * 257 + *bytes++; + } + } else { + result = result * 67503105 + bytes[0] * 16974593 + bytes[1] * 66049 + bytes[2] * 257 + bytes[3]; + result = result * 67503105 + bytes[4] * 16974593 + bytes[5] * 66049 + bytes[6] * 257 + bytes[7]; + bytes += (len - 16); + result = result * 67503105 + bytes[0] * 16974593 + bytes[1] * 66049 + bytes[2] * 257 + bytes[3]; + result = result * 67503105 + bytes[4] * 16974593 + bytes[5] * 66049 + bytes[6] * 257 + bytes[7]; + result = result * 67503105 + bytes[8] * 16974593 + bytes[9] * 66049 + bytes[10] * 257 + bytes[11]; + result = result * 67503105 + bytes[12] * 16974593 + bytes[13] * 66049 + bytes[14] * 257 + bytes[15]; + } + return result + (result << (len & 31)); +} + +CFHashCode CFStringHashCString(const uint8_t *bytes, CFIndex len) { + return __CFStrHashEightBit(bytes, len); +} + +CFHashCode CFStringHashCharacters(const UniChar *characters, CFIndex len) { + return __CFStrHashCharacters(characters, len, len); +} + +/* This is meant to be called from NSString or subclassers only. It is an error for this to be called without the ObjC runtime or an argument which is not an NSString or subclass. It can be called with NSCFString, although that would be inefficient (causing indirection) and won't normally happen anyway, as NSCFString overrides hash. +*/ +CFHashCode CFStringHashNSString(CFStringRef str) { + UniChar buffer[24]; + CFIndex bufLen; // Number of characters in the buffer for hashing + CFIndex len; // Actual length of the string + + CF_OBJC_CALL0(CFIndex, len, str, "length"); + if (len <= 24) { + CF_OBJC_VOIDCALL2(str, "getCharacters:range:", buffer, CFRangeMake(0, len)); + bufLen = len; + } else { + CF_OBJC_VOIDCALL2(str, "getCharacters:range:", buffer, CFRangeMake(0, 8)); + CF_OBJC_VOIDCALL2(str, "getCharacters:range:", buffer+8, CFRangeMake(len-16, 16)); + bufLen = 24; + } + return __CFStrHashCharacters(buffer, bufLen, len); +} + +CFHashCode __CFStringHash(CFTypeRef cf) { + /* !!! We do not need an IsString assertion here, as this is called by the CFBase runtime only */ + CFStringRef str = cf; + const uint8_t *contents = __CFStrContents(str); + CFIndex len = __CFStrLength2(str, contents); + + if (__CFStrIsEightBit(str)) { + contents += __CFStrSkipAnyLengthByte(str); + return __CFStrHashEightBit(contents, len); + } else { + return __CFStrHashCharacters((const UniChar *)contents, len, len); + } } @@ -1063,14 +1175,20 @@ __private_extern__ CFStringRef __CFStringCreateImmutableFunnel3( 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 (numBytes >= __kCFVarWidthLocalBufferSize) { + mem = ptr = (uint8_t *)CFAllocatorAllocate(alloc, numBytes, 0); + if (__CFOASafe) __CFSetLastAllocationEventName(mem, "CFString (store)"); + } else { + mem = ptr = (uint8_t *)(vBuf.localBuffer); + } + // Copy the Unicode bytes into the new ASCII buffer 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); } + // Now make everything look like we had an ASCII buffer to start with bytes = mem; encoding = kCFStringEncodingASCII; contentsDeallocator = alloc; // At this point we are using the string's allocator, as the original buffer is gone... @@ -1110,31 +1228,47 @@ __private_extern__ CFStringRef __CFStringCreateImmutableFunnel3( } } +#ifdef STRING_SIZE_STATS + // Dump alloced CFString size info every so often + static int cnt = 0; + static unsigned sizes[256] = {0}; + int allocedSize = size + sizeof(CFRuntimeBase); + if (allocedSize < 255) sizes[allocedSize]++; else sizes[255]++; + if ((++cnt % 1000) == 0) { + printf ("\nTotal: %d\n", cnt); + int i; for (i = 0; i < 256; i++) printf("%03d: %5d%s", i, sizes[i], ((i % 8) == 7) ? "\n" : " "); + } +#endif + // Finally, allocate! str = (CFMutableStringRef)_CFRuntimeCreateInstance(alloc, __kCFStringTypeID, size, NULL); - if (__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 (str) { + if (__CFOASafe) __CFSetLastAllocationEventName(str, "CFString (immutable)"); + + __CFStrSetInfoBits(str, + (useInlineData ? __kCFHasInlineContents : (contentsDeallocator == alloc ? __kCFNotInlineContentsDefaultFree : (contentsDeallocator == kCFAllocatorNull ? __kCFNotInlineContentsNoFree : __kCFNotInlineContentsCustomFree))) | + ((encoding == kCFStringEncodingUnicode) ? __kCFIsUnicode : 0) | + (useNullByte ? __kCFHasNullByte : 0) | + (useLengthByte ? __kCFHasLengthByte : 0)); + + if (!useLengthByte) { + CFIndex length = numBytes - (hasLengthByte ? 1 : 0); + if (encoding == kCFStringEncodingUnicode) length /= sizeof(UniChar); + __CFStrSetExplicitLength(str, length); + } - if (useInlineData) { - uint8_t *contents = (uint8_t *)__CFStrContents(str); - if (useLengthByte && !hasLengthByte) *contents++ = numBytes; - memmove(contents, bytes, numBytes); - if (useNullByte) contents[numBytes] = 0; + 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)); + } } else { - __CFStrSetContentPtr(str, bytes); - if (contentsDeallocator != alloc && contentsDeallocator != kCFAllocatorNull) __CFStrSetContentsDeallocator(str, CFRetain(contentsDeallocator)); + if (contentsDeallocator != kCFAllocatorNull) CFAllocatorDeallocate(contentsDeallocator, (void *)bytes); } if (vBuf.shouldFreeChars) CFAllocatorDeallocate(vBuf.allocator, (void *)bytes); @@ -1193,6 +1327,10 @@ CFStringRef _CFStringCreateWithBytesNoCopy(CFAllocatorRef alloc, const uint8_t return __CFStringCreateImmutableFunnel3(alloc, bytes, numBytes, encoding, externalFormat, true, false, false, true, contentsDeallocator, 0); } +CFStringRef CFStringCreateWithBytesNoCopy(CFAllocatorRef alloc, const uint8_t *bytes, CFIndex numBytes, CFStringEncoding encoding, Boolean externalFormat, CFAllocatorRef contentsDeallocator) { + return _CFStringCreateWithBytesNoCopy(alloc, bytes, numBytes, encoding, externalFormat, contentsDeallocator); +} + CFStringRef CFStringCreateWithFormatAndArguments(CFAllocatorRef alloc, CFDictionaryRef formatOptions, CFStringRef format, va_list arguments) { return _CFStringCreateWithFormatAndArgumentsAux(alloc, NULL, formatOptions, format, arguments); } @@ -1220,9 +1358,16 @@ CFStringRef CFStringCreateWithFormat(CFAllocatorRef alloc, CFDictionaryRef form return result; } - CFStringRef CFStringCreateWithSubstring(CFAllocatorRef alloc, CFStringRef str, CFRange range) { - CF_OBJC_FUNCDISPATCH1(__kCFStringTypeID, CFStringRef , str, "_createSubstringWithRange:", CFRangeMake(range.location, range.length)); + if (CF_IS_OBJC(__kCFStringTypeID, str)) { + static SEL s = NULL; + CFStringRef (*func)(void *, SEL, ...) = (void *)__CFSendObjCMsg; + if (!s) s = sel_registerName("_createSubstringWithRange:"); + CFStringRef result = func((void *)str, s, CFRangeMake(range.location, range.length)); + if (result && CF_USING_COLLECTABLE_MEMORY) CFRetain(result); // needs hard retain. + return result; + } +// CF_OBJC_FUNCDISPATCH1(__kCFStringTypeID, CFStringRef , str, "_createSubstringWithRange:", CFRangeMake(range.location, range.length)); __CFAssertIsString(str); __CFAssertRangeIsInStringBounds(str, range.location, range.length); @@ -1239,12 +1384,20 @@ CFStringRef CFStringCreateWithSubstring(CFAllocatorRef alloc, CFStringRef str, C } CFStringRef CFStringCreateCopy(CFAllocatorRef alloc, CFStringRef str) { - CF_OBJC_FUNCDISPATCH0(__kCFStringTypeID, CFStringRef, str, "copy"); + if (CF_IS_OBJC(__kCFStringTypeID, str)) { + static SEL s = NULL; + CFStringRef (*func)(void *, SEL, ...) = (void *)__CFSendObjCMsg; + if (!s) s = sel_registerName("copy"); + CFStringRef result = func((void *)str, s); + if (result && CF_USING_COLLECTABLE_MEMORY) CFRetain(result); // needs hard retain. + return result; + } +// 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 + (__CFStrIsInline(str) || __CFStrFreeContentsWhenDone(str) || __CFStrIsConstant(str))) { // and the characters are inline, or are owned by the string, or the string is constant CFRetain(str); // Then just retain instead of making a true copy return str; } @@ -1316,14 +1469,22 @@ static CFSpinLock_t _CFSTRLock = 0; CFStringRef __CFStringMakeConstantString(const char *cStr) { CFStringRef result; +#if defined(DEBUG) + //StringTest checks that we share kCFEmptyString, which is defeated by constantStringAllocatorForDebugging + if ('\0' == *cStr) return kCFEmptyString; +#endif if (constantStringTable == NULL) { CFDictionaryKeyCallBacks constantStringCallBacks = {0, NULL, NULL, __cStrCopyDescription, __cStrEqual, __cStrHash}; - constantStringTable = CFDictionaryCreateMutable(NULL, 0, &constantStringCallBacks, &kCFTypeDictionaryValueCallBacks); - _CFDictionarySetCapacity(constantStringTable, 2500); // avoid lots of rehashing + CFMutableDictionaryRef table = CFDictionaryCreateMutable(NULL, 0, &constantStringCallBacks, &kCFTypeDictionaryValueCallBacks); + _CFDictionarySetCapacity(table, 2500); // avoid lots of rehashing + __CFSpinLock(&_CFSTRLock); + if (constantStringTable == NULL) constantStringTable = table; + __CFSpinUnlock(&_CFSTRLock); + if (constantStringTable != table) CFRelease(table); #if defined(DEBUG) { CFAllocatorContext context = {0, NULL, NULL, NULL, csCopyDescription, csAlloc, csRealloc, csDealloc, NULL}; - constantStringAllocatorForDebugging = CFAllocatorCreate(NULL, &context); + constantStringAllocatorForDebugging = _CFAllocatorCreateGC(NULL, &context); } #else #define constantStringAllocatorForDebugging NULL @@ -1337,50 +1498,9 @@ CFStringRef __CFStringMakeConstantString(const char *cStr) { __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 + // Given this code path is rarer these days, OK to do this extra work to verify the strings const unsigned char *tmp = cStr; while (*tmp) { if (*tmp++ > 127) { @@ -1398,12 +1518,12 @@ CFStringRef __CFStringMakeConstantString(const char *cStr) { 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); } + // Treat non-7 bit chars in CFSTR() as MacOSRoman, for compatibility 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); @@ -1461,6 +1581,8 @@ void __CFStringCleanup (void) { static void __CFStringReplaceMultiple(CFMutableStringRef str, CFRange *ranges, CFIndex numRanges, CFStringRef replacement) { int cnt; + CFStringRef copy = NULL; + if (replacement == str) copy = replacement = CFStringCreateCopy(NULL, replacement); // Very special and hopefully rare case CFIndex replacementLength = CFStringGetLength(replacement); __CFStringChangeSizeMultiple(str, ranges, numRanges, replacementLength, (replacementLength > 0) && CFStrIsUnicode(replacement)); @@ -1487,11 +1609,14 @@ static void __CFStringReplaceMultiple(CFMutableStringRef str, CFRange *ranges, C memmove(contents + ranges[cnt].location, firstReplacement, replacementLength); } } + if (copy) CFRelease(copy); } // Can pass in NSString as replacement string -static void __CFStringReplace(CFMutableStringRef str, CFRange range, CFStringRef replacement) { +CF_INLINE void __CFStringReplace(CFMutableStringRef str, CFRange range, CFStringRef replacement) { + CFStringRef copy = NULL; + if (replacement == str) copy = replacement = CFStringCreateCopy(NULL, replacement); // Very special and hopefully rare case CFIndex replacementLength = CFStringGetLength(replacement); __CFStringChangeSize(str, range, replacementLength, (replacementLength > 0) && CFStrIsUnicode(replacement)); @@ -1503,13 +1628,15 @@ static void __CFStringReplace(CFMutableStringRef str, CFRange range, CFStringRef uint8_t *contents = (uint8_t *)__CFStrContents(str); CFStringGetBytes(replacement, CFRangeMake(0, replacementLength), __CFStringGetEightBitStringEncoding(), 0, false, contents + range.location + __CFStrSkipAnyLengthByte(str), replacementLength, NULL); } + + if (copy) CFRelease(copy); } /* If client does not provide a minimum capacity */ #define DEFAULTMINCAPACITY 32 -CF_INLINE CFMutableStringRef __CFStringCreateMutableFunnel(CFAllocatorRef alloc, CFIndex maxLength, UInt32 additionalInfoBits) { +CF_INLINE CFMutableStringRef __CFStringCreateMutableFunnel(CFAllocatorRef alloc, CFIndex maxLength, UInt32 additionalInfoBits) { CFMutableStringRef str; Boolean hasExternalContentsAllocator = (additionalInfoBits & __kCFHasContentsAllocator) ? true : false; @@ -1521,10 +1648,10 @@ CF_INLINE CFMutableStringRef __CFStringCreateMutableFunnel(CFAllocatorRef alloc if (__CFOASafe) __CFSetLastAllocationEventName(str, "CFString (mutable)"); __CFStrSetInfoBits(str, __kCFIsMutable | additionalInfoBits); - str->variants.externalMutable.buffer = NULL; + str->variants.notInlineMutable.buffer = NULL; __CFStrSetExplicitLength(str, 0); - str->variants.externalMutable.gapEtc = 0; - if (maxLength != 0) __CFStrSetIsFixed(str); + str->variants.notInlineMutable.gapEtc = 0; + if (maxLength != 0) __CFStrSetIsFixed(str); __CFStrSetDesiredCapacity(str, (maxLength == 0) ? DEFAULTMINCAPACITY : maxLength); __CFStrSetCapacity(str, 0); } @@ -1532,9 +1659,10 @@ CF_INLINE CFMutableStringRef __CFStringCreateMutableFunnel(CFAllocatorRef alloc } 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); + CFOptionFlags contentsAllocationBits = externalCharactersAllocator ? ((externalCharactersAllocator == kCFAllocatorNull) ? __kCFNotInlineContentsNoFree : __kCFHasContentsAllocator) : __kCFNotInlineContentsDefaultFree; + CFMutableStringRef string = __CFStringCreateMutableFunnel(alloc, 0, contentsAllocationBits | __kCFIsUnicode); if (string) { + __CFStrSetIsExternalMutable(string); if (contentsAllocationBits == __kCFHasContentsAllocator) __CFStrSetContentsAllocator(string, CFRetain(externalCharactersAllocator)); CFStringSetExternalCharactersNoCopy(string, chars, numChars, capacity); } @@ -1542,13 +1670,21 @@ CFMutableStringRef CFStringCreateMutableWithExternalCharactersNoCopy(CFAllocator } CFMutableStringRef CFStringCreateMutable(CFAllocatorRef alloc, CFIndex maxLength) { - return __CFStringCreateMutableFunnel(alloc, maxLength, __kCFHasExternalDataDefaultFree); + return __CFStringCreateMutableFunnel(alloc, maxLength, __kCFNotInlineContentsDefaultFree); } CFMutableStringRef CFStringCreateMutableCopy(CFAllocatorRef alloc, CFIndex maxLength, CFStringRef string) { CFMutableStringRef newString; - CF_OBJC_FUNCDISPATCH0(__kCFStringTypeID, CFMutableStringRef, string, "mutableCopy"); + if (CF_IS_OBJC(__kCFStringTypeID, string)) { + static SEL s = NULL; + CFMutableStringRef (*func)(void *, SEL, ...) = (void *)__CFSendObjCMsg; + if (!s) s = sel_registerName("mutableCopy"); + newString = func((void *)string, s); + if (CF_USING_COLLECTABLE_MEMORY) auto_zone_retain(__CFCollectableZone, newString); // needs hard retain IF using GC + return newString; + } + // CF_OBJC_FUNCDISPATCH0(__kCFStringTypeID, CFMutableStringRef, string, "mutableCopy"); __CFAssertIsString(string); @@ -1589,7 +1725,7 @@ CF_INLINE UniChar __CFStringGetCharacterAtIndexGuts(CFStringRef str, CFIndex idx #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"); + fprintf(stderr, "Warning: CFStringGetCharacterAtIndex() attempted on CFString containing high bytes before properly initialized to do so\n"); } #endif return __CFCharToUniCharTable[contents[idx]]; @@ -1797,7 +1933,218 @@ Boolean CFStringGetCString(CFStringRef str, char *buffer, CFIndex bufferSize, CF } } + +CF_INLINE bool _CFCanUseLocale(CFLocaleRef locale) { + return false; +} + +static const char *_CFStrGetLanguageIdentifierForLocale(CFLocaleRef locale) { + return NULL; +} + #define MAX_CASE_MAPPING_BUF (8) +#define ZERO_WIDTH_JOINER (0x200D) +#define COMBINING_GRAPHEME_JOINER (0x034F) +// Hangul ranges +#define HANGUL_CHOSEONG_START (0x1100) +#define HANGUL_CHOSEONG_END (0x115F) +#define HANGUL_JUNGSEONG_START (0x1160) +#define HANGUL_JUNGSEONG_END (0x11A2) +#define HANGUL_JONGSEONG_START (0x11A8) +#define HANGUL_JONGSEONG_END (0x11F9) + +#define HANGUL_SYLLABLE_START (0xAC00) +#define HANGUL_SYLLABLE_END (0xD7AF) + + +// Returns the length of characters filled into outCharacters. If no change, returns 0. maxBufLen shoule be at least 8 +static inline CFIndex __CFStringFoldCharacterClusterAtIndex(UTF32Char character, CFStringInlineBuffer *buffer, CFIndex index, CFOptionFlags flags, const uint8_t *langCode, UTF32Char *outCharacters, CFIndex maxBufferLength, CFIndex *consumedLength) { + CFIndex filledLength = 0, currentIndex = index; + + if (0 != character) { + UTF16Char lowSurrogate; + CFIndex planeNo = (character >> 16); + bool isTurkikCapitalI = false; + static const uint8_t *decompBMP = NULL; + static const uint8_t *nonBaseBMP = NULL; + + if (NULL == decompBMP) { + decompBMP = CFUniCharGetBitmapPtrForPlane(kCFUniCharCanonicalDecomposableCharacterSet, 0); + nonBaseBMP = CFUniCharGetBitmapPtrForPlane(kCFUniCharNonBaseCharacterSet, 0); + } + + ++currentIndex; + + if ((character < 0x0080) && ((NULL == langCode) || (character != 'I'))) { // ASCII + if ((flags & kCFCompareCaseInsensitive) && (character >= 'A') && (character <= 'Z')) { + character += ('a' - 'A'); + *outCharacters = character; + filledLength = 1; + } + } else { + // do width-insensitive mapping + if ((flags & kCFCompareWidthInsensitive) && (character >= 0xFF00) && (character <= 0xFFEF)) { + (void)CFUniCharCompatibilityDecompose(&character, 1, 1); + *outCharacters = character; + filledLength = 1; + } + + // map surrogates + if ((0 == planeNo) && CFUniCharIsSurrogateHighCharacter(character) && CFUniCharIsSurrogateLowCharacter((lowSurrogate = CFStringGetCharacterFromInlineBuffer(buffer, currentIndex)))) { + character = CFUniCharGetLongCharacterForSurrogatePair(character, lowSurrogate); + ++currentIndex; + planeNo = (character >> 16); + } + + // decompose + if (flags & (kCFCompareDiacriticsInsensitive|kCFCompareNonliteral)) { + if (CFUniCharIsMemberOfBitmap(character, ((0 == planeNo) ? decompBMP : CFUniCharGetBitmapPtrForPlane(kCFUniCharCanonicalDecomposableCharacterSet, planeNo)))) { + filledLength = CFUniCharDecomposeCharacter(character, outCharacters, maxBufferLength); + character = *outCharacters; + if ((flags & kCFCompareDiacriticsInsensitive) && (character < 0x0510)) filledLength = 1; // reset if Roman, Greek, Cyrillic + } + } + + // fold case + if (flags & kCFCompareCaseInsensitive) { + const uint8_t *nonBaseBitmap; + bool filterNonBase = (((flags & kCFCompareDiacriticsInsensitive) && (character < 0x0510)) ? true : false); + static const uint8_t *lowerBMP = NULL; + static const uint8_t *caseFoldBMP = NULL; + + if (NULL == lowerBMP) { + lowerBMP = CFUniCharGetBitmapPtrForPlane(kCFUniCharHasNonSelfLowercaseCharacterSet, 0); + caseFoldBMP = CFUniCharGetBitmapPtrForPlane(kCFUniCharHasNonSelfCaseFoldingCharacterSet, 0); + } + + if ((NULL != langCode) && ('I' == character) && ((0 == strcmp(langCode, "tr")) || (0 == strcmp(langCode, "az")))) { // do Turkik special-casing + if (filledLength > 1) { + if (0x0307 == outCharacters[1]) { + memmove(&(outCharacters[index]), &(outCharacters[index + 1]), sizeof(UTF32Char) * (--filledLength)); + character = *outCharacters = 'i'; + isTurkikCapitalI = true; + } + } else if (0x0307 == CFStringGetCharacterFromInlineBuffer(buffer, currentIndex)) { + character = *outCharacters = 'i'; + filledLength = 1; + ++currentIndex; + isTurkikCapitalI = true; + } + } + if (!isTurkikCapitalI && (CFUniCharIsMemberOfBitmap(character, ((0 == planeNo) ? lowerBMP : CFUniCharGetBitmapPtrForPlane(kCFUniCharHasNonSelfLowercaseCharacterSet, planeNo))) || CFUniCharIsMemberOfBitmap(character, ((0 == planeNo) ? caseFoldBMP : CFUniCharGetBitmapPtrForPlane(kCFUniCharHasNonSelfCaseFoldingCharacterSet, planeNo))))) { + UTF16Char caseFoldBuffer[MAX_CASE_MAPPING_BUF]; + const UTF16Char *bufferP = caseFoldBuffer, *bufferLimit; + UTF32Char *outCharactersP = outCharacters; + uint32_t bufferLength = CFUniCharMapCaseTo(character, caseFoldBuffer, MAX_CASE_MAPPING_BUF, kCFUniCharCaseFold, 0, langCode); + + bufferLimit = bufferP + bufferLength; + + if (filledLength > 0) --filledLength; // decrement filledLength (will add back later) + + // make space for casefold characters + if ((filledLength > 0) && (bufferLength > 1)) { + CFIndex totalScalerLength = 0; + + while (bufferP < bufferLimit) { + if (CFUniCharIsSurrogateHighCharacter(*(bufferP++)) && (bufferP < bufferLimit) && CFUniCharIsSurrogateLowCharacter(*bufferP)) ++bufferP; + ++totalScalerLength; + } + memmove(outCharacters + totalScalerLength, outCharacters + 1, filledLength * sizeof(UTF32Char)); + bufferP = caseFoldBuffer; + } + + // fill + while (bufferP < bufferLimit) { + character = *(bufferP++); + if (CFUniCharIsSurrogateHighCharacter(character) && (bufferP < bufferLimit) && CFUniCharIsSurrogateLowCharacter(*bufferP)) { + character = CFUniCharGetLongCharacterForSurrogatePair(character, *(bufferP++)); + nonBaseBitmap = CFUniCharGetBitmapPtrForPlane(kCFUniCharNonBaseCharacterSet, (character >> 16)); + } else { + nonBaseBitmap = nonBaseBMP; + } + + if (!filterNonBase || !CFUniCharIsMemberOfBitmap(character, nonBaseBitmap)) { + *(outCharactersP++) = character; + ++filledLength; + } + } + } + } + } + + // collect following combining marks + if (flags & (kCFCompareDiacriticsInsensitive|kCFCompareNonliteral)) { + const uint8_t *nonBaseBitmap; + const uint8_t *decompBitmap; + bool doFill = (((flags & kCFCompareDiacriticsInsensitive) && (character < 0x0510)) ? false : true); + + if (doFill && (0 == filledLength)) { // check if really needs to fill + UTF32Char nonBaseCharacter = CFStringGetCharacterFromInlineBuffer(buffer, currentIndex); + + if (CFUniCharIsSurrogateHighCharacter(nonBaseCharacter) && CFUniCharIsSurrogateLowCharacter((lowSurrogate = CFStringGetCharacterFromInlineBuffer(buffer, currentIndex + 1)))) { + nonBaseCharacter = CFUniCharGetLongCharacterForSurrogatePair(nonBaseCharacter, lowSurrogate); + nonBaseBitmap = CFUniCharGetBitmapPtrForPlane(kCFUniCharNonBaseCharacterSet, (nonBaseCharacter >> 16)); + decompBitmap = CFUniCharGetBitmapPtrForPlane(kCFUniCharCanonicalDecomposableCharacterSet, (nonBaseCharacter >> 16)); + } else { + nonBaseBitmap = nonBaseBMP; + decompBitmap = decompBMP; + } + + if (CFUniCharIsMemberOfBitmap(nonBaseCharacter, nonBaseBitmap)) { + outCharacters[filledLength++] = character; + + if ((0 == (flags & kCFCompareDiacriticsInsensitive)) || (nonBaseCharacter > 0x050F)) { + if (CFUniCharIsMemberOfBitmap(nonBaseCharacter, decompBitmap)) { + filledLength += CFUniCharDecomposeCharacter(nonBaseCharacter, &(outCharacters[filledLength]), maxBufferLength - filledLength); + } else { + outCharacters[filledLength++] = nonBaseCharacter; + } + } + currentIndex += ((nonBaseBitmap == nonBaseBMP) ? 1 : 2); + } else { + doFill = false; + } + } + + while (filledLength < maxBufferLength) { // do the rest + character = CFStringGetCharacterFromInlineBuffer(buffer, currentIndex); + + if (CFUniCharIsSurrogateHighCharacter(character) && CFUniCharIsSurrogateLowCharacter((lowSurrogate = CFStringGetCharacterFromInlineBuffer(buffer, currentIndex + 1)))) { + character = CFUniCharGetLongCharacterForSurrogatePair(character, lowSurrogate); + nonBaseBitmap = CFUniCharGetBitmapPtrForPlane(kCFUniCharNonBaseCharacterSet, (character >> 16)); + decompBitmap = CFUniCharGetBitmapPtrForPlane(kCFUniCharCanonicalDecomposableCharacterSet, (character >> 16)); + } else { + nonBaseBitmap = nonBaseBMP; + decompBitmap = decompBMP; + } + if (isTurkikCapitalI) { + isTurkikCapitalI = false; + } else if (CFUniCharIsMemberOfBitmap(character, nonBaseBitmap)) { + if (doFill && ((0 == (flags & kCFCompareDiacriticsInsensitive)) || (character > 0x050F))) { + if (CFUniCharIsMemberOfBitmap(character, decompBitmap)) { + CFIndex currentLength = CFUniCharDecomposeCharacter(character, &(outCharacters[filledLength]), maxBufferLength - filledLength); + + if (0 == currentLength) break; // didn't fit + + filledLength += currentLength; + } else { + outCharacters[filledLength++] = character; + } + } + currentIndex += ((nonBaseBitmap == nonBaseBMP) ? 1 : 2); + } else { + break; + } + } + + if (filledLength > 1) CFUniCharPrioritySort(outCharacters, filledLength); // priority sort + } + } + + if ((filledLength > 0) && (NULL != consumedLength)) *consumedLength = (currentIndex - index); + + return filledLength; +} /* Special casing for Uk sorting */ #define DO_IGNORE_PUNCTUATION 1 @@ -1852,8 +2199,8 @@ CFComparisonResult CFStringCompareWithOptions(CFStringRef string, CFStringRef st 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; + uint64_t n1 = 0; // !!! Doesn't work if numbers are > max uint64_t + uint64_t n2 = 0; do { n1 = n1 * 10 + (ch1 - '0'); strBuf1_idx++; @@ -1941,7 +2288,10 @@ CFComparisonResult CFStringCompareWithOptions(CFStringRef string, CFStringRef st if (isCh1Decomposable) { decomposedCharacterLength = CFUniCharDecomposeCharacter(ch1, decomposedCharater, MAX_DECOMPOSED_LENGTH); - for (idx = 0; idx < decomposedCharacterLength && strBuf2_idx < string2_len; idx++) { + if ((string2_len - strBuf2_idx) < decomposedCharacterLength) { // the remaining other length is shorter + if (ch1 < ch2) return kCFCompareLessThan; else if (ch1 > ch2) return kCFCompareGreaterThan; + } + for (idx = 0; idx < decomposedCharacterLength; 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); @@ -1957,6 +2307,9 @@ CFComparisonResult CFStringCompareWithOptions(CFStringRef string, CFStringRef st strBuf1_idx++; continue; } else { // ch2 is decomposable, then decomposedCharacterLength = CFUniCharDecomposeCharacter(ch2, decomposedCharater, MAX_DECOMPOSED_LENGTH); + if ((rangeToCompare.length - strBuf1_idx) < decomposedCharacterLength) { // the remaining other length is shorter + if (ch1 < ch2) return kCFCompareLessThan; else if (ch1 > ch2) return kCFCompareGreaterThan; + } for (idx = 0; idx < decomposedCharacterLength && strBuf1_idx < rangeToCompare.length; idx++) { ch2 = decomposedCharater[idx]; if (ch1 < ch2) return kCFCompareLessThan; else if (ch1 > ch2) return kCFCompareGreaterThan; @@ -1993,219 +2346,325 @@ CFComparisonResult CFStringCompare(CFStringRef string, CFStringRef str2, CFOptio return CFStringCompareWithOptions(string, str2, CFRangeMake(0, CFStringGetLength(string)), options); } -/* ??? Need to implement localized find -*/ +#define kCFStringStackBufferLength (64) + 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; + CFIndex findStrLen = CFStringGetLength(stringToFind); + Boolean didFind = false; + bool lengthVariants = ((compareOptions & (kCFCompareCaseInsensitive|kCFCompareNonliteral|kCFCompareDiacriticsInsensitive)) ? true : false); + + if ((findStrLen > 0) && (rangeToSearch.length > 0) && ((findStrLen <= rangeToSearch.length) || lengthVariants)) { + UTF32Char strBuf1[kCFStringStackBufferLength]; + UTF32Char strBuf2[kCFStringStackBufferLength]; + CFStringInlineBuffer inlineBuf1, inlineBuf2; + UTF32Char str1Char, str2Char; + CFStringEncoding eightBitEncoding = __CFStringGetEightBitStringEncoding(); + const uint8_t *str1Bytes = CFStringGetCStringPtr(string, eightBitEncoding); + const uint8_t *str2Bytes = CFStringGetCStringPtr(stringToFind, eightBitEncoding); + const UTF32Char *characters, *charactersLimit; + const uint8_t *langCode = NULL; + CFIndex fromLoc, toLoc; + CFIndex str1Index, str2Index; + CFIndex strBuf1Len, strBuf2Len; + bool equalityOptions = ((lengthVariants || (compareOptions & kCFCompareWidthInsensitive)) ? true : false); + bool caseInsensitive = ((compareOptions & kCFCompareCaseInsensitive) ? true : false); + int8_t delta; + + + CFStringInitInlineBuffer(string, &inlineBuf1, CFRangeMake(0, rangeToSearch.location + rangeToSearch.length)); + CFStringInitInlineBuffer(stringToFind, &inlineBuf2, CFRangeMake(0, findStrLen)); + + if (compareOptions & kCFCompareBackwards) { + fromLoc = rangeToSearch.location + rangeToSearch.length - (lengthVariants ? 1 : findStrLen); + toLoc = (((compareOptions & kCFCompareAnchored) && !lengthVariants) ? fromLoc : rangeToSearch.location); + } else { + fromLoc = rangeToSearch.location; + toLoc = ((compareOptions & kCFCompareAnchored) ? fromLoc : rangeToSearch.location + rangeToSearch.length - (lengthVariants ? 1 : findStrLen)); + } + + delta = ((fromLoc <= toLoc) ? 1 : -1); - if (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 + if ((NULL != str1Bytes) && (NULL != str2Bytes)) { + CFIndex maxStr1Index = (rangeToSearch.location + rangeToSearch.length); + uint8_t str1Byte, str2Byte; - 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); + while (1) { + str1Index = fromLoc; + str2Index = 0; + + while ((str1Index < maxStr1Index) && (str2Index < findStrLen)) { + str1Byte = str1Bytes[str1Index]; + str2Byte = str2Bytes[str2Index]; + + if (str1Byte != str2Byte) { + if (equalityOptions) { + if ((str1Byte < 0x80) && ((NULL == langCode) || ('I' != str1Byte))) { + if (caseInsensitive && (str1Byte >= 'A') && (str1Byte <= 'Z')) str1Byte += ('a' - 'A'); + *strBuf1 = str1Byte; + strBuf1Len = 1; + } else { + str1Char = CFStringGetCharacterFromInlineBuffer(&inlineBuf1, str1Index); + strBuf1Len = __CFStringFoldCharacterClusterAtIndex(str1Char, &inlineBuf1, str1Index, compareOptions, langCode, strBuf1, kCFStringStackBufferLength, NULL); + if (1 > strBuf1Len) { + *strBuf1 = str1Char; + strBuf1Len = 1; + } + } + if ((str2Byte < 0x80) && ((NULL == langCode) || ('I' != str2Byte))) { + if (caseInsensitive && (str2Byte >= 'A') && (str2Byte <= 'Z')) str2Byte += ('a' - 'A'); + *strBuf2 = str2Byte; + strBuf2Len = 1; + } else { + str2Char = CFStringGetCharacterFromInlineBuffer(&inlineBuf2, str2Index); + strBuf2Len = __CFStringFoldCharacterClusterAtIndex(str2Char, &inlineBuf2, str2Index, compareOptions, langCode, strBuf2, kCFStringStackBufferLength, NULL); + if (1 > strBuf2Len) { + *strBuf2 = str2Char; + strBuf2Len = 1; + } + } - if (decompose) { - SInt32 max = __CFMin(MAXISDECOMPBUFFERLEN, findStrLen); + if ((1 == strBuf1Len) && (1 == strBuf2Len)) { // normal case + if (*strBuf1 != *strBuf2) break; + } else { + CFIndex delta; - 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 && (strBuf1Len != strBuf2Len)) break; + if (memcmp(strBuf1, strBuf2, sizeof(UTF32Char) * __CFMin(strBuf1Len, strBuf2Len))) break; - 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 (strBuf1Len < strBuf2Len) { + delta = strBuf2Len - strBuf1Len; + + if ((str1Index + strBuf1Len + delta) > (rangeToSearch.location + rangeToSearch.length)) break; - if (CFUniCharIsSurrogateHighCharacter(ch1) && (cnt + 1 < findStrLen) && CFUniCharIsSurrogateLowCharacter(findBuf[cnt + 1])) { - ch1 = CFUniCharGetLongCharacterForSurrogatePair(ch1, findBuf[cnt + 1]); + characters = &(strBuf2[strBuf1Len]); + charactersLimit = characters + delta; + + while (characters < charactersLimit) { + strBuf1Len = __CFStringFoldCharacterClusterAtIndex(CFStringGetCharacterFromInlineBuffer(&inlineBuf1, str1Index + 1), &inlineBuf1, str1Index + 1, compareOptions, langCode, strBuf1, kCFStringStackBufferLength, NULL); + if ((strBuf1Len > 0) || (*characters != *strBuf1)) break; + ++characters; ++str1Index; + } + if (characters < charactersLimit) break; + } else if (strBuf2Len < strBuf1Len) { + delta = strBuf1Len - strBuf2Len; + + if ((str2Index + strBuf2Len + delta) > findStrLen) break; + + characters = &(strBuf1[strBuf2Len]); + charactersLimit = characters + delta; + + while (characters < charactersLimit) { + strBuf2Len = __CFStringFoldCharacterClusterAtIndex(CFStringGetCharacterFromInlineBuffer(&inlineBuf2, str1Index + 1), &inlineBuf2, str2Index + 1, compareOptions, langCode, strBuf2, kCFStringStackBufferLength, NULL); + if ((strBuf2Len > 0) || (*characters != *strBuf2)) break; + ++characters; ++str2Index; + } + if (characters < charactersLimit) break; + } + } + } else { + break; + } + } + ++str1Index; ++str2Index; } - if (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 (str2Index == findStrLen) { + if (((kCFCompareBackwards|kCFCompareAnchored) != (compareOptions & (kCFCompareBackwards|kCFCompareAnchored))) || (str1Index == (rangeToSearch.location + rangeToSearch.length))) { + didFind = true; + if (NULL != result) *result = CFRangeMake(fromLoc, str1Index - fromLoc); + } + break; } - } - } - } - if (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)); - } + if (fromLoc == toLoc) break; + fromLoc += delta; + } + } else if (equalityOptions) { + UTF16Char otherChar; + CFIndex str1UsedLen, str2UsedLen, strBuf1Index = 0, strBuf2Index = 0; + bool diacriticsInsensitive = ((compareOptions & kCFCompareDiacriticsInsensitive) ? true : false); + static const uint8_t *nonBaseBMP = NULL; + static const uint8_t *combClassBMP = NULL; + + if (NULL == nonBaseBMP) { + nonBaseBMP = CFUniCharGetBitmapPtrForPlane(kCFUniCharNonBaseCharacterSet, 0); + combClassBMP = CFUniCharGetUnicodePropertyDataForPlane(kCFUniCharCombiningProperty, 0); + } - 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; + while (1) { + str1Index = fromLoc; + str2Index = 0; - 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; + strBuf1Len = strBuf2Len = 0; - 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)); + while (str2Index < findStrLen) { + if (strBuf1Len == 0) { + str1Char = CFStringGetCharacterFromInlineBuffer(&inlineBuf1, str1Index); + if (caseInsensitive && (str1Char >= 'A') && (str1Char <= 'Z') && ((NULL == langCode) || (str1Char != 'I'))) str1Char += ('a' - 'A'); + str1UsedLen = 1; } else { - buf_idx--; + str1Char = strBuf1[strBuf1Index++]; + } + if (strBuf2Len == 0) { + str2Char = CFStringGetCharacterFromInlineBuffer(&inlineBuf2, str2Index); + if (caseInsensitive && (str2Char >= 'A') && (str2Char <= 'Z') && ((NULL == langCode) || (str2Char != 'I'))) str2Char += ('a' - 'A'); + str2UsedLen = 1; + } else { + str2Char = strBuf2[strBuf2Index++]; } - } - if (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 (str1Char != str2Char) { + if ((str1Char < 0x80) && (str2Char < 0x80) && ((NULL == langCode) || !caseInsensitive)) break; - 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 (CFUniCharIsSurrogateHighCharacter(str1Char) && CFUniCharIsSurrogateLowCharacter((otherChar = CFStringGetCharacterFromInlineBuffer(&inlineBuf1, str1Index + 1)))) { + str1Char = CFUniCharGetLongCharacterForSurrogatePair(str1Char, otherChar); + str1UsedLen = 2; + } + + if (CFUniCharIsSurrogateHighCharacter(str2Char) && CFUniCharIsSurrogateLowCharacter((otherChar = CFStringGetCharacterFromInlineBuffer(&inlineBuf2, str2Index + 1)))) { + str2Char = CFUniCharGetLongCharacterForSurrogatePair(str2Char, otherChar); + str2UsedLen = 2; + } + + if (diacriticsInsensitive && (str1Index > fromLoc)) { + if ((0 == strBuf1Len) && CFUniCharIsMemberOfBitmap(str1Char, ((str1Char < 0x10000) ? nonBaseBMP : CFUniCharGetBitmapPtrForPlane(kCFUniCharNonBaseCharacterSet, (str1Char >> 16))))) str1Char = str2Char; + if ((0 == strBuf2Len) && CFUniCharIsMemberOfBitmap(str2Char, ((str2Char < 0x10000) ? nonBaseBMP : CFUniCharGetBitmapPtrForPlane(kCFUniCharNonBaseCharacterSet, (str2Char >> 16))))) str2Char = str1Char; + } + + if (str1Char != str2Char) { + if (0 == strBuf1Len) { + strBuf1Len = __CFStringFoldCharacterClusterAtIndex(str1Char, &inlineBuf1, str1Index, compareOptions, langCode, strBuf1, kCFStringStackBufferLength, &str1UsedLen); + if (strBuf1Len > 0) { + str1Char = *strBuf1; + strBuf1Index = 1; + } + } + + if ((0 == strBuf1Len) && (0 < strBuf2Len)) break; + + if ((0 == strBuf2Len) && ((0 == strBuf1Len) || (str1Char != str2Char))) { + strBuf2Len = __CFStringFoldCharacterClusterAtIndex(str2Char, &inlineBuf2, str2Index, compareOptions, langCode, strBuf2, kCFStringStackBufferLength, &str2UsedLen); + if ((0 == strBuf2Len) || (str1Char != *strBuf2)) break; + strBuf2Index = 1; + } + } + + if ((strBuf1Len > 0) && (strBuf2Len > 0)) { + while ((strBuf1Index < strBuf1Len) && (strBuf2Index < strBuf2Len)) { + if (strBuf1[strBuf1Index] != strBuf2[strBuf2Index]) break; + ++strBuf1Index; ++strBuf2Index; + } + if ((strBuf1Index < strBuf1Len) && (strBuf2Index < strBuf2Len)) break; + } } + + if ((strBuf1Len > 0) && (strBuf1Index == strBuf1Len)) strBuf1Len = 0; + if ((strBuf2Len > 0) && (strBuf2Index == strBuf2Len)) strBuf2Len = 0; + + if (strBuf1Len == 0) str1Index += str1UsedLen; + if (strBuf2Len == 0) str2Index += str2UsedLen; } - } - if (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 (str2Index == findStrLen) { + bool match = true; + + if (strBuf1Len > 0) { + match = false; + + if ((compareOptions & kCFCompareDiacriticsInsensitive) && (strBuf1[0] < 0x0510)) { + while (strBuf1Index < strBuf1Len) { + if (!CFUniCharIsMemberOfBitmap(strBuf1[strBuf1Index], ((strBuf1[strBuf1Index] < 0x10000) ? nonBaseBMP : CFUniCharGetBitmapPtrForPlane(kCFUniCharCanonicalDecomposableCharacterSet, (strBuf1[strBuf1Index] >> 16))))) break; + ++strBuf1Index; + } + + if (strBuf1Index == strBuf1Len) { + str1Index += str1UsedLen; + match = true; + } + } } - } - if (CFUniCharIsSurrogateHighCharacter(findBuf[chCnt]) && (chCnt + 1 < findStrLen) && CFUniCharIsSurrogateLowCharacter(findBuf[chCnt + 1])) { - ch1 = CFUniCharGetLongCharacterForSurrogatePair(findBuf[chCnt], findBuf[chCnt + 1]); - ++chCnt; - } else { - ch1 = findBuf[chCnt]; - } + if (match && (compareOptions & (kCFCompareDiacriticsInsensitive|kCFCompareNonliteral)) && (str1Index < (rangeToSearch.location + rangeToSearch.length))) { + const uint8_t *nonBaseBitmap; - 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)); + str1Char = CFStringGetCharacterFromInlineBuffer(&inlineBuf1, str1Index); + + if (CFUniCharIsSurrogateHighCharacter(str1Char) && CFUniCharIsSurrogateLowCharacter((otherChar = CFStringGetCharacterFromInlineBuffer(&inlineBuf1, str1Index + 1)))) { + str1Char = CFUniCharGetLongCharacterForSurrogatePair(str1Char, otherChar); + nonBaseBitmap = CFUniCharGetBitmapPtrForPlane(kCFUniCharNonBaseCharacterSet, (str1Char >> 16)); + } else { + nonBaseBitmap = nonBaseBMP; + } - if (isCh1Decomposable != isCh2Decomposable) { - UTF32Char decomposedCharater[MAX_DECOMPOSED_LENGTH]; - UInt32 decomposedCharacterLength; - UInt32 idx; + if (CFUniCharIsMemberOfBitmap(str1Char, nonBaseBitmap)) { + if (diacriticsInsensitive) { + if (str1Char < 0x10000) { + CFIndex index = str1Index; - 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; + do { + str1Char = CFStringGetCharacterFromInlineBuffer(&inlineBuf1, --index); + } while (CFUniCharIsMemberOfBitmap(str1Char, nonBaseBMP), (rangeToSearch.location < index)); - 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 (str1Char < 0x0510) { + CFIndex maxIndex = (rangeToSearch.location + rangeToSearch.length); + + while (++str1Index < maxIndex) if (!CFUniCharIsMemberOfBitmap(CFStringGetCharacterFromInlineBuffer(&inlineBuf1, str1Index), nonBaseBMP)) break; } } + } else { + match = false; } - 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; + } else if (!diacriticsInsensitive) { + otherChar = CFStringGetCharacterFromInlineBuffer(&inlineBuf1, str1Index - 1); + + // this is assuming viramas are only in BMP ??? + if ((str1Char == COMBINING_GRAPHEME_JOINER) || (otherChar == COMBINING_GRAPHEME_JOINER) || (otherChar == ZERO_WIDTH_JOINER) || ((otherChar >= HANGUL_CHOSEONG_START) && (otherChar <= HANGUL_JONGSEONG_END)) || (CFUniCharGetCombiningPropertyForCharacter(otherChar, combClassBMP) == 9)) { + CFRange clusterRange = CFStringGetRangeOfCharacterClusterAtIndex(string, str1Index, kCFStringGramphemeCluster); + + if (str1Index < (clusterRange.location + clusterRange.length)) match = false; } - 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 { + + if (match) { + if (((kCFCompareBackwards|kCFCompareAnchored) != (compareOptions & (kCFCompareBackwards|kCFCompareAnchored))) || (str1Index == (rangeToSearch.location + rangeToSearch.length))) { + didFind = true; + if (NULL != result) *result = CFRangeMake(fromLoc, str1Index - fromLoc); + } break; } - } else { - if (findBuf[chCnt] != ch2) break; } + + if (fromLoc == toLoc) break; + fromLoc += delta; } - buf_idx++; - } - if (chCnt == findStrLen) { - if (decompose && (buf_idx < buf_idx_end)) { - if ((compareOptions & kCFCompareAnchored) && (compareOptions & kCFCompareBackwards)) break; - - ch2 = CFStringGetCharacterFromInlineBuffer(&buf, buf_idx); + } else { + while (1) { + str1Index = fromLoc; + str2Index = 0; + + while (str2Index < findStrLen) { + if (CFStringGetCharacterFromInlineBuffer(&inlineBuf1, str1Index) != CFStringGetCharacterFromInlineBuffer(&inlineBuf2, str2Index)) break; - if (CFUniCharIsSurrogateHighCharacter(ch2)) { - if ((buf_idx + 1) < buf_idx_end && CFUniCharIsSurrogateLowCharacter(CFStringGetCharacterFromInlineBuffer(&buf, buf_idx + 1))) { - ch2 = CFUniCharGetLongCharacterForSurrogatePair(ch2, CFStringGetCharacterFromInlineBuffer(&buf, buf_idx + 1)); - } + ++str1Index; ++str2Index; } - 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); + + if (str2Index == findStrLen) { + didFind = true; + if (NULL != result) *result = CFRangeMake(fromLoc, findStrLen); + break; + } + + if (fromLoc == toLoc) break; + fromLoc += delta; } - } else if (cnt == toLoc) { - break; - } else { - cnt += step; - buf_idx = cnt; } - } while (!done); - - if (findBuf != tmpBuf) CFAllocatorDeallocate(tmpAlloc, findBuf); + } - return done; + return didFind; } - // Functions to deal with special arrays of CFRange, CFDataRef, created by CFStringCreateArrayWithFindResults() static const void *__rangeRetain(CFAllocatorRef allocator, const void *ptr) { @@ -2301,21 +2760,8 @@ 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) { @@ -2591,27 +3037,27 @@ CFRange CFStringGetRangeOfCharacterClusterAtIndex(CFStringRef string, CFIndex ch } // 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)) { + CFIndex otherIndex; + + currentIndex = (range.location + range.length) - (MAX_TRANSCODING_LENGTH + 1); + if (currentIndex < 0) currentIndex = 0; + + while (currentIndex <= range.location) { + character = CFStringGetCharacterFromInlineBuffer(&stringBuffer, currentIndex); + + if ((character & 0x1FFFF0) == 0xF860) { // transcoding hint + otherIndex = currentIndex + __CFTranscodingHintLength[(character - 0xF860)] + 1; + if (otherIndex >= (range.location + range.length)) { + if (otherIndex <= length) { range.location = currentIndex; range.length = otherIndex - currentIndex; - break; } + break; } - ++currentIndex; } + ++currentIndex; } - + return range; } @@ -3397,8 +3843,8 @@ CFIndex CFStringFindAndReplace(CFMutableStringRef string, CFStringRef stringToFi 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; + // We use unsigneds as that is what NSRanges do; we use uint64_t do make sure the sum doesn't wrap (otherwise we'd need to do 3 separate checks). This allows catching bad ranges as described in 3375535. (-1,1) + if (((uint64_t)((unsigned)range.location)) + ((uint64_t)((unsigned)range.length)) > (uint64_t)__CFStrLength(str) && __CFStringNoteErrors()) return _CFStringErrBounds; __CFAssertIsStringAndMutable(str); __CFAssertRangeIsInStringBounds(str, range.location, range.length); __CFStringReplace(str, range, replacement); @@ -3547,7 +3993,7 @@ void CFStringLowercase(CFMutableStringRef string, CFLocaleRef locale) { length = __CFStrLength(string); - langCode = NULL; + langCode = (_CFCanUseLocale(locale) ? _CFStrGetLanguageIdentifierForLocale(locale) : NULL); if (!langCode && isEightBit) { uint8_t *contents = (uint8_t*)__CFStrContents(string) + __CFStrSkipAnyLengthByte(string); @@ -3638,7 +4084,7 @@ void CFStringUppercase(CFMutableStringRef string, CFLocaleRef locale) { length = __CFStrLength(string); - langCode = NULL; + langCode = (_CFCanUseLocale(locale) ? _CFStrGetLanguageIdentifierForLocale(locale) : NULL); if (!langCode && isEightBit) { uint8_t *contents = (uint8_t*)__CFStrContents(string) + __CFStrSkipAnyLengthByte(string); @@ -3734,7 +4180,7 @@ void CFStringCapitalize(CFMutableStringRef string, CFLocaleRef locale) { if (NULL == caseIgnorableForBMP) caseIgnorableForBMP = CFUniCharGetBitmapPtrForPlane(kCFUniCharCaseIgnorableCharacterSet, 0); - langCode = NULL; + langCode = (_CFCanUseLocale(locale) ? _CFStrGetLanguageIdentifierForLocale(locale) : NULL); if (!langCode && isEightBit) { uint8_t *contents = (uint8_t*)__CFStrContents(string) + __CFStrSkipAnyLengthByte(string); @@ -3822,6 +4268,7 @@ void CFStringCapitalize(CFMutableStringRef string, CFLocaleRef locale) { } } + #define MAX_DECOMP_BUF 64 #define HANGUL_SBASE 0xAC00 @@ -4138,24 +4585,6 @@ void CFStringNormalize(CFMutableStringRef string, CFStringNormalizationForm theF } } -#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 @@ -4182,7 +4611,7 @@ typedef struct { int16_t type; int16_t size; union { - int64_t longlongValue; + int64_t int64Value; double doubleValue; void *pointerValue; } value; @@ -4326,17 +4755,8 @@ reswtch:switch (ch) { } } -#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, ...) { +#if defined(__WIN32__) +static int snprintf(char *b, size_t n, const char * f, ...) { int retval; va_list args; va_start (args, f); @@ -4357,15 +4777,15 @@ void CFStringAppendFormatAndArguments(CFMutableStringRef outputString, CFDiction TYPE value = (TYPE) WHAT; \ if (-1 != specs[curSpec].widthArgNum) { \ if (-1 != specs[curSpec].precArgNum) { \ - snprintf(buffer, 255, formatBuffer, width, precision, value); \ + snprintf_l(buffer, 255, NULL, formatBuffer, width, precision, value); \ } else { \ - snprintf(buffer, 255, formatBuffer, width, value); \ + snprintf_l(buffer, 255, NULL, formatBuffer, width, value); \ } \ } else { \ if (-1 != specs[curSpec].precArgNum) { \ - snprintf(buffer, 255, formatBuffer, precision, value); \ + snprintf_l(buffer, 255, NULL, formatBuffer, precision, value); \ } else { \ - snprintf(buffer, 255, formatBuffer, value); \ + snprintf_l(buffer, 255, NULL, formatBuffer, value); \ } \ }} @@ -4451,7 +4871,7 @@ void _CFStringAppendFormatAndArgumentsAux(CFMutableStringRef outputString, CFStr } 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); +// fprintf(stderr, "specs[%d] = {\n size = %d,\n type = %d,\n loc = %d,\n len = %d,\n mainArgNum = %d,\n precArgNum = %d,\n widthArgNum = %d\n}\n", curSpec, specs[curSpec].size, specs[curSpec].type, specs[curSpec].loc, specs[curSpec].len, specs[curSpec].mainArgNum, specs[curSpec].precArgNum, specs[curSpec].widthArgNum); } numSpecs = curSpec; @@ -4514,15 +4934,15 @@ void _CFStringAppendFormatAndArgumentsAux(CFMutableStringRef outputString, CFStr case CFFormatLongType: case CFFormatSingleUnicharType: if (CFFormatSize1 == values[argNum].size) { - values[argNum].value.longlongValue = (int64_t)(char)va_arg(args, int); + values[argNum].value.int64Value = (int64_t)(int8_t)va_arg(args, int); } else if (CFFormatSize2 == values[argNum].size) { - values[argNum].value.longlongValue = (int64_t)(short)va_arg(args, int); + values[argNum].value.int64Value = (int64_t)(int16_t)va_arg(args, int); } else if (CFFormatSize4 == values[argNum].size) { - values[argNum].value.longlongValue = (int64_t)va_arg(args, long); + values[argNum].value.int64Value = (int64_t)va_arg(args, int32_t); } else if (CFFormatSize8 == values[argNum].size) { - values[argNum].value.longlongValue = (int64_t)va_arg(args, int64_t); + values[argNum].value.int64Value = (int64_t)va_arg(args, int64_t); } else { - values[argNum].value.longlongValue = (int64_t)va_arg(args, int); + values[argNum].value.int64Value = (int64_t)va_arg(args, int); } break; case CFFormatDoubleType: @@ -4548,11 +4968,11 @@ void _CFStringAppendFormatAndArgumentsAux(CFMutableStringRef outputString, CFStr // 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; + width = (SInt32)values[specs[curSpec].widthArgNum].value.int64Value; hasWidth = true; } if (-1 != specs[curSpec].precArgNum) { - precision = (SInt32)values[specs[curSpec].precArgNum].value.longlongValue; + precision = (SInt32)values[specs[curSpec].precArgNum].value.int64Value; hasPrecision = true; } if (-1 != specs[curSpec].widthArg) { @@ -4569,7 +4989,17 @@ void _CFStringAppendFormatAndArgumentsAux(CFMutableStringRef outputString, CFStr case CFFormatDoubleType: case CFFormatPointerType: { int8_t formatBuffer[128]; +#if defined(__GNUC__) int8_t buffer[256 + width + precision]; +#else + int8_t stackBuffer[512]; + int8_t *dynamicBuffer = NULL; + int8_t *buffer = stackBuffer; + if (256+width+precision > 512) { + dynamicBuffer = CFAllocatorAllocate(NULL, 256+width+precision, 0); + buffer = dynamicBuffer; + } +#endif SInt32 cidx, idx, loc; Boolean appended = false; loc = specs[curSpec].loc; @@ -4598,9 +5028,9 @@ void _CFStringAppendFormatAndArgumentsAux(CFMutableStringRef outputString, CFStr switch (specs[curSpec].type) { case CFFormatLongType: if (CFFormatSize8 == specs[curSpec].size) { - SNPRINTF(int64_t, values[specs[curSpec].mainArgNum].value.longlongValue) + SNPRINTF(int64_t, values[specs[curSpec].mainArgNum].value.int64Value) } else { - SNPRINTF(SInt32, values[specs[curSpec].mainArgNum].value.longlongValue) + SNPRINTF(SInt32, values[specs[curSpec].mainArgNum].value.int64Value) } break; case CFFormatPointerType: @@ -4628,7 +5058,12 @@ void _CFStringAppendFormatAndArgumentsAux(CFMutableStringRef outputString, CFStr } if (!appended) CFStringAppendCString(outputString, buffer, __CFStringGetEightBitStringEncoding()); } - break; +#if !defined(__GNUC__) + if (dynamicBuffer) { + CFAllocatorDeallocate(NULL, dynamicBuffer); + } +#endif + break; case CFFormatLiteralType: if (cformat) { __CFStringAppendBytes(outputString, cformat+specs[curSpec].loc, specs[curSpec].len, __CFStringGetEightBitStringEncoding()); @@ -4678,7 +5113,7 @@ void _CFStringAppendFormatAndArgumentsAux(CFMutableStringRef outputString, CFStr } break; case CFFormatSingleUnicharType: - ch = values[specs[curSpec].mainArgNum].value.longlongValue; + ch = values[specs[curSpec].mainArgNum].value.int64Value; CFStringAppendCharacters(outputString, &ch, 1); break; case CFFormatUnicharsType: @@ -4745,39 +5180,40 @@ void CFShowStr(CFStringRef str) { CFAllocatorRef alloc; if (!str) { - printf ("(null)\n"); + fprintf(stdout, "(null)\n"); return; } if (CF_IS_OBJC(__kCFStringTypeID, str)) { - printf ("This is an NSString, not CFString\n"); + fprintf(stdout, "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", + fprintf(stdout, "\nLength %d\nIsEightBit %d\n", (int)__CFStrLength(str), __CFStrIsEightBit(str)); + fprintf(stdout, "HasLengthByte %d\nHasNullByte %d\nInlineContents %d\n", __CFStrHasLengthByte(str), __CFStrHasNullByte(str), __CFStrIsInline(str)); - printf ("Allocator "); + fprintf(stdout, "Allocator "); if (alloc != kCFAllocatorSystemDefault) { - printf ("%p\n", (void *)alloc); + fprintf(stdout, "%p\n", (void *)alloc); } else { - printf ("SystemDefault\n"); + fprintf(stdout, "SystemDefault\n"); } - printf ("Mutable %d\n", __CFStrIsMutable(str)); + fprintf(stdout, "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"); + if (__CFStrContentsDeallocator(str)) fprintf(stdout, "ContentsDeallocatorFunc %p\n", (void *)__CFStrContentsDeallocator(str)); + else fprintf(stdout, "ContentsDeallocatorFunc None\n"); } else if (__CFStrIsMutable(str) && __CFStrHasContentsAllocator(str)) { - printf ("ExternalContentsAllocator %p\n", (void *)__CFStrContentsAllocator((CFMutableStringRef)str)); + fprintf(stdout, "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)); + fprintf(stdout, "CurrentCapacity %d\n%sCapacity %d\n", (int)__CFStrCapacity(str), __CFStrIsFixed(str) ? "Fixed" : "Desired", (int)__CFStrDesiredCapacity(str)); } - printf ("Contents %p\n", (void *)__CFStrContents(str)); + fprintf(stdout, "Contents %p\n", (void *)__CFStrContents(str)); } + diff --git a/String.subproj/CFString.h b/String.subproj/CFString.h index 23446be..2d3f7d4 100644 --- a/String.subproj/CFString.h +++ b/String.subproj/CFString.h @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -23,7 +21,7 @@ * @APPLE_LICENSE_HEADER_END@ */ /* CFString.h - Copyright (c) 1998-2003, Apple, Inc. All rights reserved. + Copyright (c) 1998-2005, Apple, Inc. All rights reserved. */ #if !defined(__COREFOUNDATION_CFSTRING__) @@ -129,6 +127,16 @@ typedef enum { kCFStringEncodingUnicode = 0x0100, /* kTextEncodingUnicodeDefault + kTextEncodingDefaultFormat (aka kUnicode16BitFormat) */ kCFStringEncodingUTF8 = 0x08000100, /* kTextEncodingUnicodeDefault + kUnicodeUTF8Format */ kCFStringEncodingNonLossyASCII = 0x0BFF /* 7bit Unicode variants used by Cocoa & Java */ +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4 + , + kCFStringEncodingUTF16 = 0x0100, /* kTextEncodingUnicodeDefault + kUnicodeUTF16Format (alias of kCFStringEncodingUnicode) */ + kCFStringEncodingUTF16BE = 0x10000100, /* kTextEncodingUnicodeDefault + kUnicodeUTF16BEFormat */ + kCFStringEncodingUTF16LE = 0x14000100, /* kTextEncodingUnicodeDefault + kUnicodeUTF16LEFormat */ + + kCFStringEncodingUTF32 = 0x0c000100, /* kTextEncodingUnicodeDefault + kUnicodeUTF32Format */ + kCFStringEncodingUTF32BE = 0x18000100, /* kTextEncodingUnicodeDefault + kUnicodeUTF32BEFormat */ + kCFStringEncodingUTF32LE = 0x1c000100 /* kTextEncodingUnicodeDefault + kUnicodeUTF32LEFormat */ +#endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4 */ } CFStringBuiltInEncodings; /* CFString type ID */ @@ -249,6 +257,7 @@ 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 @@ -331,6 +340,25 @@ CFStringEncoding CFStringGetSystemEncoding(void); /* The default encoding for t CF_EXPORT CFIndex CFStringGetMaximumSizeForEncoding(CFIndex length, CFStringEncoding encoding); /* Max bytes a string of specified length (in UniChars) will take up if encoded */ + +/*** FileSystem path conversion functions ***/ + +/* Extract the contents of the string as a NULL-terminated 8-bit string appropriate for passing to POSIX APIs. The string is zero-terminated. false will be returned if the conversion results don't fit into the buffer. Use CFStringGetMaximumSizeOfFileSystemRepresentation() if you want to make sure the buffer is of sufficient length. +*/ +CF_EXPORT +Boolean CFStringGetFileSystemRepresentation(CFStringRef string, char *buffer, CFIndex maxBufLen) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; + +/* Get the upper bound on the number of bytes required to hold the file system representation for the string. This result is returned quickly as a very rough approximation, and could be much larger than the actual space required. The result includes space for the zero termination. If you are allocating a buffer for long-term keeping, it's recommended that you reallocate it smaller (to be the right size) after calling CFStringGetFileSystemRepresentation(). +*/ +CF_EXPORT +CFIndex CFStringGetMaximumSizeOfFileSystemRepresentation(CFStringRef string) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; + +/* Create a CFString from the specified zero-terminated POSIX file system representation. If the conversion fails (possible due to bytes in the buffer not being a valid sequence of bytes for the appropriate character encoding), NULL is returned. +*/ +CF_EXPORT +CFStringRef CFStringCreateWithFileSystemRepresentation(CFAllocatorRef alloc, const char *buffer) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; + + /*** Comparison functions. ***/ /* Find and compare flags; these are OR'ed together as compareOptions or searchOptions in the various functions. @@ -448,6 +476,7 @@ CF_EXPORT Boolean CFStringFindCharacterFromSet(CFStringRef theString, CFCharacte CF_EXPORT void CFStringGetLineBounds(CFStringRef theString, CFRange range, CFIndex *lineBeginIndex, CFIndex *lineEndIndex, CFIndex *contentsEndIndex); + /*** Exploding and joining strings with a separator string ***/ CF_EXPORT @@ -456,6 +485,7 @@ CFStringRef CFStringCreateByCombiningStrings(CFAllocatorRef alloc, CFArrayRef th 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 @@ -464,6 +494,7 @@ SInt32 CFStringGetIntValue(CFStringRef str); /* Skips whitespace; returns 0 on CF_EXPORT double CFStringGetDoubleValue(CFStringRef str); /* Skips whitespace; returns 0.0 on error */ + /*** MutableString functions ***/ /* CFStringAppend("abcdef", "xxxxx") -> "abcdefxxxxx" @@ -592,6 +623,31 @@ typedef enum { CF_EXPORT void CFStringNormalize(CFMutableStringRef theString, CFStringNormalizationForm theForm); #endif +/* Perform string transliteration. The transformation represented by transform (see below for the full list of transforms supported) is applied to the given range of string, modifying it in place. Only the specified range will be modified, but the transform may look at portions of the string outside that range for context. NULL range pointer causes the whole string to be transformed. On return, range is modified to reflect the new range corresponding to the original range. reverse indicates that the inverse transform should be used instead, if it exists. If the transform is successful, true is returned; if unsuccessful, false. Reasons for the transform being unsuccessful include an invalid transform identifier, or attempting to reverse an irreversible transform. +*/ +Boolean CFStringTransform(CFMutableStringRef string, CFRange *range, CFStringRef transform, Boolean reverse) AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; + +/* Transform identifiers for CFStringTransform() +*/ +CF_EXPORT const CFStringRef kCFStringTransformStripCombiningMarks AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; +CF_EXPORT const CFStringRef kCFStringTransformToLatin AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; +CF_EXPORT const CFStringRef kCFStringTransformFullwidthHalfwidth AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; +CF_EXPORT const CFStringRef kCFStringTransformLatinKatakana AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; +CF_EXPORT const CFStringRef kCFStringTransformLatinHiragana AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; +CF_EXPORT const CFStringRef kCFStringTransformHiraganaKatakana AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; +CF_EXPORT const CFStringRef kCFStringTransformMandarinLatin AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; +CF_EXPORT const CFStringRef kCFStringTransformLatinHangul AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; +CF_EXPORT const CFStringRef kCFStringTransformLatinArabic AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; +CF_EXPORT const CFStringRef kCFStringTransformLatinHebrew AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; +CF_EXPORT const CFStringRef kCFStringTransformLatinThai AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; +CF_EXPORT const CFStringRef kCFStringTransformLatinCyrillic AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; +CF_EXPORT const CFStringRef kCFStringTransformLatinGreek AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; +CF_EXPORT const CFStringRef kCFStringTransformToXMLHex AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; +CF_EXPORT const CFStringRef kCFStringTransformToUnicodeName AVAILABLE_MAC_OS_X_VERSION_10_4_AND_LATER; + + +/*** General encoding related functionality ***/ + /* This returns availability of the encoding on the system */ CF_EXPORT @@ -638,6 +694,8 @@ CFStringRef CFStringConvertEncodingToIANACharSetName(CFStringEncoding encoding) 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), @@ -692,6 +750,9 @@ CF_INLINE UniChar CFStringGetCharacterFromInlineBuffer(CFStringInlineBuffer *buf #endif /* CF_INLINE */ + + + /* Rest of the stuff in this file is private and should not be used directly */ /* For debugging only diff --git a/String.subproj/CFStringDefaultEncoding.h b/String.subproj/CFStringDefaultEncoding.h new file mode 100644 index 0000000..976e06a --- /dev/null +++ b/String.subproj/CFStringDefaultEncoding.h @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFStringDefaultEncoding.h + Copyright (c) 1998-2005, Apple, Inc. All rights reserved. +*/ + +/* This file defines static inline functions used both by CarbonCore & CF. */ + +#include +#if defined(__MACH__) +#include +#include +#include +#include +#include +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +#define __kCFUserEncodingEnvVariableName ("__CF_USER_TEXT_ENCODING") +#define __kCFMaxDefaultEncodingFileLength (24) +#define __kCFUserEncodingFileName ("/.CFUserTextEncoding") + +/* This function is used to obtain users' default script/region code. + The function first looks at environment variable __kCFUserEncodingEnvVariableName, then, reads the configuration file in user's home directory. +*/ +CF_INLINE void __CFStringGetUserDefaultEncoding(UInt32 *oScriptValue, UInt32 *oRegionValue) { + char *stringValue; + char buffer[__kCFMaxDefaultEncodingFileLength]; + int uid = getuid(); + + if ((stringValue = getenv(__kCFUserEncodingEnvVariableName)) != NULL) { + if ((uid == strtol(stringValue, &stringValue, 0)) && (':' == *stringValue)) { + ++stringValue; + } else { + stringValue = NULL; + } + } + + if ((stringValue == NULL) && ((uid > 0) || getenv("HOME"))) { + struct passwd *passwdp; + + if ((passwdp = getpwuid((uid_t)uid))) { + char filename[MAXPATHLEN + 1]; + int fd; + + strcpy(filename, passwdp->pw_dir); + strcat(filename, __kCFUserEncodingFileName); + + if ((fd = open(filename, O_RDONLY, 0)) == -1) { + // Cannot open the file. Let's fallback to smRoman/verUS + snprintf(filename, sizeof(filename), "%s=0x%X:0:0", __kCFUserEncodingEnvVariableName, uid); + putenv(filename); + } else { + int readSize; + + // cjk: We do not turn on F_NOCACHE on the fd here, because + // many processes read this file on startup, and caching the + // is probably a good thing, for the system as a whole. + readSize = read(fd, buffer, __kCFMaxDefaultEncodingFileLength - 1); + buffer[(readSize < 0 ? 0 : readSize)] = '\0'; + close(fd); + stringValue = buffer; + + // Well, we already have a buffer, let's reuse it + snprintf(filename, sizeof(filename), "%s=0x%X:%s", __kCFUserEncodingEnvVariableName, uid, buffer); + putenv(filename); + } + } + } + + if (stringValue) { + *oScriptValue = strtol(stringValue, &stringValue, 0); + if (*stringValue == ':') { + if (oRegionValue) *oRegionValue = strtol(++stringValue, NULL, 0); + return; + } + } + + // Falling back + *oScriptValue = 0; // smRoman + if (oRegionValue) *oRegionValue = 0; // verUS +} + +CF_INLINE uint32_t __CFStringGetInstallationRegion() { + char *stringValue = NULL; + char buffer[__kCFMaxDefaultEncodingFileLength]; + struct passwd *passwdp; + + if ((passwdp = getpwuid((uid_t)0))) { + char filename[MAXPATHLEN + 1]; + int fd; + + strcpy(filename, passwdp->pw_dir); + strcat(filename, __kCFUserEncodingFileName); + + if ((fd = open(filename, O_RDONLY, 0)) != -1) { + int readSize; + + // cjk: We do not turn on F_NOCACHE on the fd here, because + // many processes read this file on startup, and caching the + // is probably a good thing, for the system as a whole. + readSize = read(fd, buffer, __kCFMaxDefaultEncodingFileLength - 1); + buffer[(readSize < 0 ? 0 : readSize)] = '\0'; + close(fd); + stringValue = buffer; + } + } + + if (stringValue) { + (void)strtol(stringValue, &stringValue, 0); + if (*stringValue == ':') return strtol(++stringValue, NULL, 0); + } + + return 0; // verUS +} + +CF_INLINE void __CFStringGetInstallationEncodingAndRegion(uint32_t *encoding, uint32_t *region) { + char *stringValue = NULL; + char buffer[__kCFMaxDefaultEncodingFileLength]; + struct passwd *passwdp; + + *encoding = 0; *region = 0; + + if ((passwdp = getpwuid((uid_t)0))) { + char filename[MAXPATHLEN + 1]; + int fd; + + strcpy(filename, passwdp->pw_dir); + strcat(filename, __kCFUserEncodingFileName); + + if ((fd = open(filename, O_RDONLY, 0)) != -1) { + int readSize; + + // cjk: We do not turn on F_NOCACHE on the fd here, because + // many processes read this file on startup, and caching the + // is probably a good thing, for the system as a whole. + readSize = read(fd, buffer, __kCFMaxDefaultEncodingFileLength - 1); + buffer[(readSize < 0 ? 0 : readSize)] = '\0'; + close(fd); + stringValue = buffer; + } + } + + if (stringValue) { + *encoding = strtol(stringValue, &stringValue, 0); + if (*stringValue == ':') *region = strtol(++stringValue, NULL, 0); + } +} + +CF_INLINE void __CFStringSaveUserDefaultEncoding(UInt32 iScriptValue, UInt32 iRegionValue) { + struct passwd *passwdp; + + if ((passwdp = getpwuid(getuid()))) { + char filename[MAXPATHLEN + 1]; + int fd; + + strcpy(filename, passwdp->pw_dir); + strcat(filename, __kCFUserEncodingFileName); + + // In case, file exists + (void)unlink(filename); + + if ((fd = open(filename, O_WRONLY|O_CREAT, 0400)) != -1) { + char buffer[__kCFMaxDefaultEncodingFileLength]; + unsigned int writeSize; + + writeSize = snprintf(buffer, __kCFMaxDefaultEncodingFileLength, "0x%X:0x%X", (unsigned int)iScriptValue, (unsigned int)iRegionValue); + (void)write(fd, buffer, (writeSize > __kCFMaxDefaultEncodingFileLength ? __kCFMaxDefaultEncodingFileLength : writeSize)); + close(fd); + } + } +} + +#if defined(__cplusplus) +} +#endif + +#endif /* __MACH__ */ diff --git a/String.subproj/CFStringEncodingExt.h b/String.subproj/CFStringEncodingExt.h new file mode 100644 index 0000000..d162980 --- /dev/null +++ b/String.subproj/CFStringEncodingExt.h @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* CFStringEncodingExt.h + Copyright (c) 1998-2005, Apple, Inc. All rights reserved. +*/ + +#if !defined(__COREFOUNDATION_CFSTRINGENCODINGEXT__) +#define __COREFOUNDATION_CFSTRINGENCODINGEXT__ 1 + +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +typedef enum { +/* kCFStringEncodingMacRoman = 0L, defined in CoreFoundation/CFString.h */ + kCFStringEncodingMacJapanese = 1, + kCFStringEncodingMacChineseTrad = 2, + kCFStringEncodingMacKorean = 3, + kCFStringEncodingMacArabic = 4, + kCFStringEncodingMacHebrew = 5, + kCFStringEncodingMacGreek = 6, + kCFStringEncodingMacCyrillic = 7, + kCFStringEncodingMacDevanagari = 9, + kCFStringEncodingMacGurmukhi = 10, + kCFStringEncodingMacGujarati = 11, + kCFStringEncodingMacOriya = 12, + kCFStringEncodingMacBengali = 13, + kCFStringEncodingMacTamil = 14, + kCFStringEncodingMacTelugu = 15, + kCFStringEncodingMacKannada = 16, + kCFStringEncodingMacMalayalam = 17, + kCFStringEncodingMacSinhalese = 18, + kCFStringEncodingMacBurmese = 19, + kCFStringEncodingMacKhmer = 20, + kCFStringEncodingMacThai = 21, + kCFStringEncodingMacLaotian = 22, + kCFStringEncodingMacGeorgian = 23, + kCFStringEncodingMacArmenian = 24, + kCFStringEncodingMacChineseSimp = 25, + kCFStringEncodingMacTibetan = 26, + kCFStringEncodingMacMongolian = 27, + kCFStringEncodingMacEthiopic = 28, + kCFStringEncodingMacCentralEurRoman = 29, + kCFStringEncodingMacVietnamese = 30, + kCFStringEncodingMacExtArabic = 31, + /* The following use script code 0, smRoman */ + kCFStringEncodingMacSymbol = 33, + kCFStringEncodingMacDingbats = 34, + kCFStringEncodingMacTurkish = 35, + kCFStringEncodingMacCroatian = 36, + kCFStringEncodingMacIcelandic = 37, + kCFStringEncodingMacRomanian = 38, + kCFStringEncodingMacCeltic = 39, + kCFStringEncodingMacGaelic = 40, + /* The following use script code 4, smArabic */ + kCFStringEncodingMacFarsi = 0x8C, /* Like MacArabic but uses Farsi digits */ + /* The following use script code 7, smCyrillic */ + kCFStringEncodingMacUkrainian = 0x98, + /* The following use script code 32, smUnimplemented */ + kCFStringEncodingMacInuit = 0xEC, + kCFStringEncodingMacVT100 = 0xFC, /* VT100/102 font from Comm Toolbox: Latin-1 repertoire + box drawing etc */ + /* Special Mac OS encodings*/ + kCFStringEncodingMacHFS = 0xFF, /* Meta-value, should never appear in a table */ + + /* Unicode & ISO UCS encodings begin at 0x100 */ + /* We don't use Unicode variations defined in TextEncoding; use the ones in CFString.h, instead. */ + + /* ISO 8-bit and 7-bit encodings begin at 0x200 */ +/* kCFStringEncodingISOLatin1 = 0x0201, defined in CoreFoundation/CFString.h */ + kCFStringEncodingISOLatin2 = 0x0202, /* ISO 8859-2 */ + kCFStringEncodingISOLatin3 = 0x0203, /* ISO 8859-3 */ + kCFStringEncodingISOLatin4 = 0x0204, /* ISO 8859-4 */ + kCFStringEncodingISOLatinCyrillic = 0x0205, /* ISO 8859-5 */ + kCFStringEncodingISOLatinArabic = 0x0206, /* ISO 8859-6, =ASMO 708, =DOS CP 708 */ + kCFStringEncodingISOLatinGreek = 0x0207, /* ISO 8859-7 */ + kCFStringEncodingISOLatinHebrew = 0x0208, /* ISO 8859-8 */ + kCFStringEncodingISOLatin5 = 0x0209, /* ISO 8859-9 */ + kCFStringEncodingISOLatin6 = 0x020A, /* ISO 8859-10 */ + kCFStringEncodingISOLatinThai = 0x020B, /* ISO 8859-11 */ + kCFStringEncodingISOLatin7 = 0x020D, /* ISO 8859-13 */ + kCFStringEncodingISOLatin8 = 0x020E, /* ISO 8859-14 */ + kCFStringEncodingISOLatin9 = 0x020F, /* ISO 8859-15 */ +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4 + kCFStringEncodingISOLatin10 = 0x0210, /* ISO 8859-16 */ +#endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4 */ + + /* MS-DOS & Windows encodings begin at 0x400 */ + kCFStringEncodingDOSLatinUS = 0x0400, /* code page 437 */ + kCFStringEncodingDOSGreek = 0x0405, /* code page 737 (formerly code page 437G) */ + kCFStringEncodingDOSBalticRim = 0x0406, /* code page 775 */ + kCFStringEncodingDOSLatin1 = 0x0410, /* code page 850, "Multilingual" */ + kCFStringEncodingDOSGreek1 = 0x0411, /* code page 851 */ + kCFStringEncodingDOSLatin2 = 0x0412, /* code page 852, Slavic */ + kCFStringEncodingDOSCyrillic = 0x0413, /* code page 855, IBM Cyrillic */ + kCFStringEncodingDOSTurkish = 0x0414, /* code page 857, IBM Turkish */ + kCFStringEncodingDOSPortuguese = 0x0415, /* code page 860 */ + kCFStringEncodingDOSIcelandic = 0x0416, /* code page 861 */ + kCFStringEncodingDOSHebrew = 0x0417, /* code page 862 */ + kCFStringEncodingDOSCanadianFrench = 0x0418, /* code page 863 */ + kCFStringEncodingDOSArabic = 0x0419, /* code page 864 */ + kCFStringEncodingDOSNordic = 0x041A, /* code page 865 */ + kCFStringEncodingDOSRussian = 0x041B, /* code page 866 */ + kCFStringEncodingDOSGreek2 = 0x041C, /* code page 869, IBM Modern Greek */ + kCFStringEncodingDOSThai = 0x041D, /* code page 874, also for Windows */ + kCFStringEncodingDOSJapanese = 0x0420, /* code page 932, also for Windows */ + kCFStringEncodingDOSChineseSimplif = 0x0421, /* code page 936, also for Windows */ + kCFStringEncodingDOSKorean = 0x0422, /* code page 949, also for Windows; Unified Hangul Code */ + kCFStringEncodingDOSChineseTrad = 0x0423, /* code page 950, also for Windows */ +/* kCFStringEncodingWindowsLatin1 = 0x0500, defined in CoreFoundation/CFString.h */ + kCFStringEncodingWindowsLatin2 = 0x0501, /* code page 1250, Central Europe */ + kCFStringEncodingWindowsCyrillic = 0x0502, /* code page 1251, Slavic Cyrillic */ + kCFStringEncodingWindowsGreek = 0x0503, /* code page 1253 */ + kCFStringEncodingWindowsLatin5 = 0x0504, /* code page 1254, Turkish */ + kCFStringEncodingWindowsHebrew = 0x0505, /* code page 1255 */ + kCFStringEncodingWindowsArabic = 0x0506, /* code page 1256 */ + kCFStringEncodingWindowsBalticRim = 0x0507, /* code page 1257 */ + kCFStringEncodingWindowsVietnamese = 0x0508, /* code page 1258 */ + kCFStringEncodingWindowsKoreanJohab = 0x0510, /* code page 1361, for Windows NT */ + + /* Various national standards begin at 0x600 */ +/* kCFStringEncodingASCII = 0x0600, defined in CoreFoundation/CFString.h */ +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4 + kCFStringEncodingANSEL = 0x0601, /* ANSEL (ANSI Z39.47) */ +#endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4 */ + kCFStringEncodingJIS_X0201_76 = 0x0620, + kCFStringEncodingJIS_X0208_83 = 0x0621, + kCFStringEncodingJIS_X0208_90 = 0x0622, + kCFStringEncodingJIS_X0212_90 = 0x0623, + kCFStringEncodingJIS_C6226_78 = 0x0624, + kCFStringEncodingShiftJIS_X0213_00 = 0x0628, /* Shift-JIS format encoding of JIS X0213 planes 1 and 2*/ +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4 + kCFStringEncodingShiftJIS_X0213_MenKuTen = 0x0629, /* JIS X0213 in plane-row-column notation */ +#endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4 */ + kCFStringEncodingGB_2312_80 = 0x0630, + kCFStringEncodingGBK_95 = 0x0631, /* annex to GB 13000-93; for Windows 95 */ + kCFStringEncodingGB_18030_2000 = 0x0632, + kCFStringEncodingKSC_5601_87 = 0x0640, /* same as KSC 5601-92 without Johab annex */ + kCFStringEncodingKSC_5601_92_Johab = 0x0641, /* KSC 5601-92 Johab annex */ + kCFStringEncodingCNS_11643_92_P1 = 0x0651, /* CNS 11643-1992 plane 1 */ + kCFStringEncodingCNS_11643_92_P2 = 0x0652, /* CNS 11643-1992 plane 2 */ + kCFStringEncodingCNS_11643_92_P3 = 0x0653, /* CNS 11643-1992 plane 3 (was plane 14 in 1986 version) */ + + /* ISO 2022 collections begin at 0x800 */ + kCFStringEncodingISO_2022_JP = 0x0820, + kCFStringEncodingISO_2022_JP_2 = 0x0821, + kCFStringEncodingISO_2022_JP_1 = 0x0822, /* RFC 2237*/ + kCFStringEncodingISO_2022_JP_3 = 0x0823, /* JIS X0213*/ + kCFStringEncodingISO_2022_CN = 0x0830, + kCFStringEncodingISO_2022_CN_EXT = 0x0831, + kCFStringEncodingISO_2022_KR = 0x0840, + + /* EUC collections begin at 0x900 */ + kCFStringEncodingEUC_JP = 0x0920, /* ISO 646, 1-byte katakana, JIS 208, JIS 212 */ + kCFStringEncodingEUC_CN = 0x0930, /* ISO 646, GB 2312-80 */ + kCFStringEncodingEUC_TW = 0x0931, /* ISO 646, CNS 11643-1992 Planes 1-16 */ + kCFStringEncodingEUC_KR = 0x0940, /* ISO 646, KS C 5601-1987 */ + + /* Misc standards begin at 0xA00 */ + kCFStringEncodingShiftJIS = 0x0A01, /* plain Shift-JIS */ + kCFStringEncodingKOI8_R = 0x0A02, /* Russian internet standard */ + kCFStringEncodingBig5 = 0x0A03, /* Big-5 (has variants) */ + kCFStringEncodingMacRomanLatin1 = 0x0A04, /* Mac OS Roman permuted to align with ISO Latin-1 */ + kCFStringEncodingHZ_GB_2312 = 0x0A05, /* HZ (RFC 1842, for Chinese mail & news) */ + kCFStringEncodingBig5_HKSCS_1999 = 0x0A06, /* Big-5 with Hong Kong special char set supplement*/ +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4 + kCFStringEncodingVISCII = 0x0A07, /* RFC 1456, Vietnamese */ + kCFStringEncodingKOI8_U = 0x0A08, /* RFC 2319, Ukrainian */ + kCFStringEncodingBig5_E = 0x0A09, /* Taiwan Big-5E standard */ +#endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4 */ + + /* Other platform encodings*/ +/* kCFStringEncodingNextStepLatin = 0x0B01, defined in CoreFoundation/CFString.h */ +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4 + kCFStringEncodingNextStepJapanese = 0x0B02, /* NextStep Japanese encoding */ +#endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4 */ + + /* EBCDIC & IBM host encodings begin at 0xC00 */ + kCFStringEncodingEBCDIC_US = 0x0C01, /* basic EBCDIC-US */ + kCFStringEncodingEBCDIC_CP037 = 0x0C02 /* code page 037, extended EBCDIC (Latin-1 set) for US,Canada... */ +} CFStringEncodings; + +#if defined(__cplusplus) +} +#endif + +#endif /* !__COREFOUNDATION_CFSTRINGENCODINGEXT__ */ + diff --git a/String.subproj/CFStringEncodings.c b/String.subproj/CFStringEncodings.c index a82057c..c85bc11 100644 --- a/String.subproj/CFStringEncodings.c +++ b/String.subproj/CFStringEncodings.c @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -30,7 +28,7 @@ #include "CFInternal.h" #include #include -#include "CFUtilities.h" +#include "CFUtilitiesPriv.h" #include #include "CFStringEncodingConverterExt.h" #include "CFUniChar.h" @@ -39,7 +37,7 @@ static UInt32 __CFWantsToUseASCIICompatibleConversion = (UInt32)-1; CF_INLINE UInt32 __CFGetASCIICompatibleFlag(void) { if (__CFWantsToUseASCIICompatibleConversion == (UInt32)-1) { - __CFWantsToUseASCIICompatibleConversion = false; + __CFWantsToUseASCIICompatibleConversion = false; } return (__CFWantsToUseASCIICompatibleConversion ? kCFStringEncodingASCIICompatibleConversion : 0); } @@ -100,12 +98,6 @@ __private_extern__ void __CFStrConvertBytesToUnicode(const uint8_t *bytes, UniCh #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 @@ -127,204 +119,314 @@ enum { }; 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 ((encoding == kCFStringEncodingUTF16) || (encoding == kCFStringEncodingUTF16BE) || (encoding == kCFStringEncodingUTF16LE)) { // UTF-16 + const UTF16Char *src = (const UTF16Char *)bytes; + const UTF16Char *limit = (const UTF16Char *)(bytes + len); + bool swap = false; + + if (kCFStringEncodingUTF16 == encoding) { + UTF16Char bom = ((*src == 0xFFFE) || (*src == 0xFEFF) ? *(src++) : 0); + +#if defined(__BIG_ENDIAN__) + if (bom == 0xFFFE) swap = true; +#else + if (bom != 0xFEFF) swap = true; +#endif + if (bom) useClientsMemoryPtr = NULL; + } else { +#if defined(__BIG_ENDIAN__) + if (kCFStringEncodingUTF16LE == encoding) swap = true; +#else + if (kCFStringEncodingUTF16BE == encoding) swap = true; +#endif } - 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]; - } + buffer->numChars = limit - src; + + if (useClientsMemoryPtr && !swap) { // If the caller is ready to deal with no-copy situation, and the situation is possible, indicate it... + *useClientsMemoryPtr = true; + buffer->chars.unicode = (UniChar *)src; + buffer->isASCII = false; } 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)); + if (buffer->isASCII) { // Let's see if we can reduce the Unicode down to ASCII... + const UTF16Char *characters = src; + UTF16Char mask = (swap ? 0x80FF : 0xFF80); + + while (characters < limit) { + if (*(characters++) & mask) { + buffer->isASCII = false; + break; + } } } - } - 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; + + if (buffer->isASCII) { + uint8_t *dst; + if (NULL == buffer->chars.ascii) { // we never reallocate when buffer is supplied + if (buffer->numChars > MAX_LOCAL_CHARS) { + buffer->chars.ascii = CFAllocatorAllocate(buffer->allocator, (buffer->numChars * sizeof(uint8_t)), 0); + buffer->shouldFreeChars = true; + } else { + buffer->chars.ascii = (uint8_t *)buffer->localBuffer; + } } - 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; + dst = buffer->chars.ascii; + + if (swap) { + while (src < limit) *(dst++) = (*(src++) >> 8); } else { - mode = __NSNonLossyErrorMode; + while (src < limit) *(dst++) = *(src++); } - break; + } else { + UTF16Char *dst; - default: - if (mode < __NSNonLossyHexFinalMode) { - if ((character >= '0') && (character <= '9')) { - currentValue = (currentValue << 4) | (character - '0'); - if (++mode == __NSNonLossyHexFinalMode) mode = __NSNonLossyASCIIMode; + if (NULL == buffer->chars.unicode) { // we never reallocate when buffer is supplied + if (buffer->numChars > MAX_LOCAL_UNICHARS) { + buffer->chars.unicode = CFAllocatorAllocate(buffer->allocator, (buffer->numChars * sizeof(UTF16Char)), 0); + buffer->shouldFreeChars = true; } 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; - } + buffer->chars.unicode = (UTF16Char *)buffer->localBuffer; } + } + dst = buffer->chars.unicode; + + if (swap) { + while (src < limit) *(dst++) = CFSwapInt16(*(src++)); } else { - if ((character >= '0') && (character <= '9')) { - currentValue = (currentValue << 3) | (character - '0'); - if (++mode == __NSNonLossyOctalFinalMode) mode = __NSNonLossyASCIIMode; - } else { - mode = __NSNonLossyErrorMode; - } + memmove(dst, src, buffer->numChars * sizeof(UTF16Char)); } - break; } + } + } else if ((encoding == kCFStringEncodingUTF32) || (encoding == kCFStringEncodingUTF32BE) || (encoding == kCFStringEncodingUTF32LE)) { + const UTF32Char *src = (const UTF32Char *)bytes; + const UTF32Char *limit = (const UTF32Char *)(bytes + len); + bool swap = false; - if (mode == __NSNonLossyASCIIMode) { - buffer->chars.unicode[buffer->numChars++] = currentValue; - } else if (mode == __NSNonLossyErrorMode) { - return false; - } - } - return (mode == __NSNonLossyASCIIMode); - } + if (kCFStringEncodingUTF32 == encoding) { + UTF32Char bom = ((*src == 0xFFFE0000) || (*src == 0x0000FEFF) ? *(src++) : 0); - 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; +#if defined(__BIG_ENDIAN__) + if (bom == 0xFFFE0000) swap = true; +#else + if (bom != 0x0000FEFF) swap = true; +#endif + } else { +#if defined(__BIG_ENDIAN__) + if (kCFStringEncodingUTF32LE == encoding) swap = true; +#else + if (kCFStringEncodingUTF32BE == encoding) swap = true; +#endif } - allASCII = !alwaysUnicode; - if (allASCII) { - for (idx = 0; idx < len; idx++) { - if (128 <= chars[idx]) { - allASCII = false; - break; + + buffer->numChars = limit - src; + + { + // Let's see if we have non-ASCII or non-BMP + const UTF32Char *characters = src; + UTF32Char asciiMask = (swap ? 0x80FFFFFF : 0xFFFFFF80); + UTF32Char bmpMask = (swap ? 0x0000FFFF : 0xFFFF0000); + + while (characters < limit) { + if (*characters & asciiMask) { + buffer->isASCII = false; + if (*characters & bmpMask) ++(buffer->numChars); } + ++characters; } } - 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; + + if (buffer->isASCII) { + uint8_t *dst; + if (NULL == buffer->chars.ascii) { // we never reallocate when buffer is supplied + if (buffer->numChars > MAX_LOCAL_CHARS) { + buffer->chars.ascii = CFAllocatorAllocate(buffer->allocator, (buffer->numChars * sizeof(uint8_t)), 0); + buffer->shouldFreeChars = true; + } else { + buffer->chars.ascii = (uint8_t *)buffer->localBuffer; + } } + dst = buffer->chars.ascii; + if (swap) { + while (src < limit) *(dst++) = (*(src++) >> 24); + } else { + while (src < limit) *(dst++) = *(src++); + } + } else { + if (NULL == buffer->chars.unicode) { // we never reallocate when buffer is supplied + if (buffer->numChars > MAX_LOCAL_UNICHARS) { + buffer->chars.unicode = CFAllocatorAllocate(buffer->allocator, (buffer->numChars * sizeof(UTF16Char)), 0); + buffer->shouldFreeChars = true; + } else { + buffer->chars.unicode = (UTF16Char *)buffer->localBuffer; + } + } + CFUniCharFromUTF32(src, limit - src, buffer->chars.unicode, false, +#if defined(__BIG_ENDIAN__) + !swap +#else + swap +#endif + ); + } + } else { + UInt32 idx; + const uint8_t *chars = (const uint8_t *)bytes; + const uint8_t *end = chars + len; + + switch (encoding) { + 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) { - 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; + 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; } - buffer->numChars += numDone; } + return (mode == __NSNonLossyASCIIMode); } - 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; - } + + 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; + } + if (buffer->isASCII) { + for (idx = 0; idx < len; idx++) { + if (128 <= chars[idx]) { + 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)); + 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; + } + } + break; + + default: + if (CFStringEncodingIsValidEncoding(encoding)) { + const CFStringEncodingConverter *converter = CFStringEncodingGetConverter(encoding); + Boolean isASCIISuperset = __CFStringEncodingIsSupersetOfASCII(encoding); + + if (!converter) return false; + + if (!isASCIISuperset) buffer->isASCII = false; + + if (buffer->isASCII) { + for (idx = 0; idx < len; idx++) { + if (128 <= chars[idx]) { + buffer->isASCII = false; + break; } } - buffer->isASCII = allASCII; - if (allASCII) { + } + + if (converter->encodingClass == kCFStringEncodingConverterCheapEightBit) { + 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)); @@ -343,46 +445,38 @@ Boolean __CFStringDecodeByteStream3(const uint8_t *bytes, UInt32 len, CFStringEn 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; + 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)); + 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; } - } else { - return false; } } + + return true; } @@ -454,15 +548,15 @@ CFIndex __CFStringEncodeByteStream(CFStringRef string, CFIndex rangeLoc, CFIndex totalBytesWritten += reqLength; numCharsProcessed++; } - } else if (encoding == kCFStringEncodingUnicode) { - CFIndex extraForBOM = generatingExternalFile ? sizeof(UniChar) : 0; + } else if ((encoding == kCFStringEncodingUTF16) || (encoding == kCFStringEncodingUTF16BE) || (encoding == kCFStringEncodingUTF16LE)) { + CFIndex extraForBOM = (generatingExternalFile && (encoding == kCFStringEncodingUTF16) ? 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 (extraForBOM) { /* Generate BOM */ #if defined(__BIG_ENDIAN__) *buffer++ = 0xfe; *buffer++ = 0xff; #else @@ -470,13 +564,89 @@ CFIndex __CFStringEncodeByteStream(CFStringRef string, CFIndex rangeLoc, CFIndex #endif } CFStringGetCharacters(string, CFRangeMake(rangeLoc, numCharsProcessed), (UniChar *)buffer); + if ( +#if defined(__BIG_ENDIAN__) + kCFStringEncodingUTF16LE +#else + kCFStringEncodingUTF16BE +#endif + == encoding) { // Need to swap + UTF16Char *characters = (UTF16Char *)buffer; + const UTF16Char *limit = characters + numCharsProcessed; + + while (characters < limit) { + *characters = CFSwapInt16(*characters); + ++characters; + } + } } + } else if ((encoding == kCFStringEncodingUTF32) || (encoding == kCFStringEncodingUTF32BE) || (encoding == kCFStringEncodingUTF32LE)) { + UTF32Char character; + CFStringInlineBuffer buf; + UTF32Char *characters = (UTF32Char *)buffer; + +#if defined(__BIG_ENDIAN__) + bool swap = (encoding == kCFStringEncodingUTF32LE ? true : false); +#else + bool swap = (encoding == kCFStringEncodingUTF32BE ? true : false); +#endif + + if (generatingExternalFile && (encoding == kCFStringEncodingUTF32)) { + totalBytesWritten += sizeof(UTF32Char); + if (characters) { + if (totalBytesWritten > max) { // insufficient buffer + totalBytesWritten = 0; + } else { +#if defined(__BIG_ENDIAN__) + *(characters++) = 0x0000FEFF; +#else + *(characters++) = 0xFFFE0000; +#endif + } + } + } + + CFStringInitInlineBuffer(string, &buf, CFRangeMake(rangeLoc, rangeLen)); + while (numCharsProcessed < rangeLen) { + character = CFStringGetCharacterFromInlineBuffer(&buf, numCharsProcessed); + + if (CFUniCharIsSurrogateHighCharacter(character)) { + UTF16Char otherCharacter; + + if (((numCharsProcessed + 1) < rangeLen) && CFUniCharIsSurrogateLowCharacter((otherCharacter = CFStringGetCharacterFromInlineBuffer(&buf, numCharsProcessed + 1)))) { + character = CFUniCharGetLongCharacterForSurrogatePair(character, otherCharacter); + } else if (lossByte) { + character = lossByte; + } else { + break; + } + } else if (CFUniCharIsSurrogateLowCharacter(character)) { + if (lossByte) { + character = lossByte; + } else { + break; + } + } + + totalBytesWritten += sizeof(UTF32Char); + + if (characters) { + if (totalBytesWritten > max) { + totalBytesWritten -= sizeof(UTF32Char); + break; + } + *(characters++) = (swap ? CFSwapInt32(character) : character); + } + + numCharsProcessed += (character > 0xFFFF ? 2 : 1); + } } else { CFIndex numChars; UInt32 flags; const unsigned char *cString = NULL; + BOOL isASCIISuperset = __CFStringEncodingIsSupersetOfASCII(encoding); - if (!CF_IS_OBJC(CFStringGetTypeID(), string) && __CFStringEncodingIsSupersetOfASCII(encoding)) { // Checking for NSString to avoid infinite recursion + if (!CF_IS_OBJC(CFStringGetTypeID(), string) && isASCIISuperset) { // Checking for NSString to avoid infinite recursion const unsigned char *ptr; if ((cString = CFStringGetCStringPtr(string, __CFStringGetEightBitStringEncoding()))) { ptr = (cString += rangeLoc); @@ -535,7 +705,8 @@ CFIndex __CFStringEncodeByteStream(CFStringRef string, CFIndex rangeLoc, CFIndex 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(); + // Aki 11/24/04 __CFGetASCIICompatibleFlag() is called only for non-ASCII superset encodings. Otherwise, it could lead to a deadlock (see 3890536). + flags = (lossByte ? ((unsigned char)lossByte == 0xFF && encoding == kCFStringEncodingASCII ? kCFStringEncodingAllowLossyConversion : CFStringEncodingLossyByteToMask(lossByte)) : 0) | (generatingExternalFile ? kCFStringEncodingPrependBOM : 0) | (isASCIISuperset ? 0 : __CFGetASCIICompatibleFlag()); if (!cString && (cString = (const char*)CFStringGetCharactersPtr(string))) { // Must be Unicode string if (CFStringEncodingIsValidEncoding(encoding)) { // Converter available in CF @@ -633,9 +804,25 @@ CFIndex __CFStringEncodeByteStream(CFStringRef string, CFIndex rangeLoc, CFIndex return numCharsProcessed; } -#define MAX_STACK_BUFFER_LEN (255) -CF_EXPORT Boolean _CFStringGetFileSystemRepresentation(CFStringRef string, uint8_t *buffer, CFIndex maxBufLen) { +CFStringRef CFStringCreateWithFileSystemRepresentation(CFAllocatorRef alloc, const char *buffer) { + return CFStringCreateWithCString(alloc, buffer, CFStringFileSystemEncoding()); +} + +CFIndex CFStringGetMaximumSizeOfFileSystemRepresentation(CFStringRef string) { + CFIndex len = CFStringGetLength(string); + CFStringEncoding enc = CFStringGetFastestEncoding(string); + switch (enc) { + case kCFStringEncodingASCII: + case kCFStringEncodingMacRoman: + return len * 3 + 1; + default: + return len * 9 + 1; + } +} + +Boolean CFStringGetFileSystemRepresentation(CFStringRef string, char *buffer, CFIndex maxBufLen) { #if defined(__MACH__) +#define MAX_STACK_BUFFER_LEN (255) const UTF16Char *characters = CFStringGetCharactersPtr(string); uint32_t usedBufLen; @@ -662,7 +849,7 @@ CF_EXPORT Boolean _CFStringGetFileSystemRepresentation(CFStringRef string, uint8 range.length = (length < MAX_STACK_BUFFER_LEN ? length : MAX_STACK_BUFFER_LEN); } } else { - UTF16Char charactersBuffer[MAX_STACK_BUFFER_LEN]; // C99 Variable array + UTF16Char charactersBuffer[MAX_STACK_BUFFER_LEN]; CFStringGetCharacters(string, CFRangeMake(0, length), charactersBuffer); if (!CFUniCharDecompose(charactersBuffer, length, NULL, (void *)buffer, maxBufLen, &usedBufLen, true, kCFUniCharUTF8Format, true)) return false; @@ -683,3 +870,8 @@ CF_EXPORT Boolean _CFStringGetFileSystemRepresentation(CFStringRef string, uint8 return CFStringGetCString(string, buffer, maxBufLen, CFStringFileSystemEncoding()); #endif __MACH__ } + +Boolean _CFStringGetFileSystemRepresentation(CFStringRef string, uint8_t *buffer, CFIndex maxBufLen) { + return CFStringGetFileSystemRepresentation(string, buffer, maxBufLen); +} + diff --git a/String.subproj/CFStringScanner.c b/String.subproj/CFStringScanner.c index 6baafe7..601e65c 100644 --- a/String.subproj/CFStringScanner.c +++ b/String.subproj/CFStringScanner.c @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -258,7 +256,9 @@ __private_extern__ Boolean __CFStringScanDouble(CFStringInlineBuffer *buf, CFDic ch = __CFStringGetCharacterFromInlineBufferAux(buf, *indexPtr + numChars); } while (true); charPtr[numChars] = 0; // Null byte for strtod - result = strtod(charPtr, &endCharPtr); + + result = strtod_l(charPtr, &endCharPtr, NULL); + if (tmpAlloc) CFAllocatorDeallocate(tmpAlloc, charPtr); if (charPtr == endCharPtr) return false; *indexPtr += (endCharPtr - charPtr); diff --git a/String.subproj/CFStringUtilities.c b/String.subproj/CFStringUtilities.c index 51acaba..27bea85 100644 --- a/String.subproj/CFStringUtilities.c +++ b/String.subproj/CFStringUtilities.c @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -30,6 +28,7 @@ #include "CFInternal.h" #include "CFStringEncodingConverterExt.h" #include "CFUniChar.h" +#include #include #if defined(__MACH__) || defined(__LINUX__) #include @@ -39,16 +38,20 @@ #endif - Boolean CFStringIsEncodingAvailable(CFStringEncoding theEncoding) { switch (theEncoding) { case kCFStringEncodingASCII: // Built-in encodings case kCFStringEncodingMacRoman: - case kCFStringEncodingUnicode: case kCFStringEncodingUTF8: case kCFStringEncodingNonLossyASCII: case kCFStringEncodingWindowsLatin1: case kCFStringEncodingNextStepLatin: + case kCFStringEncodingUTF16: + case kCFStringEncodingUTF16BE: + case kCFStringEncodingUTF16LE: + case kCFStringEncodingUTF32: + case kCFStringEncodingUTF32BE: + case kCFStringEncodingUTF32LE: return true; default: @@ -65,25 +68,31 @@ CFStringRef CFStringGetNameOfEncoding(CFStringEncoding theEncoding) { 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); + switch (theEncoding) { + case kCFStringEncodingUTF8: theName = CFSTR("Unicode (UTF-8)"); break; + case kCFStringEncodingUTF16: theName = CFSTR("Unicode (UTF-16)"); break; + case kCFStringEncodingUTF16BE: theName = CFSTR("Unicode (UTF-16BE)"); break; + case kCFStringEncodingUTF16LE: theName = CFSTR("Unicode (UTF-16LE)"); break; + case kCFStringEncodingUTF32: theName = CFSTR("Unicode (UTF-32)"); break; + case kCFStringEncodingUTF32BE: theName = CFSTR("Unicode (UTF-32BE)"); break; + case kCFStringEncodingUTF32LE: theName = CFSTR("Unicode (UTF-32LE)"); break; + case kCFStringEncodingNonLossyASCII: theName = CFSTR("Non-lossy ASCII"); break; + + default: { + const uint8_t *encodingName = CFStringEncodingName(theEncoding); + + if (encodingName) { + theName = CFStringCreateWithCString(NULL, encodingName, kCFStringEncodingASCII); + } } + break; + } - if (theName) { - if (!mappingTable) mappingTable = CFDictionaryCreateMutable(NULL, 0, (const CFDictionaryKeyCallBacks *)NULL, &kCFTypeDictionaryValueCallBacks); + if (theName) { + if (!mappingTable) mappingTable = CFDictionaryCreateMutable(NULL, 0, (const CFDictionaryKeyCallBacks *)NULL, &kCFTypeDictionaryValueCallBacks); - CFDictionaryAddValue(mappingTable, (const void*)theEncoding, (const void*)theName); - CFRelease(theName); - } + CFDictionaryAddValue(mappingTable, (const void*)theEncoding, (const void*)theName); + CFRelease(theName); } } @@ -93,23 +102,19 @@ CFStringRef CFStringGetNameOfEncoding(CFStringEncoding theEncoding) { CFStringEncoding CFStringConvertIANACharSetNameToEncoding(CFStringRef charsetName) { static CFMutableDictionaryRef mappingTable = NULL; CFStringEncoding result = kCFStringEncodingInvalidId; - CFMutableStringRef lowerCharsetName = CFStringCreateMutableCopy(NULL, 0, charsetName); - - /* Create lowercase copy */ - CFStringLowercase(lowerCharsetName, NULL); + CFMutableStringRef lowerCharsetName; /* Check for common encodings first */ - if (CFStringCompare(lowerCharsetName, CFSTR("utf-8"), kCFCompareCaseInsensitive) == kCFCompareEqualTo) { - CFRelease(lowerCharsetName); + if (CFStringCompare(charsetName, CFSTR("utf-8"), kCFCompareCaseInsensitive) == kCFCompareEqualTo) { return kCFStringEncodingUTF8; - } else if (CFStringCompare(lowerCharsetName, CFSTR("iso-8859-1"), kCFCompareCaseInsensitive) == kCFCompareEqualTo) { - CFRelease(lowerCharsetName); + } else if (CFStringCompare(charsetName, CFSTR("iso-8859-1"), kCFCompareCaseInsensitive) == kCFCompareEqualTo) { return kCFStringEncodingISOLatin1; - } else if (CFStringCompare(lowerCharsetName, CFSTR("utf-16be"), kCFCompareCaseInsensitive) == kCFCompareEqualTo) { - CFRelease(lowerCharsetName); - return kCFStringEncodingUnicode; } + /* Create lowercase copy */ + lowerCharsetName = CFStringCreateMutableCopy(NULL, 0, charsetName); + CFStringLowercase(lowerCharsetName, NULL); + if (mappingTable == NULL) { CFMutableDictionaryRef table = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, (const CFDictionaryValueCallBacks *)NULL); const CFStringEncoding *encodings = CFStringGetListOfAvailableEncodings(); @@ -129,10 +134,15 @@ CFStringEncoding CFStringConvertIANACharSetNameToEncoding(CFStringRef charsetNa } 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); + // Adding Unicode names + CFDictionaryAddValue(table, (const void*)CFSTR("unicode-1-1"), (const void*)kCFStringEncodingUTF16); + CFDictionaryAddValue(table, (const void*)CFSTR("iso-10646-ucs-2"), (const void*)kCFStringEncodingUTF16); + CFDictionaryAddValue(table, (const void*)CFSTR("utf-16"), (const void*)kCFStringEncodingUTF16); + CFDictionaryAddValue(table, (const void*)CFSTR("utf-16be"), (const void*)kCFStringEncodingUTF16BE); + CFDictionaryAddValue(table, (const void*)CFSTR("utf-16le"), (const void*)kCFStringEncodingUTF16LE); + CFDictionaryAddValue(table, (const void*)CFSTR("utf-32"), (const void*)kCFStringEncodingUTF32); + CFDictionaryAddValue(table, (const void*)CFSTR("utf-32be"), (const void*)kCFStringEncodingUTF32BE); + CFDictionaryAddValue(table, (const void*)CFSTR("utf-32le"), (const void*)kCFStringEncodingUTF32LE); mappingTable = table; } @@ -151,23 +161,32 @@ CFStringRef CFStringConvertEncodingToIANACharSetName(CFStringEncoding encoding) 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; + switch (encoding) { + case kCFStringEncodingUTF16: theName = CFSTR("UTF-16"); break; + case kCFStringEncodingUTF16BE: theName = CFSTR("UTF-16BE"); break; + case kCFStringEncodingUTF16LE: theName = CFSTR("UTF-16LE"); break; + case kCFStringEncodingUTF32: theName = CFSTR("UTF-32"); break; + case kCFStringEncodingUTF32BE: theName = CFSTR("UTF-32BE"); break; + case kCFStringEncodingUTF32LE: theName = CFSTR("UTF-32LE"); break; + + + default: { + 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; + } } } - } + break; + } if (theName) { if (!mappingTable) mappingTable = CFDictionaryCreateMutable(NULL, 0, (const CFDictionaryKeyCallBacks *)NULL, &kCFTypeDictionaryValueCallBacks); @@ -205,19 +224,29 @@ enum { #define NSENCODING_MASK (1 << 31) UInt32 CFStringConvertEncodingToNSStringEncoding(CFStringEncoding theEncoding) { - if (theEncoding == kCFStringEncodingUTF8) { - return NSUTF8StringEncoding; - } else { - theEncoding &= 0xFFF; - } - switch (theEncoding) { + switch (theEncoding & 0xFFF) { 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; +#if defined(__MACH__) + case kCFStringEncodingEUC_JP: return NSJapaneseEUCStringEncoding; + case kCFStringEncodingMacSymbol: return NSSymbolStringEncoding; + case kCFStringEncodingDOSJapanese: return NSShiftJISStringEncoding; + case kCFStringEncodingISOLatin2: return NSISOLatin2StringEncoding; + case kCFStringEncodingWindowsCyrillic: return NSWindowsCP1251StringEncoding; + case kCFStringEncodingWindowsGreek: return NSWindowsCP1253StringEncoding; + case kCFStringEncodingWindowsLatin5: return NSWindowsCP1254StringEncoding; + case kCFStringEncodingWindowsLatin2: return NSWindowsCP1250StringEncoding; + case kCFStringEncodingISO_2022_JP: return NSISO2022JPStringEncoding; + case kCFStringEncodingUnicode: + if (theEncoding == kCFStringEncodingUTF16) return NSUnicodeStringEncoding; + else if (theEncoding == kCFStringEncodingUTF8) return NSUTF8StringEncoding; +#endif // __MACH__ + /* fall-through for other encoding schemes */ + default: return NSENCODING_MASK | theEncoding; } @@ -230,9 +259,20 @@ CFStringEncoding CFStringConvertNSStringEncodingToEncoding(UInt32 theEncoding) { case NSUTF8StringEncoding: return kCFStringEncodingUTF8; case NSISOLatin1StringEncoding: return kCFStringEncodingISOLatin1; case NSNonLossyASCIIStringEncoding: return kCFStringEncodingNonLossyASCII; - case NSUnicodeStringEncoding: return kCFStringEncodingUnicode; + case NSUnicodeStringEncoding: return kCFStringEncodingUTF16; case NSWindowsCP1252StringEncoding: return kCFStringEncodingWindowsLatin1; case NSMacOSRomanStringEncoding: return kCFStringEncodingMacRoman; +#if defined(__MACH__) + case NSSymbolStringEncoding: return kCFStringEncodingMacSymbol; + case NSJapaneseEUCStringEncoding: return kCFStringEncodingEUC_JP; + case NSShiftJISStringEncoding: return kCFStringEncodingDOSJapanese; + case NSISO2022JPStringEncoding: return kCFStringEncodingISO_2022_JP; + case NSISOLatin2StringEncoding: return kCFStringEncodingISOLatin2; + case NSWindowsCP1251StringEncoding: return kCFStringEncodingWindowsCyrillic; + case NSWindowsCP1253StringEncoding: return kCFStringEncodingWindowsGreek; + case NSWindowsCP1254StringEncoding: return kCFStringEncodingWindowsLatin5; + case NSWindowsCP1250StringEncoding: return kCFStringEncodingWindowsLatin2; +#endif // __MACH__ default: return ((theEncoding & NSENCODING_MASK) ? theEncoding & ~NSENCODING_MASK : kCFStringEncodingInvalidId); } @@ -248,56 +288,144 @@ static const uint16_t _CFToDOSCodePageList[] = { }; static const uint16_t _CFToWindowsCodePageList[] = { - 1252, 1250, 1251, 1253, 1254, 1255, 1256, 1257, 1361, + 1252, 1250, 1251, 1253, 1254, 1255, 1256, 1257, 1258, +}; + +static const uint16_t _CFEUCToCodePage[] = { // 0x900 + 51932, 51936, 51950, 51949, }; UInt32 CFStringConvertEncodingToWindowsCodepage(CFStringEncoding theEncoding) { - if (theEncoding == kCFStringEncodingUTF8) { - return 65001; - } else { - theEncoding &= 0xFFF; +#if defined(__MACH__) + CFStringEncoding encodingBase = theEncoding & 0x0FFF; +#endif + + switch (theEncoding & 0x0F00) { +#if defined(__MACH__) + case 0: // Mac OS script + if (encodingBase <= kCFStringEncodingMacCentralEurRoman) { + return MACCODEPAGE_BASE + encodingBase; + } else if (encodingBase == kCFStringEncodingMacTurkish) { + return 10081; + } else if (encodingBase == kCFStringEncodingMacCroatian) { + return 10082; + } else if (encodingBase == kCFStringEncodingMacIcelandic) { + return 10079; + } + break; +#endif + + case 0x100: // Unicode + switch (theEncoding) { + case kCFStringEncodingUTF8: return 65001; + case kCFStringEncodingUTF16: return 1200; + case kCFStringEncodingUTF16BE: return 1201; + case kCFStringEncodingUTF32: return 65005; + case kCFStringEncodingUTF32BE: return 65006; + } + break; + +#if defined(__MACH__) + case 0x0200: // ISO 8859 series + if (encodingBase <= kCFStringEncodingISOLatin10) return ISO8859CODEPAGE_BASE + (encodingBase - 0x200); + break; + + case 0x0400: // DOS codepage + if (encodingBase <= kCFStringEncodingDOSChineseTrad) return _CFToDOSCodePageList[encodingBase - 0x400]; + break; + + case 0x0500: // ANSI (Windows) codepage + if (encodingBase <= kCFStringEncodingWindowsVietnamese) return _CFToWindowsCodePageList[theEncoding - 0x500]; + else if (encodingBase == kCFStringEncodingWindowsKoreanJohab) return 1361; + break; + + case 0x600: // National standards + if (encodingBase == kCFStringEncodingASCII) return 20127; + else if (encodingBase == kCFStringEncodingGB_18030_2000) return 54936; + break; + + case 0x0800: // ISO 2022 series + switch (encodingBase) { + case kCFStringEncodingISO_2022_JP: return 50220; + case kCFStringEncodingISO_2022_CN: return 50227; + case kCFStringEncodingISO_2022_KR: return 50225; + } + break; + + case 0x0900: // EUC series + if (encodingBase <= kCFStringEncodingEUC_KR) return _CFEUCToCodePage[encodingBase - 0x0900]; + break; + + + case 0x0A00: // Misc encodings + switch (encodingBase) { + case kCFStringEncodingKOI8_R: return 20866; + case kCFStringEncodingHZ_GB_2312: return 52936; + case kCFStringEncodingKOI8_U: return 21866; + } + break; + + case 0x0C00: // IBM EBCDIC encodings + if (encodingBase == kCFStringEncodingEBCDIC_CP037) return 37; + break; +#endif // __MACH__ } + return kCFStringEncodingInvalidId; } +#if defined(__MACH__) 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}, + {37, kCFStringEncodingEBCDIC_CP037}, + {437, kCFStringEncodingDOSLatinUS}, + {737, kCFStringEncodingDOSGreek}, + {775, kCFStringEncodingDOSBalticRim}, + {850, kCFStringEncodingDOSLatin1}, + {851, kCFStringEncodingDOSGreek1}, + {852, kCFStringEncodingDOSLatin2}, + {855, kCFStringEncodingDOSCyrillic}, + {857, kCFStringEncodingDOSTurkish}, + {860, kCFStringEncodingDOSPortuguese}, + {861, kCFStringEncodingDOSIcelandic}, + {862, kCFStringEncodingDOSHebrew}, + {863, kCFStringEncodingDOSCanadianFrench}, + {864, kCFStringEncodingDOSArabic}, + {865, kCFStringEncodingDOSNordic}, + {866, kCFStringEncodingDOSRussian}, + {869, kCFStringEncodingDOSGreek2}, + {874, kCFStringEncodingDOSThai}, + {932, kCFStringEncodingDOSJapanese}, + {936, kCFStringEncodingDOSChineseSimplif}, + {949, kCFStringEncodingDOSKorean}, + {950, kCFStringEncodingDOSChineseTrad}, + {1250, kCFStringEncodingWindowsLatin2}, + {1251, kCFStringEncodingWindowsCyrillic}, + {1252, kCFStringEncodingWindowsLatin1}, + {1253, kCFStringEncodingWindowsGreek}, + {1254, kCFStringEncodingWindowsLatin5}, + {1255, kCFStringEncodingWindowsHebrew}, + {1256, kCFStringEncodingWindowsArabic}, + {1257, kCFStringEncodingWindowsBalticRim}, + {1258, kCFStringEncodingWindowsVietnamese}, + {1361, kCFStringEncodingWindowsKoreanJohab}, + {20127, kCFStringEncodingASCII}, + {20866, kCFStringEncodingKOI8_R}, + {21866, kCFStringEncodingKOI8_U}, + {50220, kCFStringEncodingISO_2022_JP}, + {50225, kCFStringEncodingISO_2022_KR}, + {50227, kCFStringEncodingISO_2022_CN}, + {51932, kCFStringEncodingEUC_JP}, + {51936, kCFStringEncodingEUC_CN}, + {51949, kCFStringEncodingEUC_KR}, + {51950, kCFStringEncodingEUC_TW}, + {52936, kCFStringEncodingHZ_GB_2312}, + {54936, kCFStringEncodingGB_18030_2000}, }; -static SInt32 bsearchEncoding(unsigned short target) { +static SInt32 bsearchEncoding(uint16_t target) { const unsigned int *start, *end, *divider; unsigned int size = sizeof(_CFACPToCFTable) / sizeof(UInt32); @@ -305,30 +433,44 @@ static SInt32 bsearchEncoding(unsigned short target) { 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); + if (*(const uint16_t*)divider == target) return *((const uint16_t*)divider + 1); + else if (*(const uint16_t*)divider > target) end = divider - 1; + else if (*(const uint16_t*)(divider + 1) > target) return *((const uint16_t*)divider + 1); else start = divider + 1; } return (kCFStringEncodingInvalidId); } +#endif 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 + } else if ((theEncoding >= MACCODEPAGE_BASE) && (theEncoding < 20000)) { // Mac script + if (theEncoding <= 10029) return theEncoding - MACCODEPAGE_BASE; // up to Mac Central European +#if defined(__MACH__) + else if (theEncoding == 10079) return kCFStringEncodingMacIcelandic; + else if (theEncoding == 10081) return kCFStringEncodingMacTurkish; + else if (theEncoding == 10082) return kCFStringEncodingMacCroatian; +#endif + } else if ((theEncoding >= ISO8859CODEPAGE_BASE) && (theEncoding <= 28605)) { // ISO 8859 return (theEncoding - ISO8859CODEPAGE_BASE) + 0x200; + } else if (theEncoding == 65001) { // UTF-8 + return kCFStringEncodingUTF8; + } else if (theEncoding == 12000) { // UTF-16 + return kCFStringEncodingUTF16; + } else if (theEncoding == 12001) { // UTF-16BE + return kCFStringEncodingUTF16BE; + } else if (theEncoding == 65005) { // UTF-32 + return kCFStringEncodingUTF32; + } else if (theEncoding == 65006) { // UTF-32BE + return kCFStringEncodingUTF32BE; } else { - switch (theEncoding) { - case 65001: return kCFStringEncodingUTF8; - case 20127: return kCFStringEncodingASCII; - default: return kCFStringEncodingInvalidId; - } +#if defined(__MACH__) + return bsearchEncoding(theEncoding); +#endif } + + return kCFStringEncodingInvalidId; } CFStringEncoding CFStringGetMostCompatibleMacStringEncoding(CFStringEncoding encoding) { @@ -339,3 +481,4 @@ CFStringEncoding CFStringGetMostCompatibleMacStringEncoding(CFStringEncoding enc return macEncoding; } + diff --git a/StringEncodings.subproj/CFBuiltinConverters.c b/StringEncodings.subproj/CFBuiltinConverters.c index 2c9c947..3e6459a 100644 --- a/StringEncodings.subproj/CFBuiltinConverters.c +++ b/StringEncodings.subproj/CFBuiltinConverters.c @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -100,7 +98,7 @@ static Boolean __CFFromASCII(UInt32 flags, uint8_t byte, UniChar *character) { } -__private_extern__ CFStringEncodingConverter __CFConverterASCII = { +__private_extern__ const CFStringEncodingConverter __CFConverterASCII = { __CFToASCII, __CFFromASCII, 1, 1, kCFStringEncodingConverterCheapEightBit, NULL, NULL, NULL, NULL, NULL, NULL, }; @@ -136,7 +134,7 @@ static UInt32 __CFToISOLatin1Precompose(UInt32 flags, const UniChar *character, } } -__private_extern__ CFStringEncodingConverter __CFConverterISOLatin1 = { +__private_extern__ const CFStringEncodingConverter __CFConverterISOLatin1 = { __CFToISOLatin1, __CFFromISOLatin1, 1, 1, kCFStringEncodingConverterCheapEightBit, NULL, NULL, NULL, NULL, __CFToISOLatin1Precompose, CFStringEncodingIsValidCombiningCharacterForLatin1, }; @@ -433,7 +431,7 @@ static UInt32 __CFToMacRomanPrecompose(UInt32 flags, const UniChar *character, U } } -__private_extern__ CFStringEncodingConverter __CFConverterMacRoman = { +__private_extern__ const CFStringEncodingConverter __CFConverterMacRoman = { __CFToMacRoman, __CFFromMacRoman, 1, 1, kCFStringEncodingConverterCheapEightBit, NULL, NULL, NULL, NULL, __CFToMacRomanPrecompose, CFStringEncodingIsValidCombiningCharacterForLatin1, }; @@ -478,7 +476,7 @@ static Boolean __CFToWinLatin1(UInt32 flags, UniChar character, uint8_t *byte) { return CFStringEncodingUnicodeTo8BitEncoding(cp1252_from_uni, NUM_1252_FROM_UNI, character, byte); } -static const unsigned short cp1252_to_uni[32] = { +static const uint16_t cp1252_to_uni[32] = { 0x20AC, // EURO SIGN 0xFFFD, // NOT USED 0x201A, // SINGLE LOW-9 QUOTATION MARK @@ -531,7 +529,7 @@ static UInt32 __CFToWinLatin1Precompose(UInt32 flags, const UniChar *character, } } -__private_extern__ CFStringEncodingConverter __CFConverterWinLatin1 = { +__private_extern__ const CFStringEncodingConverter __CFConverterWinLatin1 = { __CFToWinLatin1, __CFFromWinLatin1, 1, 1, kCFStringEncodingConverterCheapEightBit, NULL, NULL, NULL, NULL, __CFToWinLatin1Precompose, CFStringEncodingIsValidCombiningCharacterForLatin1, }; @@ -829,7 +827,7 @@ static UInt32 __CFToNextStepLatinPrecompose(UInt32 flags, const UniChar *charact } } -__private_extern__ CFStringEncodingConverter __CFConverterNextStepLatin = { +__private_extern__ const CFStringEncodingConverter __CFConverterNextStepLatin = { __CFToNextStepLatin, __CFFromNextStepLatin, 1, 1, kCFStringEncodingConverterCheapEightBit, NULL, NULL, NULL, NULL, __CFToNextStepLatinPrecompose, CFStringEncodingIsValidCombiningCharacterForLatin1, }; @@ -966,7 +964,7 @@ static UInt32 __CFToUTF8(UInt32 flags, const UniChar *characters, UInt32 numChar } if (!(bytesWritten = (maxByteLen ? __CFToUTF8Core(ch, bytes, endBytes - bytes) : __CFUTF8BytesToWriteForCharacter(ch)))) { - --characters; + characters -= (ch < 0x10000 ? 1 : 2); break; } bytes += bytesWritten; @@ -989,25 +987,147 @@ static UInt32 __CFToUTF8(UInt32 flags, const UniChar *characters, UInt32 numChar */ 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; + if (length > 4) return false; + + const uint8_t *srcptr = source+length; + uint8_t head = *source; + + while (--srcptr > source) if ((*srcptr & 0xC0) != 0x80) return false; + + if (((head >= 0x80) && (head < 0xC2)) || (head > 0xF4)) return false; + + if (((head == 0xE0) && (*(source + 1) < 0xA0)) || ((head == 0xED) && (*(source + 1) > 0x9F)) || ((head == 0xF0) && (*(source + 1) < 0x90)) || ((head == 0xF4) && (*(source + 1) > 0x8F))) return false; + return true; +} + +/* This version of the routine returns the length of the sequence, + or 0 on illegal sequence. This version is correct according to + the Unicode 4.0 spec. */ +#define ISLEGALUTF8_FAST 0 +static CFIndex __CFIsLegalUTF8_2(const uint8_t *source, CFIndex maxBytes) { + if (maxBytes < 1) return 0; + uint8_t first = source[0]; + if (first <= 0x7F) return 1; + if (first < 0xC2) return 0; + if (maxBytes < 2) return 0; + if (first <= 0xDF) { +#if ISLEGALUTF8_FAST + if ((source[1] & 0xC0) == 0x80) return 2; +#else + if (source[1] < 0x80) return 0; + if (source[1] <= 0xBF) return 2; +#endif + return 0; + } + if (maxBytes < 3) return 0; +#if ISLEGALUTF8_FAST + if (first <= 0xEF) { + uint32_t value = (first << 24) | ((*(const uint16_t *)((const uint8_t *)source + 1)) << 8); + uint32_t masked1 = (value & 0xFFF0C000); + + // 0b 11100000 101{0,1}xxxx 10xxxxxx (0xE0) + if (masked1 == 0xE0A08000) return 3; + if (masked1 == 0xE0B08000) return 3; + + // 0b 11101101 100{0,1}xxxx 10xxxxxx (0xED) + if (masked1 == 0xED808000) return 3; + if (masked1 == 0xED908000) return 3; + + // 0b 1110{0001 - 1100} 10xxxxxx 10xxxxxx (0xE1 - 0xEC) + // 0b 1110{1110 - 1111} 10xxxxxx 10xxxxxx (0xEE - 0xEF) + if ((value & 0x00C0C000) == 0x00808000) return 3; + + return 0; + } +#else + if (first == 0xE0) { + if (source[1] < 0xA0 /* NOTE */) return 0; + if (source[1] <= 0xBF) { + if (source[2] < 0x80) return 0; + if (source[2] <= 0xBF) return 3; + } + return 0; + } + if (first <= 0xEC) { + if (source[1] < 0x80) return 0; + if (source[1] <= 0xBF) { + if (source[2] < 0x80) return 0; + if (source[2] <= 0xBF) return 3; } - return true; + return 0; + } + if (first == 0xED) { + if (source[1] < 0x80) return 0; + if (source[1] <= 0x9F /* NOTE */) { + if (source[2] < 0x80) return 0; + if (source[2] <= 0xBF) return 3; + } + return 0; + } + if (first <= 0xEF) { + if (source[1] < 0x80) return 0; + if (source[1] <= 0xBF) { + if (source[2] < 0x80) return 0; + if (source[2] <= 0xBF) return 3; + } + return 0; + } +#endif + if (maxBytes < 4) return 0; +#if ISLEGALUTF8_FAST + if (first <= 0xF4) { + uint32_t value = *(const uint32_t *)source; + uint32_t masked1 = (value & 0xFFF0C0C0); + + // 0b 11110000 10{01,10,11}xxxx 10xxxxxx 10xxxxxx (0xF0) + if (masked1 == 0xF0908080) return 4; + if (masked1 == 0xF0A08080) return 4; + if (masked1 == 0xF0B08080) return 4; + + // 0b 11110100 1000xxxx 10xxxxxx 10xxxxxx (0xF4) + if (masked1 == 0xF4808080) return 4; + + // 0b 111100{01,10,11} 10xxxxxx 10xxxxxx 10xxxxxx (0xF1 - 0xF3) + if ((value & 0x00C0C0C0) == 0x00808080) return 4; + + return 0; + } +#else + if (first == 0xF0) { + if (source[1] < 0x90 /* NOTE */) return 0; + if (source[1] <= 0xBF) { + if (source[2] < 0x80) return 0; + if (source[2] <= 0xBF) { + if (source[3] < 0x80) return 0; + if (source[3] <= 0xBF) return 4; + } + } + return 0; + } + if (first <= 0xF3) { + if (source[1] < 0x80) return 0; + if (source[1] <= 0xBF) { + if (source[2] < 0x80) return 0; + if (source[2] <= 0xBF) { + if (source[3] < 0x80) return 0; + if (source[3] <= 0xBF) return 4; + } + } + return 0; + } + if (first == 0xF4) { + if (source[1] < 0x80) return 0; + if (source[1] <= 0x8F /* NOTE */) { + if (source[2] < 0x80) return 0; + if (source[2] <= 0xBF) { + if (source[3] < 0x80) return 0; + if (source[3] <= 0xBF) return 4; + } + } + return 0; + } +#endif + return 0; } static UInt32 __CFFromUTF8(UInt32 flags, const uint8_t *bytes, UInt32 numBytes, UniChar *characters, UInt32 maxCharLen, UInt32 *usedCharLen) { @@ -1031,7 +1151,7 @@ static UInt32 __CFFromUTF8(UInt32 flags, const uint8_t *bytes, UInt32 numBytes, /* 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 ((extraBytesToRead > 3) || (strictUTF8 && !__CFIsLegalUTF8(source, extraBytesToRead + 1))) { if ((*source == 0xA9) || (flags & kCFStringEncodingAllowLossyConversion)) { numBytes += extraBytesToRead; ++source; @@ -1142,7 +1262,7 @@ static UInt32 __CFFromUTF8Len(UInt32 flags, const uint8_t *source, UInt32 numByt /* 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 ((extraBytesToRead > 3) || (strictUTF8 && !__CFIsLegalUTF8(source, extraBytesToRead + 1))) { if ((*source == 0xA9) || (flags & kCFStringEncodingAllowLossyConversion)) { numBytes += extraBytesToRead; ++source; @@ -1191,7 +1311,7 @@ static UInt32 __CFFromUTF8Len(UInt32 flags, const uint8_t *source, UInt32 numByt return theUsedCharLen; } -__private_extern__ CFStringEncodingConverter __CFConverterUTF8 = { - __CFToUTF8, __CFFromUTF8, 6, 2, kCFStringEncodingConverterStandard, +__private_extern__ const CFStringEncodingConverter __CFConverterUTF8 = { + __CFToUTF8, __CFFromUTF8, 3, 2, kCFStringEncodingConverterStandard, __CFToUTF8Len, __CFFromUTF8Len, NULL, NULL, NULL, NULL, }; diff --git a/StringEncodings.subproj/CFStringEncodingConverter.c b/StringEncodings.subproj/CFStringEncodingConverter.c index a380f76..00a41cd 100644 --- a/StringEncodings.subproj/CFStringEncodingConverter.c +++ b/StringEncodings.subproj/CFStringEncodingConverter.c @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -31,7 +29,7 @@ #include #include #include "CFUniChar.h" -#include "CFUtilities.h" +#include "CFUtilitiesPriv.h" #include "CFUnicodeDecomposition.h" #include "CFStringEncodingConverterExt.h" #include "CFStringEncodingConverterPriv.h" @@ -564,7 +562,9 @@ CF_INLINE _CFConverterEntry *__CFStringEncodingConverterGetEntry(UInt32 encoding case kCFStringEncodingUTF8: return &__CFConverterEntryUTF8; - default: return NULL; + default: { + return NULL; + } } } @@ -914,7 +914,7 @@ __private_extern__ const char **CFStringEncodingCanonicalCharsetNames(UInt32 enc __private_extern__ UInt32 CFStringEncodingGetScriptCodeForEncoding(CFStringEncoding encoding) { _CFConverterEntry *entry = __CFStringEncodingConverterGetEntry(encoding); - return (entry ? entry->scriptCode : (encoding == kCFStringEncodingUnicode ? kCFStringEncodingUnicode : (encoding < 0xFF ? encoding : kCFStringEncodingInvalidId))); + return (entry ? entry->scriptCode : ((encoding & 0x0FFF) == kCFStringEncodingUnicode ? kCFStringEncodingUnicode : (encoding < 0xFF ? encoding : kCFStringEncodingInvalidId))); } __private_extern__ UInt32 CFStringEncodingCharLengthForBytes(UInt32 encoding, UInt32 flags, const uint8_t *bytes, UInt32 numBytes) { @@ -967,9 +967,17 @@ static const UInt32 __CFBuiltinEncodings[] = { kCFStringEncodingNextStepLatin, kCFStringEncodingASCII, kCFStringEncodingUTF8, - /* These two are available only in CFString-level */ - kCFStringEncodingUnicode, + /* These seven are available only in CFString-level */ kCFStringEncodingNonLossyASCII, + + kCFStringEncodingUTF16, + kCFStringEncodingUTF16BE, + kCFStringEncodingUTF16LE, + + kCFStringEncodingUTF32, + kCFStringEncodingUTF32BE, + kCFStringEncodingUTF32LE, + kCFStringEncodingInvalidId, }; diff --git a/StringEncodings.subproj/CFStringEncodingConverter.h b/StringEncodings.subproj/CFStringEncodingConverter.h index e5a3b5b..9513570 100644 --- a/StringEncodings.subproj/CFStringEncodingConverter.h +++ b/StringEncodings.subproj/CFStringEncodingConverter.h @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -23,7 +21,7 @@ * @APPLE_LICENSE_HEADER_END@ */ /* CFStringEncodingConverter.h - Copyright (c) 1998-2003, Apple, Inc. All rights reserved. + Copyright (c) 1998-2005, Apple, Inc. All rights reserved. */ #ifndef __CFSTRINGENCODINGCONVERTER__ diff --git a/StringEncodings.subproj/CFStringEncodingConverterExt.h b/StringEncodings.subproj/CFStringEncodingConverterExt.h index 4db9b6a..6f6a18f 100644 --- a/StringEncodings.subproj/CFStringEncodingConverterExt.h +++ b/StringEncodings.subproj/CFStringEncodingConverterExt.h @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -23,13 +21,13 @@ * @APPLE_LICENSE_HEADER_END@ */ /* CFStringEncodingConverterExt.h - Copyright (c) 1998-2003, Apple, Inc. All rights reserved. + Copyright (c) 1998-2005, Apple, Inc. All rights reserved. */ #ifndef __CFSTRINGENCODINGCONVERETEREXT_H__ #define __CFSTRINGENCODINGCONVERETEREXT_H__ 1 -#include "CFStringEncodingConverter.h" +#include #if defined(__cplusplus) extern "C" { diff --git a/StringEncodings.subproj/CFStringEncodingConverterPriv.h b/StringEncodings.subproj/CFStringEncodingConverterPriv.h index 7c956e3..4817a8b 100644 --- a/StringEncodings.subproj/CFStringEncodingConverterPriv.h +++ b/StringEncodings.subproj/CFStringEncodingConverterPriv.h @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -23,14 +21,14 @@ * @APPLE_LICENSE_HEADER_END@ */ /* CFStringEncodingConverterPriv.h - Copyright (c) 1998-2003, Apple, Inc. All rights reserved. + Copyright (c) 1998-2005, Apple, Inc. All rights reserved. */ #if !defined(__COREFOUNDATION_CFSTRINGENCODINGCONVERTERPRIV__) #define __COREFOUNDATION_CFSTRINGENCODINGCONVERTERPRIV__ 1 #include -#include "CFStringEncodingConverterExt.h" +#include #define MAX_IANA_ALIASES (4) @@ -65,12 +63,12 @@ typedef struct { UInt32 scriptCode; } _CFConverterEntry; -extern CFStringEncodingConverter __CFConverterASCII; -extern CFStringEncodingConverter __CFConverterISOLatin1; -extern CFStringEncodingConverter __CFConverterMacRoman; -extern CFStringEncodingConverter __CFConverterWinLatin1; -extern CFStringEncodingConverter __CFConverterNextStepLatin; -extern CFStringEncodingConverter __CFConverterUTF8; +extern const CFStringEncodingConverter __CFConverterASCII; +extern const CFStringEncodingConverter __CFConverterISOLatin1; +extern const CFStringEncodingConverter __CFConverterMacRoman; +extern const CFStringEncodingConverter __CFConverterWinLatin1; +extern const CFStringEncodingConverter __CFConverterNextStepLatin; +extern const CFStringEncodingConverter __CFConverterUTF8; #endif /* ! __COREFOUNDATION_CFSTRINGENCODINGCONVERTERPRIV__ */ diff --git a/StringEncodings.subproj/CFUniChar.c b/StringEncodings.subproj/CFUniChar.c index b092590..35de0a7 100644 --- a/StringEncodings.subproj/CFUniChar.c +++ b/StringEncodings.subproj/CFUniChar.c @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -63,8 +61,19 @@ #if !defined(__MACOS8__) CF_INLINE void __CFUniCharCharacterSetPath(char *cpath) { +#if defined(__MACH__) + strlcpy(cpath, __kCFCharacterSetDir, MAXPATHLEN); +#elif defined(__WIN32__) + strlcpy(cpath, _CFDLLPath(), MAXPATHLEN); +#else strlcpy(cpath, __kCFCharacterSetDir, MAXPATHLEN); +#endif + +#if defined(__WIN32__) + strlcat(cpath, "\\CharacterSets\\", MAXPATHLEN); +#else strlcat(cpath, "/CharacterSets/", MAXPATHLEN); +#endif } static bool __CFUniCharLoadBytesFromFile(const char *fileName, const void **bytes) { @@ -87,18 +96,10 @@ static bool __CFUniCharLoadBytesFromFile(const char *fileName, const void **byte 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; @@ -300,7 +301,7 @@ static bool __CFUniCharLoadBitmapData(void) { 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]._planes = (const uint8_t **)CFAllocatorAllocate(NULL, sizeof(const void *) * numPlanes, 0); __CFUniCharBitmapDataArray[idx]._numPlanes = numPlanes; currentPlane = 0; @@ -745,11 +746,11 @@ caseFoldRetry: case TURKISH_LANG_CODE: case AZERI_LANG_CODE: - if (theChar == 0x0049) { // LATIN CAPITAL LETTER I - *convertedChar = (ctype == kCFUniCharToLowercase ? ((kCFUniCharCaseMapMoreAbove & flags) ? 0x0069 : 0x0131) : 0x0049); + if ((theChar == 0x0049) || (theChar == 0x0131)) { // LATIN CAPITAL LETTER I & LATIN SMALL LETTER DOTLESS I + *convertedChar = (((ctype == kCFUniCharToLowercase) || (ctype == kCFUniCharCaseFold)) ? ((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); + *convertedChar = (((ctype == kCFUniCharToLowercase) || (ctype == kCFUniCharCaseFold)) ? 0x0069 : 0x0130); return 1; } else if (theChar == 0x0307 && (kCFUniCharCaseMapAfter_i & flags)) { // COMBINING DOT ABOVE AFTER_i if (ctype == kCFUniCharToLowercase) { @@ -825,8 +826,15 @@ caseFoldRetry: goto caseFoldRetry; } - *convertedChar = theChar; - return 1; + if (theChar > 0xFFFF) { // non-BMP + theChar = (theChar & 0xFFFFFF) - 0x10000; + *(convertedChar++) = (theChar >> 10) + 0xD800UL; + *(convertedChar++) = (theChar & 0x3FF) + 0xDC00UL; + return 2; + } else { + *convertedChar = theChar; + return 1; + } } UInt32 CFUniCharMapTo(UniChar theChar, UniChar *convertedChar, UInt32 maxLength, uint16_t ctype, UInt32 flags) { @@ -971,6 +979,7 @@ __private_extern__ uint32_t CFUniCharGetConditionalCaseMappingFlags(UTF32Char th // Unicode property database static __CFUniCharBitmapData *__CFUniCharUnicodePropertyTable = NULL; +static int __CFUniCharUnicodePropertyTableCount = 0; static CFSpinLock_t __CFUniCharPropTableLock = 0; @@ -1000,6 +1009,7 @@ const void *CFUniCharGetUnicodePropertyDataForPlane(uint32_t propertyType, uint3 bodyBase = (char *)bytes + headerSize; count = headerSize / sizeof(uint32_t); + __CFUniCharUnicodePropertyTableCount = count; __CFUniCharUnicodePropertyTable = (__CFUniCharBitmapData *)CFAllocatorAllocate(NULL, sizeof(__CFUniCharBitmapData) * count, 0); @@ -1159,3 +1169,62 @@ bool CFUniCharFillDestinationBuffer(const UTF32Char *src, uint32_t srcLength, vo return true; } + +#if defined(__WIN32__) +void __CFUniCharCleanup(void) +{ + int idx; + + // cleanup memory allocated by __CFUniCharLoadBitmapData() + __CFSpinLock(&__CFUniCharBitmapLock); + + if (__CFUniCharBitmapDataArray != NULL) { + for (idx = 0; idx < __CFUniCharNumberOfBitmaps; idx++) { + CFAllocatorDeallocate(NULL, __CFUniCharBitmapDataArray[idx]._planes); + __CFUniCharBitmapDataArray[idx]._planes = NULL; + } + + CFAllocatorDeallocate(NULL, __CFUniCharBitmapDataArray); + __CFUniCharBitmapDataArray = NULL; + __CFUniCharNumberOfBitmaps = 0; + } + + __CFSpinUnlock(&__CFUniCharBitmapLock); + + // cleanup memory allocated by CFUniCharGetMappingData() + __CFSpinLock(&__CFUniCharMappingTableLock); + + if (__CFUniCharMappingTables != NULL) { + CFAllocatorDeallocate(NULL, __CFUniCharMappingTables); + __CFUniCharMappingTables = NULL; + } + + // cleanup memory allocated by __CFUniCharLoadCaseMappingTable() + if (__CFUniCharCaseMappingTableCounts != NULL) { + CFAllocatorDeallocate(NULL, __CFUniCharCaseMappingTableCounts); + __CFUniCharCaseMappingTableCounts = NULL; + + __CFUniCharCaseMappingTable = NULL; + __CFUniCharCaseMappingExtraTable = NULL; + } + + __CFSpinUnlock(&__CFUniCharMappingTableLock); + + // cleanup memory allocated by CFUniCharGetUnicodePropertyDataForPlane() + __CFSpinLock(&__CFUniCharPropTableLock); + + if (__CFUniCharUnicodePropertyTable != NULL) { + for (idx = 0; idx < __CFUniCharUnicodePropertyTableCount; idx++) { + CFAllocatorDeallocate(NULL, __CFUniCharUnicodePropertyTable[idx]._planes); + __CFUniCharUnicodePropertyTable[idx]._planes = NULL; + } + + CFAllocatorDeallocate(NULL, __CFUniCharUnicodePropertyTable); + __CFUniCharUnicodePropertyTable = NULL; + __CFUniCharUnicodePropertyTableCount = 0; + } + + __CFSpinUnlock(&__CFUniCharPropTableLock); +} +#endif // __WIN32__ + diff --git a/StringEncodings.subproj/CFUniChar.h b/StringEncodings.subproj/CFUniChar.h index 508ff54..87cedfd 100644 --- a/StringEncodings.subproj/CFUniChar.h +++ b/StringEncodings.subproj/CFUniChar.h @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -23,12 +21,14 @@ * @APPLE_LICENSE_HEADER_END@ */ /* CFUniChar.h - Copyright (c) 1998-2003, Apple, Inc. All rights reserved. + Copyright (c) 1998-2005, Apple, Inc. All rights reserved. */ #if !defined(__COREFOUNDATION_CFUNICHAR__) #define __COREFOUNDATION_CFUNICHAR__ 1 + +#include #include #if defined(__cplusplus) @@ -161,9 +161,11 @@ CF_INLINE uint8_t CFUniCharGetBidiPropertyForCharacter(UTF16Char character, cons if (bitmap) { uint8_t value = bitmap[(character >> 8)]; - if (value != kCFUniCharBiDiPropertyL) { + if (value > kCFUniCharBiDiPropertyPDF) { bitmap = bitmap + 256 + ((value - kCFUniCharBiDiPropertyPDF - 1) * 256); return bitmap[character % 256]; + } else { + return value; } } return kCFUniCharBiDiPropertyL; @@ -187,6 +189,77 @@ CF_EXPORT uint32_t CFUniCharGetUnicodeProperty(UTF32Char character, uint32_t pro CF_EXPORT bool CFUniCharFillDestinationBuffer(const UTF32Char *src, uint32_t srcLength, void **dst, uint32_t dstLength, uint32_t *filledLength, uint32_t dstFormat); +// UTF32 support + +CF_INLINE bool CFUniCharToUTF32(const UTF16Char *src, CFIndex length, UTF32Char *dst, bool allowLossy, bool isBigEndien) { + const UTF16Char *limit = src + length; + UTF32Char character; + + while (src < limit) { + character = *(src++); + + if (CFUniCharIsSurrogateHighCharacter(character)) { + if ((src < limit) && CFUniCharIsSurrogateLowCharacter(*src)) { + character = CFUniCharGetLongCharacterForSurrogatePair(character, *(src++)); + } else { + if (!allowLossy) return false; + character = 0xFFFD; // replacement character + } + } else if (CFUniCharIsSurrogateLowCharacter(character)) { + if (!allowLossy) return false; + character = 0xFFFD; // replacement character + } + + *(dst++) = (isBigEndien ? CFSwapInt32HostToBig(character) : CFSwapInt32HostToLittle(character)); + } + + return true; +} + +CF_INLINE bool CFUniCharFromUTF32(const UTF32Char *src, CFIndex length, UTF16Char *dst, bool allowLossy, bool isBigEndien) { + const UTF32Char *limit = src + length; + UTF32Char character; + + while (src < limit) { + character = (isBigEndien ? CFSwapInt32BigToHost(*(src++)) : CFSwapInt32LittleToHost(*(src++))); + + if (character < 0xFFFF) { // BMP + if (allowLossy) { + if (CFUniCharIsSurrogateHighCharacter(character)) { + UTF32Char otherCharacter = 0xFFFD; // replacement character + + if (src < limit) { + otherCharacter = (isBigEndien ? CFSwapInt32BigToHost(*src) : CFSwapInt32LittleToHost(*src)); + + + if ((otherCharacter < 0x10000) && CFUniCharIsSurrogateLowCharacter(otherCharacter)) { + *(dst++) = character; ++src; + } else { + otherCharacter = 0xFFFD; // replacement character + } + } + + character = otherCharacter; + } else if (CFUniCharIsSurrogateLowCharacter(character)) { + character = 0xFFFD; // replacement character + } + } else { + if (CFUniCharIsSurrogateHighCharacter(character) || CFUniCharIsSurrogateLowCharacter(character)) return false; + } + } else if (character < 0x110000) { // non-BMP + character -= 0x10000; + *(dst++) = (UTF16Char)((character >> 10) + 0xD800UL); + character = (UTF16Char)((character & 0x3FF) + 0xDC00UL); + } else { + if (!allowLossy) return false; + character = 0xFFFD; // replacement character + } + + *(dst++) = character; + } + return true; +} + #if defined(__cplusplus) } #endif diff --git a/StringEncodings.subproj/CFUniCharPriv.h b/StringEncodings.subproj/CFUniCharPriv.h index 4446e64..cb243b6 100644 --- a/StringEncodings.subproj/CFUniCharPriv.h +++ b/StringEncodings.subproj/CFUniCharPriv.h @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -23,14 +21,14 @@ * @APPLE_LICENSE_HEADER_END@ */ /* CFUniCharPriv.h - Copyright (c) 1998-2003, Apple, Inc. All rights reserved. + Copyright (c) 1998-2005, Apple, Inc. All rights reserved. */ #if !defined __UCHAR_PRIVATE__ #define __UCHAR_PRIVATE__ 1 #include -#include "CFUniChar.h" +#include #define kCFUniCharRecursiveDecompositionFlag (1 << 30) #define kCFUniCharNonBmpFlag (1 << 31) diff --git a/StringEncodings.subproj/CFUnicodeDecomposition.c b/StringEncodings.subproj/CFUnicodeDecomposition.c index 2aa5d07..8d6f315 100644 --- a/StringEncodings.subproj/CFUnicodeDecomposition.c +++ b/StringEncodings.subproj/CFUnicodeDecomposition.c @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -28,33 +26,14 @@ */ #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 +#include #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; @@ -127,7 +106,6 @@ CF_INLINE bool __CFUniCharIsDecomposableCharacterWithFlag(UTF32Char character, b CF_INLINE bool __CFUniCharIsNonBaseCharacter(UTF32Char character) { return CFUniCharIsMemberOfBitmap(character, (character < 0x10000 ? __CFUniCharNonBaseBitmapForBMP : CFUniCharGetBitmapPtrForPlane(kCFUniCharNonBaseCharacterSet, ((character >> 16) & 0xFF)))); } -#endif KERNEL typedef struct { uint32_t _key; @@ -151,11 +129,7 @@ static uint32_t __CFUniCharGetMappedValue(const __CFUniCharDecomposeMappings *th 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; @@ -163,7 +137,6 @@ static void __CFUniCharPrioritySort(UTF32Char *characters, uint32_t length) { bool changes = true; UTF32Char *end = characters + length; -#if !KERNEL if (NULL == __CFUniCharCombiningPriorityTable) { __CFSpinLock(&__CFUniCharDecompositionTableLock); if (NULL == __CFUniCharCombiningPriorityTable) { @@ -176,7 +149,6 @@ static void __CFUniCharPrioritySort(UTF32Char *characters, uint32_t length) { } __CFSpinUnlock(&__CFUniCharDecompositionTableLock); } -#endif !KERNEL if (length < 2) return; @@ -199,35 +171,11 @@ static uint32_t __CFUniCharRecursivelyDecomposeCharacter(UTF32Char character, UT 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); @@ -255,9 +203,7 @@ static uint32_t __CFUniCharRecursivelyDecomposeCharacter(UTF32Char character, UT #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; @@ -276,11 +222,6 @@ uint32_t CFUniCharDecomposeCharacter(UTF32Char character, UTF32Char *convertedCh } } -#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; @@ -293,9 +234,7 @@ bool CFUniCharDecompose(const UTF16Char *src, uint32_t length, uint32_t *consume bool isDecomp = false; bool isNonBase = false; -#if !KERNEL if (NULL == __CFUniCharDecompositionTable) __CFUniCharLoadDecompositionTable(); -#endif !KERNEL while (length > 0) { currentChar = *(src++); @@ -504,8 +443,6 @@ bool CFUniCharDecompose(const UTF16Char *src, uint32_t length, uint32_t *consume return true; } -#if !KERNEL - #define MAX_COMP_DECOMP_LEN (32) static uint32_t __CFUniCharRecursivelyCompatibilityDecomposeCharacter(UTF32Char character, UTF32Char *convertedChars) { @@ -578,4 +515,3 @@ __private_extern__ uint32_t CFUniCharCompatibilityDecompose(UTF32Char *converted 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 index 4f9659e..442a58e 100644 --- a/StringEncodings.subproj/CFUnicodeDecomposition.h +++ b/StringEncodings.subproj/CFUnicodeDecomposition.h @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -27,39 +25,27 @@ * CoreFoundation * * Created by aki on Wed Oct 03 2001. - * Copyright (c) 2001-2003, Apple Inc. All rights reserved. + * Copyright (c) 2001-2005, 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 */ +#include #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); diff --git a/StringEncodings.subproj/CFUnicodePrecomposition.c b/StringEncodings.subproj/CFUnicodePrecomposition.c index cea2844..c2cc29f 100644 --- a/StringEncodings.subproj/CFUnicodePrecomposition.c +++ b/StringEncodings.subproj/CFUnicodePrecomposition.c @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -27,66 +25,15 @@ 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; @@ -128,7 +75,6 @@ static void __CFUniCharLoadPrecompositionTable(void) { CF_INLINE bool __CFUniCharIsNonBaseCharacter(UTF32Char character) { return CFUniCharIsMemberOfBitmap(character, (character < 0x10000 ? __CFUniCharNonBaseBitmapForBMP_P : CFUniCharGetBitmapPtrForPlane(kCFUniCharNonBaseCharacterSet, ((character >> 16) & 0xFF)))); } -#endif KERNEL typedef struct { UTF16Char _key; @@ -174,28 +120,20 @@ static uint32_t __CFUniCharGetMappedValue_P(const __CFUniCharPrecomposeMappings 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); } @@ -227,9 +165,7 @@ bool CFUniCharPrecompose(const UTF16Char *characters, uint32_t length, uint32_t bool currentBaseIsBMP = true; bool isPrecomposed; -#if !KERNEL if (NULL == __CFUniCharPrecompSourceTable) __CFUniCharLoadPrecompositionTable(); -#endif !KERNEL while (length > 0) { currentChar = *(characters++); @@ -244,11 +180,7 @@ bool CFUniCharPrecompose(const UTF16Char *characters, uint32_t length, uint32_t 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) { diff --git a/StringEncodings.subproj/CFUnicodePrecomposition.h b/StringEncodings.subproj/CFUnicodePrecomposition.h index 2f469ca..4882dc4 100644 --- a/StringEncodings.subproj/CFUnicodePrecomposition.h +++ b/StringEncodings.subproj/CFUnicodePrecomposition.h @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -27,18 +25,14 @@ * CoreFoundation * * Created by aki on Wed Oct 03 2001. - * Copyright (c) 2001-2003, Apple Inc. All rights reserved. + * Copyright (c) 2001-2005, 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 */ +#include #if defined(__cplusplus) extern "C" { diff --git a/URL.subproj/CFURL.c b/URL.subproj/CFURL.c index 226023c..c178d89 100644 --- a/URL.subproj/CFURL.c +++ b/URL.subproj/CFURL.c @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -23,7 +21,7 @@ * @APPLE_LICENSE_HEADER_END@ */ /* CFURL.c - Copyright 1998-2002, Apple, Inc. All rights reserved. + Copyright 1998-2004, Apple, Inc. All rights reserved. Responsibility: Becky Willrich */ @@ -33,7 +31,7 @@ #include #include "CFInternal.h" #include "CFStringEncodingConverter.h" -#include "CFUtilities.h" +#include "CFUtilitiesPriv.h" #include #include #include @@ -42,6 +40,8 @@ #include #endif +#include +#include static CFArrayRef HFSPathToURLComponents(CFStringRef path, CFAllocatorRef alloc, Boolean isDir); static CFArrayRef WindowsPathToURLComponents(CFStringRef path, CFAllocatorRef alloc, Boolean isDir); @@ -50,200 +50,44 @@ static CFStringRef POSIXPathToURLPath(CFStringRef path, CFAllocatorRef alloc, Bo 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; -} +extern CFURLRef _CFURLCreateCurrentDirectoryURL(CFAllocatorRef allocator); -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; -} +DEFINE_WEAK_CARBONCORE_FUNC(void, DisposeHandle, (Handle A), (A)) +DEFINE_WEAK_CARBONCORE_FUNC(OSErr, FSNewAlias, (const FSRef *A, const FSRef *B, AliasHandle *C), (A, B, C), -3296) +DEFINE_WEAK_CARBONCORE_FUNC(OSErr, FSGetVolumeInfo, (FSVolumeRefNum A, ItemCount B, FSVolumeRefNum *C, FSVolumeInfoBitmap D, FSVolumeInfo *E, HFSUniStr255*F, FSRef *G), (A, B, C, D, E, F, G), -3296) +DEFINE_WEAK_CARBONCORE_FUNC(OSErr, FSGetCatalogInfo, (const FSRef *A, FSCatalogInfoBitmap B, FSCatalogInfo *C, HFSUniStr255 *D, FSSpec *E, FSRef *F), (A, B, C, D, E, F), -3296) +DEFINE_WEAK_CARBONCORE_FUNC(OSErr, FSMakeFSRefUnicode, (const FSRef *A, UniCharCount B, const UniChar *C, TextEncoding D, FSRef *E), (A, B, C, D, E), -3296) +DEFINE_WEAK_CARBONCORE_FUNC(OSStatus, FSPathMakeRef, (const uint8_t *A, FSRef *B, Boolean *C), (A, B, C), -3296) +DEFINE_WEAK_CARBONCORE_FUNC(OSStatus, FSRefMakePath, (const FSRef *A, uint8_t *B, UInt32 C), (A, B, C), -3296) +DEFINE_WEAK_CARBONCORE_FUNC(OSErr, FSpMakeFSRef, (const FSSpec *A, FSRef *B), (A, B), -3296) +DEFINE_WEAK_CARBONCORE_FUNC(Size, GetAliasSizeFromPtr, (AliasPtr A), (A), 0) +DEFINE_WEAK_CARBONCORE_FUNC(OSErr, _FSGetFSRefInformationFast, (const FSRef* A, SInt16 *B, UInt32 *C, UInt32 *D, Boolean *E, Boolean *F, HFSUniStr255 *G), (A, B, C, D, E, F, G), -3296) +DEFINE_WEAK_CARBONCORE_FUNC(CFStringRef, _FSCopyStrippedPathString, (CFAllocatorRef A, CFStringRef B), (A, B), (B ? (CFStringRef)CFRetain(B) : NULL)) -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; -} +DEFINE_WEAK_CARBONCORE_FUNC(OSErr, _FSGetVolumeByName, ( CFStringRef volumeNameRef, FSVolumeRefNum* vRefNumP), ( volumeNameRef, vRefNumP), -3296 ) -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 +#ifndef DEBUG_URL_MEMORY_USAGE #define DEBUG_URL_MEMORY_USAGE 0 +#endif + +#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; +static UInt32 numExtraDataAllocated = 0; +static UInt32 numURLsWithBaseURL = 0; +#endif /* The bit flags in myURL->_flags */ #define HAS_SCHEME (0x0001) @@ -255,6 +99,7 @@ static OSStatus __CF_FSRefMakePath_internal(const FSRef *A, uint8_t *B, UInt32 C #define HAS_PARAMETERS (0x0040) #define HAS_QUERY (0x0080) #define HAS_FRAGMENT (0x0100) +#define HAS_HTTP_SCHEME (0x0200) // Last free bit (0x200) in lower word goes here! #define IS_IPV6_ENCODED (0x0400) #define IS_OLD_UTF8_STYLE (0x0800) @@ -270,16 +115,16 @@ static OSStatus __CF_FSRefMakePath_internal(const FSRef *A, uint8_t *B, UInt32 C /* 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 SCHEME_DIFFERS (0x00400000) unused #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) +// #define PATH_DIFFERS (0x08000000) unused +// #define PARAMETERS_DIFFER (0x10000000) unused +// #define QUERY_DIFFERS (0x20000000) unused +// #define FRAGMENT_DIFfERS (0x40000000) unused // Number of bits to shift to get from HAS_FOO to FOO_DIFFERS flag #define BIT_SHIFT_FROM_COMPONENT_TO_DIFFERS_FLAG (22) @@ -295,17 +140,83 @@ static OSStatus __CF_FSRefMakePath_internal(const FSRef *A, uint8_t *B, UInt32 C #define PATH_DELIM_AS_STRING_FOR_TYPE(fsType) ((fsType) == kCFURLHFSPathStyle ? CFSTR(":") : (((fsType) == kCFURLWindowsPathStyle) ? CFSTR("\\") : CFSTR("/"))) +// In order to get the sizeof ( __CFURL ) < 32 bytes, move these items into a seperate structure which is +// only allocated when necessary. In my tests, it's almost never needed -- very rarely does a CFURL have +// either a sanitized string or a reserved pointer for URLHandle. +struct _CFURLAdditionalData { + void *_reserved; // Reserved for URLHandle's use. + CFMutableStringRef _sanitizedString; // 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 +}; + 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. + CFURLRef _base; 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 + struct _CFURLAdditionalData* extra; }; + +CF_INLINE void* _getReserved ( const struct __CFURL* url ) +{ + if ( url && url->extra ) + return url->extra->_reserved; + + return NULL; +} + +CF_INLINE CFMutableStringRef _getSanitizedString ( const struct __CFURL* url ) +{ + if ( url && url->extra ) + return url->extra->_sanitizedString; + + return NULL; +} + +static void _CFURLAllocateExtraDataspace( struct __CFURL* url ) +{ + if ( url && ! url->extra ) + { struct _CFURLAdditionalData* extra = (struct _CFURLAdditionalData*) CFAllocatorAllocate( CFGetAllocator( url), sizeof( struct _CFURLAdditionalData ), 0 ); + + extra->_reserved = _getReserved( url ); + extra->_sanitizedString = _getSanitizedString( url ); + + url->extra = extra; + + #if DEBUG_URL_MEMORY_USAGE + numExtraDataAllocated ++; + #endif + } +} + +CF_INLINE void _setReserved ( struct __CFURL* url, void* reserved ) +{ + if ( url ) + { + // Don't allocate extra space if we're just going to be storing NULL + if ( ! url->extra && reserved ) + _CFURLAllocateExtraDataspace( url ); + + if ( url->extra ) + url->extra->_reserved = reserved; + } +} + +CF_INLINE void _setSanitizedString ( struct __CFURL* url, CFMutableStringRef sanitizedString ) +{ + if ( url ) + { + // Don't allocate extra space if we're just going to be storing NULL + if ( ! url->extra && sanitizedString ) + _CFURLAllocateExtraDataspace( url ); + + if ( url->extra ) + url->extra->_sanitizedString = sanitizedString; + } +} + static void _convertToURLRepresentation(struct __CFURL *url); static CFURLRef _CFURLCopyAbsoluteFileURL(CFURLRef relativeURL); static CFStringRef _resolveFileSystemPaths(CFStringRef relativePath, CFStringRef basePath, Boolean baseIsDir, CFURLPathStyle fsType, CFAllocatorRef alloc); @@ -338,45 +249,134 @@ 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; +enum { + VALID = 1, + UNRESERVED = 2, + PATHVALID = 4, + SCHEME = 8, + HEXDIGIT = 16 +}; + +static const unsigned char sURLValidCharacters[] = { + /* ' ' 32 */ 0, + /* '!' 33 */ VALID | UNRESERVED | PATHVALID , + /* '"' 34 */ 0, + /* '#' 35 */ 0, + /* '$' 36 */ VALID | PATHVALID , + /* '%' 37 */ 0, + /* '&' 38 */ VALID | PATHVALID , + /* ''' 39 */ VALID | UNRESERVED | PATHVALID , + /* '(' 40 */ VALID | UNRESERVED | PATHVALID , + /* ')' 41 */ VALID | UNRESERVED | PATHVALID , + /* '*' 42 */ VALID | UNRESERVED | PATHVALID , + /* '+' 43 */ VALID | SCHEME | PATHVALID , + /* ',' 44 */ VALID | PATHVALID , + /* '-' 45 */ VALID | UNRESERVED | SCHEME | PATHVALID , + /* '.' 46 */ VALID | UNRESERVED | SCHEME | PATHVALID , + /* '/' 47 */ VALID | PATHVALID , + /* '0' 48 */ VALID | UNRESERVED | SCHEME | PATHVALID | HEXDIGIT , + /* '1' 49 */ VALID | UNRESERVED | SCHEME | PATHVALID | HEXDIGIT , + /* '2' 50 */ VALID | UNRESERVED | SCHEME | PATHVALID | HEXDIGIT , + /* '3' 51 */ VALID | UNRESERVED | SCHEME | PATHVALID | HEXDIGIT , + /* '4' 52 */ VALID | UNRESERVED | SCHEME | PATHVALID | HEXDIGIT , + /* '5' 53 */ VALID | UNRESERVED | SCHEME | PATHVALID | HEXDIGIT , + /* '6' 54 */ VALID | UNRESERVED | SCHEME | PATHVALID | HEXDIGIT , + /* '7' 55 */ VALID | UNRESERVED | SCHEME | PATHVALID | HEXDIGIT , + /* '8' 56 */ VALID | UNRESERVED | SCHEME | PATHVALID | HEXDIGIT , + /* '9' 57 */ VALID | UNRESERVED | SCHEME | PATHVALID | HEXDIGIT , + /* ':' 58 */ VALID , + /* ';' 59 */ VALID , + /* '<' 60 */ 0, + /* '=' 61 */ VALID | PATHVALID , + /* '>' 62 */ 0, + /* '?' 63 */ VALID , + /* '@' 64 */ VALID | PATHVALID , + /* 'A' 65 */ VALID | UNRESERVED | SCHEME | PATHVALID | HEXDIGIT , + /* 'B' 66 */ VALID | UNRESERVED | SCHEME | PATHVALID | HEXDIGIT , + /* 'C' 67 */ VALID | UNRESERVED | SCHEME | PATHVALID | HEXDIGIT , + /* 'D' 68 */ VALID | UNRESERVED | SCHEME | PATHVALID | HEXDIGIT , + /* 'E' 69 */ VALID | UNRESERVED | SCHEME | PATHVALID | HEXDIGIT , + /* 'F' 70 */ VALID | UNRESERVED | SCHEME | PATHVALID | HEXDIGIT , + /* 'G' 71 */ VALID | UNRESERVED | SCHEME | PATHVALID , + /* 'H' 72 */ VALID | UNRESERVED | SCHEME | PATHVALID , + /* 'I' 73 */ VALID | UNRESERVED | SCHEME | PATHVALID , + /* 'J' 74 */ VALID | UNRESERVED | SCHEME | PATHVALID , + /* 'K' 75 */ VALID | UNRESERVED | SCHEME | PATHVALID , + /* 'L' 76 */ VALID | UNRESERVED | SCHEME | PATHVALID , + /* 'M' 77 */ VALID | UNRESERVED | SCHEME | PATHVALID , + /* 'N' 78 */ VALID | UNRESERVED | SCHEME | PATHVALID , + /* 'O' 79 */ VALID | UNRESERVED | SCHEME | PATHVALID , + /* 'P' 80 */ VALID | UNRESERVED | SCHEME | PATHVALID , + /* 'Q' 81 */ VALID | UNRESERVED | SCHEME | PATHVALID , + /* 'R' 82 */ VALID | UNRESERVED | SCHEME | PATHVALID , + /* 'S' 83 */ VALID | UNRESERVED | SCHEME | PATHVALID , + /* 'T' 84 */ VALID | UNRESERVED | SCHEME | PATHVALID , + /* 'U' 85 */ VALID | UNRESERVED | SCHEME | PATHVALID , + /* 'V' 86 */ VALID | UNRESERVED | SCHEME | PATHVALID , + /* 'W' 87 */ VALID | UNRESERVED | SCHEME | PATHVALID , + /* 'X' 88 */ VALID | UNRESERVED | SCHEME | PATHVALID , + /* 'Y' 89 */ VALID | UNRESERVED | SCHEME | PATHVALID , + /* 'Z' 90 */ VALID | UNRESERVED | SCHEME | PATHVALID , + /* '[' 91 */ 0, + /* '\' 92 */ 0, + /* ']' 93 */ 0, + /* '^' 94 */ 0, + /* '_' 95 */ VALID | UNRESERVED | PATHVALID , + /* '`' 96 */ 0, + /* 'a' 97 */ VALID | UNRESERVED | SCHEME | PATHVALID | HEXDIGIT , + /* 'b' 98 */ VALID | UNRESERVED | SCHEME | PATHVALID | HEXDIGIT , + /* 'c' 99 */ VALID | UNRESERVED | SCHEME | PATHVALID | HEXDIGIT , + /* 'd' 100 */ VALID | UNRESERVED | SCHEME | PATHVALID | HEXDIGIT , + /* 'e' 101 */ VALID | UNRESERVED | SCHEME | PATHVALID | HEXDIGIT , + /* 'f' 102 */ VALID | UNRESERVED | SCHEME | PATHVALID | HEXDIGIT , + /* 'g' 103 */ VALID | UNRESERVED | SCHEME | PATHVALID , + /* 'h' 104 */ VALID | UNRESERVED | SCHEME | PATHVALID , + /* 'i' 105 */ VALID | UNRESERVED | SCHEME | PATHVALID , + /* 'j' 106 */ VALID | UNRESERVED | SCHEME | PATHVALID , + /* 'k' 107 */ VALID | UNRESERVED | SCHEME | PATHVALID , + /* 'l' 108 */ VALID | UNRESERVED | SCHEME | PATHVALID , + /* 'm' 109 */ VALID | UNRESERVED | SCHEME | PATHVALID , + /* 'n' 110 */ VALID | UNRESERVED | SCHEME | PATHVALID , + /* 'o' 111 */ VALID | UNRESERVED | SCHEME | PATHVALID , + /* 'p' 112 */ VALID | UNRESERVED | SCHEME | PATHVALID , + /* 'q' 113 */ VALID | UNRESERVED | SCHEME | PATHVALID , + /* 'r' 114 */ VALID | UNRESERVED | SCHEME | PATHVALID , + /* 's' 115 */ VALID | UNRESERVED | SCHEME | PATHVALID , + /* 't' 116 */ VALID | UNRESERVED | SCHEME | PATHVALID , + /* 'u' 117 */ VALID | UNRESERVED | SCHEME | PATHVALID , + /* 'v' 118 */ VALID | UNRESERVED | SCHEME | PATHVALID , + /* 'w' 119 */ VALID | UNRESERVED | SCHEME | PATHVALID , + /* 'x' 120 */ VALID | UNRESERVED | SCHEME | PATHVALID , + /* 'y' 121 */ VALID | UNRESERVED | SCHEME | PATHVALID , + /* 'z' 122 */ VALID | UNRESERVED | SCHEME | PATHVALID , + /* '{' 123 */ 0, + /* '|' 124 */ 0, + /* '}' 125 */ 0, + /* '~' 126 */ VALID | UNRESERVED | PATHVALID , + /* '' 127 */ 0 +}; + +CF_INLINE Boolean isURLLegalCharacter(UniChar ch) { + return ( ( 32 <= ch ) && ( ch <= 127 ) ) ? ( sURLValidCharacters[ ch - 32 ] & VALID ) : false; +} + +CF_INLINE Boolean scheme_valid(UniChar ch) { + return ( ( 32 <= ch ) && ( ch <= 127 ) ) ? ( sURLValidCharacters[ ch - 32 ] & SCHEME ) : 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; + return ( ( 32 <= ch ) && ( ch <= 127 ) ) ? ( sURLValidCharacters[ ch - 32 ] & UNRESERVED ) : 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; + return ( ( 32 <= ch ) && ( ch <= 127 ) ) ? ( sURLValidCharacters[ ch - 32 ] & PATHVALID ) : false; } CF_INLINE Boolean isHexDigit(UniChar ch) { - return ((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F')); + return ( ( 32 <= ch ) && ( ch <= 127 ) ) ? ( sURLValidCharacters[ ch - 32 ] & HEXDIGIT ) : false; } + // Returns false if ch1 or ch2 isn't properly formatted CF_INLINE Boolean _translateBytes(UniChar ch1, UniChar ch2, uint8_t *result) { *result = 0; @@ -395,7 +395,7 @@ CF_INLINE Boolean _translateBytes(UniChar ch1, UniChar ch2, uint8_t *result) { } CF_INLINE Boolean _haveTestedOriginalString(CFURLRef url) { - return ((url->_flags & ORIGINAL_AND_URL_STRINGS_MATCH) != 0) || (url->_sanatizedString != NULL); + return ((url->_flags & ORIGINAL_AND_URL_STRINGS_MATCH) != 0) || (_getSanitizedString(url) != NULL); } typedef CFStringRef (*StringTransformation)(CFAllocatorRef, CFStringRef, CFIndex); @@ -441,16 +441,6 @@ static CFStringRef escapePathComponent(CFAllocatorRef alloc, CFStringRef origCom 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]; @@ -861,10 +851,10 @@ static Boolean __CFURLEqual(CFTypeRef cf1, CFTypeRef cf2) { 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) { + if ( url1->_base ) { + if (! url2->_base) return false; + if (!CFEqual( url1->_base, url2->_base )) return false; + } else if ( url2->_base) { return false; } @@ -924,12 +914,20 @@ static UInt32 __CFURLHash(CFTypeRef cf) { UInt32 result; if (CFURLCanBeDecomposed(url)) { CFStringRef lastComp = CFURLCopyLastPathComponent(url); + CFStringRef hostNameRef = CFURLCopyHostName(url ); + + result = 0; + if (lastComp) { result = CFHash(lastComp); CFRelease(lastComp); - } else { - result = 0; } + + if ( hostNameRef ) + { + result ^= CFHash( hostNameRef ); + CFRelease( hostNameRef ); + } } else { result = CFHash(CFURLGetString(url)); } @@ -939,7 +937,7 @@ static UInt32 __CFURLHash(CFTypeRef cf) { static CFStringRef __CFURLCopyFormattingDescription(CFTypeRef cf, CFDictionaryRef formatOptions) { CFURLRef url = cf; __CFGenericValidateType(cf, CFURLGetTypeID()); - if (!url->_base) { + if (! url->_base) { CFRetain(url->_string); return url->_string; } else { @@ -953,7 +951,7 @@ static CFStringRef __CFURLCopyDescription(CFTypeRef cf) { CFURLRef url = (CFURLRef)cf; CFStringRef result; CFAllocatorRef alloc = CFGetAllocator(url); - if (url->_base) { + 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); @@ -964,17 +962,12 @@ static CFStringRef __CFURLCopyDescription(CFTypeRef cf) { } #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); + +extern __attribute((used)) void __CFURLDumpMemRecord(void) { + CFStringRef str = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d URLs created; %d destroyed\n%d file URLs created; %d converted; %d destroyed. %d urls had 'extra' data allocated, %d had base urls\n"), numURLs, numDealloced, numFileURLsCreated, numFileURLsConverted, numFileURLsDealloced, numExtraDataAllocated, numURLsWithBaseURL ); CFShow(str); CFRelease(str); - if (URLAllocator) CFCountingAllocatorPrintPointers(URLAllocator); + // if (URLAllocator) CFCountingAllocatorPrintPointers(URLAllocator); } #endif @@ -989,10 +982,13 @@ static void __CFURLDeallocate(CFTypeRef cf) { numFileURLsDealloced ++; } #endif - CFRelease(url->_string); + if (url->_string) CFRelease(url->_string); // GC: 3879914 if (url->_base) CFRelease(url->_base); if (url->ranges) CFAllocatorDeallocate(alloc, url->ranges); - if (url->_sanatizedString) CFRelease(url->_sanatizedString); + if (_getSanitizedString(url)) CFRelease(_getSanitizedString(url)); + + if ( url->extra != NULL ) + CFAllocatorDeallocate( alloc, url->extra ); } static CFTypeID __kCFURLTypeID = _kCFRuntimeNotATypeID; @@ -1028,41 +1024,41 @@ CFTypeID CFURLGetTypeID(void) { __private_extern__ void CFShowURL(CFURLRef url) { if (!url) { - printf("(null)\n"); + fprintf(stdout, "(null)\n"); return; } - printf("{", (unsigned)url); + fprintf(stdout, "{", (const void*)url); if (CF_IS_OBJC(__kCFURLTypeID, url)) { - printf("ObjC bridged object}\n"); + fprintf(stdout, "ObjC bridged object}\n"); return; } - printf("\n\tPath type: "); + fprintf(stdout, "\n\tPath type: "); switch (URL_PATH_TYPE(url)) { case kCFURLPOSIXPathStyle: - printf("POSIX"); + fprintf(stdout, "POSIX"); break; case kCFURLHFSPathStyle: - printf("HFS"); + fprintf(stdout, "HFS"); break; case kCFURLWindowsPathStyle: - printf("NTFS"); + fprintf(stdout, "NTFS"); break; case FULL_URL_REPRESENTATION: - printf("Native URL"); + fprintf(stdout, "Native URL"); break; default: - printf("UNRECOGNIZED PATH TYPE %d", (char)URL_PATH_TYPE(url)); + fprintf(stdout, "UNRECOGNIZED PATH TYPE %d", (char)URL_PATH_TYPE(url)); } - printf("\n\tRelative string: "); + fprintf(stdout, "\n\tRelative string: "); CFShow(url->_string); - printf("\tBase URL: "); + fprintf(stdout, "\tBase URL: "); if (url->_base) { - printf("<0x%x> ", (unsigned)url->_base); + fprintf(stdout, "<%p> ", (const void*)url->_base); CFShow(url->_base); } else { - printf("(null)\n"); + fprintf(stdout, "(null)\n"); } - printf("\tFlags: 0x%x\n}\n", (unsigned)url->_flags); + fprintf(stdout, "\tFlags: %p\n}\n", (const void*)url->_flags); } @@ -1143,7 +1139,13 @@ static void _parseComponents(CFAllocatorRef alloc, CFStringRef string, CFURLRef ranges[0].location = base_idx; ranges[0].length = idx; numRanges ++; - base_idx = idx + 1; + base_idx = idx + 1; + // optimization for http urls + if (idx == 4 && STRING_CHAR(0) == 'h' && STRING_CHAR(1) == 't' && + STRING_CHAR(2) == 't' && STRING_CHAR(3) == 'p') + { + flags |= HAS_HTTP_SCHEME; + } break; } else if (!scheme_valid(ch)) { break; // invalid scheme character -- no scheme @@ -1151,9 +1153,11 @@ static void _parseComponents(CFAllocatorRef alloc, CFStringRef string, CFURLRef } // Make sure we have an RFC-1808 compliant URL - that's either something without a scheme, or scheme:/(stuff) or scheme://(stuff) + // Strictly speaking, RFC 1808 & 2396 bar "scheme:" (with nothing following the colon); however, common usage + // expects this to be treated identically to "scheme://" - REW, 12/08/03 if (!(flags & HAS_SCHEME)) { isCompliant = true; - } else if (!(base_idx < string_length)) { + } else if (base_idx == string_length) { isCompliant = false; } else if (STRING_CHAR(base_idx) != '/') { isCompliant = false; @@ -1171,19 +1175,18 @@ static void _parseComponents(CFAllocatorRef alloc, CFStringRef string, CFURLRef (*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; } @@ -1224,14 +1227,16 @@ static void _parseComponents(CFAllocatorRef alloc, CFStringRef string, CFURLRef 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)) { + // Find the ']' terminator of the IPv6 address, leave idx pointing to ']' or end + for ( ; idx < extent; ++ idx ) { + if ( ']' == STRING_CHAR(idx)) { + flags |= IS_IPV6_ENCODED; + break; + } + } + } + // there is a port if we see a colon. Only the last one is the port, though. + else if ( ':' == STRING_CHAR(idx)) { flags |= HAS_PORT; numRanges ++; ranges[4].location = idx+1; // base of port @@ -1285,6 +1290,17 @@ static void _parseComponents(CFAllocatorRef alloc, CFStringRef string, CFURLRef ranges[5] = pathRg; if (pathRg.length > 0) { + Boolean sawPercent = FALSE; + for (idx = pathRg.location; idx < string_length; idx++) { + if ('%' == STRING_CHAR(idx)) { + sawPercent = TRUE; + break; + } + } + if (!sawPercent) { + flags |= POSIX_AND_URL_PATHS_MATCH; + } + ch = STRING_CHAR(pathRg.location + pathRg.length - 1); if (ch == '/') { isDir = true; @@ -1326,9 +1342,6 @@ static void _parseComponents(CFAllocatorRef alloc, CFStringRef string, CFURLRef 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) { @@ -1386,11 +1399,14 @@ static void computeSanitizedString(CFURLRef url) { constructBuffers(alloc, url->_string, &cstring, &ustring, &useCString, &freeCharacters); if (!(url->_flags & IS_DECOMPOSABLE)) { // Impossible to have a problem character in the scheme + CFMutableStringRef sanitizedString = NULL; 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)) { + if (!scanCharacters(alloc, & sanitizedString, &(((struct __CFURL *)url)->_flags), cstring, ustring, useCString, base, string_length, &mark, 0, url->_encoding)) { ((struct __CFURL *)url)->_flags |= ORIGINAL_AND_URL_STRINGS_MATCH; } + if ( sanitizedString ) + _setSanitizedString( (struct __CFURL*) url, sanitizedString ); } else { // Go component by component CFIndex currentComponent = HAS_USER; @@ -1398,21 +1414,25 @@ static void computeSanitizedString(CFURLRef url) { 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); + CFMutableStringRef sanitizedString = NULL; + scanCharacters(alloc, & sanitizedString, &(((struct __CFURL *)url)->_flags), cstring, ustring, useCString, componentRange.location, componentRange.location + componentRange.length, &mark, currentComponent, url->_encoding); + + if ( sanitizedString ) + _setSanitizedString( (struct __CFURL*) url, sanitizedString ); } currentComponent = currentComponent << 1; } - if (!url->_sanatizedString) { + if (!_getSanitizedString(url)) { ((struct __CFURL *)url)->_flags |= ORIGINAL_AND_URL_STRINGS_MATCH; } } - if (url->_sanatizedString && mark != string_length) { + if (_getSanitizedString(url) && mark != string_length) { if (useCString) { CFStringRef tempString = CFStringCreateWithBytes(alloc, &(cstring[mark]), string_length - mark, kCFStringEncodingISOLatin1, false); - CFStringAppend(url->_sanatizedString, tempString); + CFStringAppend(_getSanitizedString(url), tempString); CFRelease(tempString); } else { - CFStringAppendCharacters(url->_sanatizedString, &(ustring[mark]), string_length - mark); + CFStringAppendCharacters(_getSanitizedString(url), &(ustring[mark]), string_length - mark); } } if (freeCharacters) { @@ -1454,14 +1474,13 @@ static CFStringRef correctedComponent(CFStringRef comp, UInt32 compFlag, CFStrin } #undef STRING_CHAR - CF_EXPORT CFURLRef _CFURLAlloc(CFAllocatorRef allocator) { struct __CFURL *url; #if DEBUG_URL_MEMORY_USAGE numURLs ++; - if (!URLAllocator) { - URLAllocator = CFCountingAllocatorCreate(NULL); - } + // if (!URLAllocator) { + // URLAllocator = CFCountingAllocatorCreate(NULL); + // } allocator = URLAllocator; #endif url = (struct __CFURL *)_CFRuntimeCreateInstance(allocator, __kCFURLTypeID, sizeof(struct __CFURL) - sizeof(CFRuntimeBase), NULL); @@ -1473,10 +1492,11 @@ CF_EXPORT CFURLRef _CFURLAlloc(CFAllocatorRef allocator) { url->_string = NULL; url->_base = NULL; url->ranges = NULL; - url->_reserved = NULL; + // url->_reserved = NULL; url->_encoding = kCFStringEncodingUTF8; - url->_sanatizedString = NULL; - } + // url->_sanatizedString = NULL; + url->extra = NULL; + } return url; } @@ -1488,12 +1508,16 @@ static void _CFURLInit(struct __CFURL *url, CFStringRef URLString, UInt32 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 + + url->_base = base ? CFURLCopyAbsoluteURL(base) : NULL; + + #if DEBUG_URL_MEMORY_USAGE if (fsType != FULL_URL_REPRESENTATION) { numFileURLsCreated ++; } -#endif + if ( url->_base ) + numURLsWithBaseURL ++; + #endif } CF_EXPORT void _CFURLInitFSPath(CFURLRef url, CFStringRef path) { @@ -1526,41 +1550,42 @@ CF_EXPORT Boolean _CFStringIsLegalURLString(CFStringRef string) { 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 + + // Make sure that two valid hex digits follow a '%' character + if ( ch == '%' ) { + 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 (! isHexDigit(ch) ) { + //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 (! isHexDigit(ch) ) { + //CFAssert1(false, __kCFLogAssertion, "Detected illegal percent escape sequence at character %d when trying to create a CFURL", idx-3); + idx = -1; + break; + } + + 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 ( isURLLegalCharacter( ch ) ) + continue; + break; } if (idx < length) { return false; @@ -1755,8 +1780,8 @@ CFURLRef CFURLCreateAbsoluteURLWithBytes(CFAllocatorRef alloc, const UInt8 *rela if (!useCompatibilityMode) { CFURLRef url = _CFURLCreateWithArbitraryString(alloc, relativeString, baseURL); CFRelease(relativeString); - ((struct __CFURL *)url)->_encoding = encoding; if (url) { + ((struct __CFURL *)url)->_encoding = encoding; CFURLRef absURL = CFURLCopyAbsoluteURL(url); CFRelease(url); return absURL; @@ -1838,7 +1863,7 @@ CFURLRef CFURLCreateAbsoluteURLWithBytes(CFAllocatorRef alloc, const UInt8 *rela 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); + UniChar *buf = CFAllocatorAllocate(alloc, sizeof(UniChar) * (pathRg.length + 1), 0); CFStringRef newPath; CFStringGetCharacters(absString, pathRg, buf); buf[pathRg.length] = '\0'; @@ -1884,7 +1909,7 @@ static CFStringRef _resolvedPath(UniChar *pathStr, UniChar *end, UniChar pathDel // Do not delete the sole path component break; } - } else if (*(idx+1) == '.' && (idx+2 == end || *(idx+2) == pathDelimiter)) { + } else if (( end-idx >= 2 ) && *(idx+1) == '.' && (idx+2 == end || (( end-idx > 2 ) && *(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; @@ -1907,6 +1932,7 @@ static CFStringRef _resolvedPath(UniChar *pathStr, UniChar *end, UniChar pathDel pathStr[0] = '.'; pathStr[1] = '/'; pathStr[2] = '\0'; + end = & pathStr[3]; break; } } @@ -1923,10 +1949,10 @@ static CFStringRef _resolvedPath(UniChar *pathStr, UniChar *end, UniChar pathDel } } } - while (idx < end && *idx != pathDelimiter) idx ++; + while (idx < end && *idx != pathDelimiter) idx ++; idx ++; } - if (stripTrailingDelimiter && end != pathStr && end-1 != pathStr && *(end-1) == pathDelimiter) { + if (stripTrailingDelimiter && end > pathStr && end-1 != pathStr && *(end-1) == pathDelimiter) { end --; } return CFStringCreateWithCharactersNoCopy(alloc, pathStr, end - pathStr, alloc); @@ -1991,11 +2017,11 @@ static CFMutableStringRef resolveAbsoluteURLString(CFAllocatorRef alloc, CFStrin * so we have to add one '/' to the newString * (Sergey Zubarev) */ -#if defined(__WIN32__) - if (CFStringGetCharacterAtIndex(newPath, 0) != '/') { - CFStringAppend(newString, CFSTR("/")); - } -#endif + // No - the input strings here are URL path strings, not Win32 paths. + // Absolute paths should have had a '/' prepended before this point. + // I have removed Sergey Zubarev's change and left his comment (and + // this one) as a record. - REW, 1/5/2004 + // 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) != '/') { @@ -2081,9 +2107,7 @@ CFURLRef CFURLCopyAbsoluteURL(CFURLRef relativeURL) { 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); + CF_OBJC_CALL0(CFURLRef, anURL, relativeURL, "absoluteURL"); if (anURL) CFRetain(anURL); return anURL; } @@ -2146,6 +2170,9 @@ CFURLRef CFURLCopyAbsoluteURL(CFURLRef relativeURL) { /*******************/ /* Basic accessors */ /*******************/ +CFStringEncoding _CFURLGetEncoding(CFURLRef url) { + return url->_encoding; +} Boolean CFURLCanBeDecomposed(CFURLRef anURL) { anURL = _CFURLFromNSURL(anURL); @@ -2170,7 +2197,7 @@ CFStringRef CFURLGetString(CFURLRef url) { if (url->_flags & ORIGINAL_AND_URL_STRINGS_MATCH) { return url->_string; } else { - return url->_sanatizedString; + return _getSanitizedString( url ); } } @@ -2182,6 +2209,9 @@ CFIndex CFURLGetBytes(CFURLRef url, UInt8 *buffer, CFIndex bufferLength) { string = CFURLGetString(url); enc = kCFStringEncodingUTF8; } else { + if (URL_PATH_TYPE(url) != FULL_URL_REPRESENTATION) { + _convertToURLRepresentation((struct __CFURL *)url); + } string = url->_string; enc = url->_encoding; } @@ -2212,7 +2242,7 @@ static CFRange _rangeForComponent(UInt32 flags, CFRange *ranges, UInt32 compFlag } return ranges[idx]; } - + static CFStringRef _retainedComponentString(CFURLRef url, UInt32 compFlag, Boolean fromOriginalString, Boolean removePercentEscapes) { CFRange rg; CFStringRef comp; @@ -2224,7 +2254,11 @@ static CFStringRef _retainedComponentString(CFURLRef url, UInt32 compFlag, Boole } rg = _rangeForComponent(url->_flags, url->ranges, compFlag); if (rg.location == kCFNotFound) return NULL; - comp = CFStringCreateWithSubstring(alloc, url->_string, rg); + if (compFlag & HAS_SCHEME && url->_flags & HAS_HTTP_SCHEME) { + comp = CFSTR("http"); + } else { + comp = CFStringCreateWithSubstring(alloc, url->_string, rg); + } if (!fromOriginalString) { if (!_haveTestedOriginalString(url)) { computeSanitizedString(url); @@ -2251,9 +2285,7 @@ static CFStringRef _retainedComponentString(CFURLRef url, UInt32 compFlag, Boole 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); + CF_OBJC_CALL0(CFStringRef, scheme, anURL, "scheme"); if (scheme) CFRetain(scheme); return scheme; } @@ -2265,6 +2297,9 @@ CFStringRef CFURLCopyScheme(CFURLRef anURL) { return kCFURLFileScheme; } } + if (anURL->_flags & IS_PARSED && anURL->_flags & HAS_HTTP_SCHEME) { + return(CFSTR("http")); + } scheme = _retainedComponentString(anURL, HAS_SCHEME, true, false); if (scheme) { return scheme; @@ -2321,11 +2356,11 @@ CFStringRef CFURLCopyNetLocation(CFURLRef 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 = CFStringGetLength( _getSanitizedString(anURL)) - netRg.location; + if (CFStringFindWithOptions(_getSanitizedString(anURL), CFSTR("/"), netRg, 0, &netLocEnd)) { netRg.length = netLocEnd.location - netRg.location; } - netLoc = CFStringCreateWithSubstring(CFGetAllocator(anURL), anURL->_sanatizedString, netRg); + netLoc = CFStringCreateWithSubstring(CFGetAllocator(anURL), _getSanitizedString(anURL), netRg); } else { netLoc = CFStringCreateWithSubstring(CFGetAllocator(anURL), anURL->_string, netRg); } @@ -2414,9 +2449,9 @@ CFStringRef CFURLCopyResourceSpecifier(CFURLRef anURL) { if (!_haveTestedOriginalString(anURL)) { computeSanitizedString(anURL); } - if (anURL->_sanatizedString) { + if (_getSanitizedString(anURL)) { // 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)); + return CFStringCreateWithSubstring(CFGetAllocator(anURL), _getSanitizedString(anURL), CFRangeMake(base, CFStringGetLength(_getSanitizedString(anURL))-base)); } else { return CFStringCreateWithSubstring(CFGetAllocator(anURL), anURL->_string, CFRangeMake(base, CFStringGetLength(anURL->_string)-base)); } @@ -2456,19 +2491,19 @@ CFStringRef CFURLCopyResourceSpecifier(CFURLRef anURL) { } 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); + rg.length = CFStringGetLength(_getSanitizedString(anURL)) - rg.location; + return CFStringCreateWithSubstring(alloc, _getSanitizedString(anURL), 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); + _parseComponents(alloc, _getSanitizedString(anURL), 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); + rg.length = CFStringGetLength(_getSanitizedString(anURL)) - rg.location; + return CFStringCreateWithSubstring(CFGetAllocator(anURL), _getSanitizedString(anURL), rg); } } else { // The resource specifier cannot possibly come from the base. @@ -2485,9 +2520,7 @@ CFStringRef CFURLCopyResourceSpecifier(CFURLRef anURL) { 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); + CF_OBJC_CALL0(CFStringRef, tmp, anURL, "host"); if (tmp) CFRetain(tmp); return tmp; } @@ -2521,9 +2554,8 @@ CFStringRef CFURLCopyHostName(CFURLRef anURL) { 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); + CFNumberRef cfPort; + CF_OBJC_CALL0(CFNumberRef, cfPort, anURL, "port"); SInt32 num; if (cfPort && CFNumberGetValue(cfPort, kCFNumberSInt32Type, &num)) return num; return -1; @@ -2556,9 +2588,7 @@ SInt32 CFURLGetPortNumber(CFURLRef anURL) { 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); + CF_OBJC_CALL0(CFStringRef, user, anURL, "user"); if (user) CFRetain(user); return user; } @@ -2582,9 +2612,7 @@ CFStringRef CFURLCopyUserName(CFURLRef anURL) { 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); + CF_OBJC_CALL0(CFStringRef, passwd, anURL, "password"); if (passwd) CFRetain(passwd); return passwd; } @@ -2610,9 +2638,7 @@ CFStringRef CFURLCopyPassword(CFURLRef anURL) { 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); + CF_OBJC_CALL0(CFStringRef, str, anURL, "parameterString"); if (str) CFRetain(str); return str; } @@ -2627,7 +2653,7 @@ static CFStringRef _unescapedParameterString(CFURLRef anURL) { return NULL; // Parameter string definitely coming from the relative portion of the URL } - return _unescapedParameterString(anURL->_base); + return _unescapedParameterString( anURL->_base); } CFStringRef CFURLCopyParameterString(CFURLRef anURL, CFStringRef charactersToLeaveEscaped) { @@ -2648,9 +2674,7 @@ CFStringRef CFURLCopyParameterString(CFURLRef anURL, CFStringRef charactersToL 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); + CF_OBJC_CALL0(CFStringRef, str, anURL, "query"); if (str) CFRetain(str); return str; } @@ -2686,9 +2710,7 @@ CFStringRef CFURLCopyQueryString(CFURLRef anURL, CFStringRef charactersToLeav 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); + CF_OBJC_CALL0(CFStringRef, str, anURL, "fragment"); if (str) CFRetain(str); return str; } @@ -2727,8 +2749,15 @@ static CFIndex insertionLocationForMask(CFURLRef url, CFOptionFlags mask) { // 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; + // Do not have to worry about the non-decomposable case here. However, we must be prepared for the degenerate + // case file:/path/immediately/without/host + CFRange schemeRg = _rangeForComponent(url->_flags, url->ranges, HAS_SCHEME); + CFRange pathRg = _rangeForComponent(url->_flags, url->ranges, HAS_PATH); + if (schemeRg.length + 1 == pathRg.location) { + return schemeRg.length + 1; + } else { + return schemeRg.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 @@ -2856,8 +2885,11 @@ static CFRange _getCharRangeInNonDecomposableURL(CFURLRef url, CFURLComponentTyp 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); + CFAssert2(component > 0 && component < 13, __kCFLogAssertion, "%s(): passed invalid component %d", __PRETTY_FUNCTION__, component); url = _CFURLFromNSURL(url); + if (URL_PATH_TYPE(url) != FULL_URL_REPRESENTATION) { + _convertToURLRepresentation((struct __CFURL *)url); + } if (!(url->_flags & IS_PARSED)) { _parseComponentsOfURL(url); } @@ -2938,7 +2970,7 @@ static CFStringRef schemeSpecificString(CFURLRef url) { } static Boolean decomposeToNonHierarchical(CFURLRef url, CFURLComponentsNonHierarchical *components) { - if (CFURLGetBaseURL(url) != NULL) { + if ( CFURLGetBaseURL(url) != NULL) { components->scheme = NULL; } else { components->scheme = CFURLCopyScheme(url); @@ -3070,11 +3102,13 @@ static CFURLRef composeFromRFC1808(CFAllocatorRef alloc, const CFURLComponentsRF } if (comp->host) { CFStringAppend(urlString, comp->host); - if (comp->port != kCFNotFound) { - CFStringAppendFormat(urlString, NULL, CFSTR(":%d"), comp->port); - } hadPrePathComponent = true; } + 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("/")); } @@ -3214,11 +3248,11 @@ CFURLRef _CFURLCreateFromComponents(CFAllocatorRef alloc, CFURLComponentDecompos } CF_EXPORT void *__CFURLReservedPtr(CFURLRef url) { - return url->_reserved; + return _getReserved(url); } CF_EXPORT void __CFURLSetReservedPtr(CFURLRef url, void *ptr) { - ((struct __CFURL *)url)->_reserved = ptr; + _setReserved ( (struct __CFURL*) url, ptr ); } @@ -3228,28 +3262,18 @@ CF_EXPORT void __CFURLSetReservedPtr(CFURLRef url, void *ptr) { static CFArrayRef WindowsPathToURLComponents(CFStringRef path, CFAllocatorRef alloc, Boolean isDir) { CFArrayRef tmp; CFMutableArrayRef urlComponents = NULL; - CFStringRef str; - UInt32 i=0; + CFIndex 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); + + CFStringRef 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; + CFIndex c; for (c = CFArrayGetCount(urlComponents); i < c; i ++) { CFStringRef fileComp = CFArrayGetValueAtIndex(urlComponents,i); CFStringRef urlComp = _replacePathIllegalCharacters(fileComp, alloc, false); @@ -3263,7 +3287,7 @@ static CFArrayRef WindowsPathToURLComponents(CFStringRef path, CFAllocatorRef al } CFRelease(urlComp); } -#endif + if (isDir) { if (CFStringGetLength(CFArrayGetValueAtIndex(urlComponents, CFArrayGetCount(urlComponents) - 1)) != 0) CFArrayAppendValue(urlComponents, CFSTR("")); @@ -3273,20 +3297,14 @@ static CFArrayRef WindowsPathToURLComponents(CFStringRef path, CFAllocatorRef al 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); - } + // WindowsPathToURLComponents already added percent escapes for us; no need to add them again here. + str = CFStringCreateByCombiningStrings(alloc, urlComponents, CFSTR("/")); CFRelease(urlComponents); return str; } @@ -3330,14 +3348,17 @@ static CFStringRef URLPathToWindowsPath(CFStringRef path, CFAllocatorRef allocat 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 + // Absolute path; we need to check for a drive letter in the second component, and if so, remove the first component CFStringRef firstComponent = CFURLCreateStringByReplacingPercentEscapesUsingEncoding(allocator, CFArrayGetValueAtIndex(components, 1), CFSTR(""), encoding); - CFArrayRemoveValueAtIndex(components, 0); - if (CFStringGetLength(firstComponent) == 2 && CFStringGetCharacterAtIndex(firstComponent, 1) == '|') { + UniChar ch; + if (CFStringGetLength(firstComponent) == 2 && ((ch = CFStringGetCharacterAtIndex(firstComponent, 1)) == '|' || ch == ':')) { // Drive letter - CFStringRef driveStr = CFStringCreateWithFormat(allocator, NULL, CFSTR("%c:"), CFStringGetCharacterAtIndex(firstComponent, 0)); - CFArraySetValueAtIndex(components, 0, driveStr); - CFRelease(driveStr); + CFArrayRemoveValueAtIndex(components, 0); + if (ch == '|') { + CFStringRef driveStr = CFStringCreateWithFormat(allocator, NULL, CFSTR("%c:"), CFStringGetCharacterAtIndex(firstComponent, 0)); + CFArraySetValueAtIndex(components, 0, driveStr); + CFRelease(driveStr); + } } CFRelease(firstComponent); } @@ -3436,36 +3457,29 @@ static CFStringRef _resolveFileSystemPaths(CFStringRef relativePath, CFStringRef 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); + CFIndex len; 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__); + len = CFStringGetLength(filePath); + switch(fsType) { case kCFURLPOSIXPathStyle: isAbsolute = (len > 0 && CFStringGetCharacterAtIndex(filePath, 0) == '/'); break; case kCFURLWindowsPathStyle: - isAbsolute = (len > 3 && CFStringGetCharacterAtIndex(filePath, 1) == ':' && CFStringGetCharacterAtIndex(filePath, 2) == '\\'); + isAbsolute = (len >= 3 && CFStringGetCharacterAtIndex(filePath, 1) == ':' && CFStringGetCharacterAtIndex(filePath, 2) == '\\'); /* Absolute path under Win32 can begin with "\\" * (Sergey Zubarev) */ @@ -3489,41 +3503,93 @@ CF_EXPORT CFURLRef CFURLCreateWithFileSystemPathRelativeToBase(CFAllocatorRef al CFURLRef url; Boolean isAbsolute = true, releaseFilePath = false; UniChar pathDelim = '\0'; - CFIndex len = CFStringGetLength(filePath); + CFIndex len; 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); + len = CFStringGetLength(filePath); + switch(fsType) { case kCFURLPOSIXPathStyle: isAbsolute = (len > 0 && CFStringGetCharacterAtIndex(filePath, 0) == '/'); + pathDelim = '/'; break; case kCFURLWindowsPathStyle: - isAbsolute = (len > 3 && CFStringGetCharacterAtIndex(filePath, 1) == ':' && CFStringGetCharacterAtIndex(filePath, 2) == '\\'); + 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) == '\\'); + if (!isAbsolute) + isAbsolute = (len > 2 && CFStringGetCharacterAtIndex(filePath, 0) == '\\' && CFStringGetCharacterAtIndex(filePath, 1) == '\\'); pathDelim = '\\'; break; case kCFURLHFSPathStyle: + { CFRange fullStrRange = CFRangeMake( 0, CFStringGetLength( filePath ) ); + isAbsolute = (len > 0 && CFStringGetCharacterAtIndex(filePath, 0) != ':'); pathDelim = ':'; + + if ( _CFExecutableLinkedOnOrAfter( CFSystemVersionTiger ) && + filePath && CFStringFindWithOptions( filePath, CFSTR("::"), fullStrRange, 0, NULL ) ) { + UniChar* chars = (UniChar*) malloc( fullStrRange.length * sizeof( UniChar ) ); + CFIndex index, writeIndex, firstColonOffset = -1; + + CFStringGetCharacters( filePath, fullStrRange, chars ); + + for ( index = 0, writeIndex = 0 ; index < fullStrRange.length; index ++ ) { + if ( chars[ index ] == ':' ) { + if ( index + 1 < fullStrRange.length && chars[ index + 1 ] == ':' ) { + + // Don't let :: go off the 'top' of the path -- which means that there always has to be at + // least one ':' to the left of the current write position to go back to. + if ( writeIndex > 0 && firstColonOffset >= 0 ) + { + writeIndex --; + while ( writeIndex > 0 && writeIndex >= firstColonOffset && chars[ writeIndex ] != ':' ) + writeIndex --; + } + index ++; // skip over the first ':', so we replace the ':' which is there with a new one + } + + if ( firstColonOffset == -1 ) + firstColonOffset = writeIndex; + } + + chars[ writeIndex ++ ] = chars[ index ]; + } + + if ( releaseFilePath && filePath ) + CFRelease( filePath ); + + filePath = CFStringCreateWithCharacters( allocator, chars, writeIndex ); + releaseFilePath = true; + + free( chars ); + } + break; + } } if (isAbsolute) { baseURL = NULL; } + if (isDirectory && len > 0 && CFStringGetCharacterAtIndex(filePath, len-1) != pathDelim) { - filePath = CFStringCreateWithFormat(allocator, NULL, CFSTR("%@%c"), filePath, pathDelim); + CFStringRef tempRef = CFStringCreateWithFormat(allocator, NULL, CFSTR("%@%c"), filePath, pathDelim); + if ( releaseFilePath && filePath ) CFRelease( filePath ); + filePath = tempRef; 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)); + CFStringRef tempRef = CFStringCreateWithSubstring(allocator, filePath, CFRangeMake(0, len-1)); + if ( releaseFilePath && filePath ) + CFRelease( filePath ); + filePath = tempRef; releaseFilePath = true; } } @@ -3536,7 +3602,7 @@ CF_EXPORT CFURLRef CFURLCreateWithFileSystemPathRelativeToBase(CFAllocatorRef al 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 + // 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; @@ -3566,7 +3632,7 @@ CF_EXPORT CFURLRef CFURLCreateWithFileSystemPathRelativeToBase(CFAllocatorRef al return url; } -CFStringRef CFURLCopyFileSystemPath(CFURLRef anURL, CFURLPathStyle pathStyle) { +CF_EXPORT 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); } @@ -3612,7 +3678,12 @@ CFStringRef CFURLCreateStringWithFileSystemPath(CFAllocatorRef allocator, CFURLR CFRelease(urlPath); } } - if (relPath && CFURLHasDirectoryPath(anURL) && CFStringGetLength(relPath) > 1 && CFStringGetCharacterAtIndex(relPath, CFStringGetLength(relPath)-1) == PATH_DELIM_FOR_TYPE(fsType)) { + + // For Tiger, leave this behavior in for all path types. For Chablis, it would be nice to remove this entirely + // and do a linked-on-or-later check so we don't break third parties. + // See Converting volume name from POSIX to HFS form fails and + // CF needs to back out 4003028 for icky details. + 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; @@ -3636,8 +3707,6 @@ Boolean CFURLGetFileSystemRepresentation(CFURLRef url, Boolean resolveAgainstBas 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 @@ -3666,8 +3735,6 @@ CFURLRef CFURLCreateFromFileSystemRepresentation(CFAllocatorRef allocator, const 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 @@ -3681,8 +3748,6 @@ CF_EXPORT CFURLRef CFURLCreateFromFileSystemRepresentationRelativeToBase(CFAlloc 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 @@ -3977,28 +4042,11 @@ CFURLRef CFURLCreateCopyDeletingPathExtension(CFAllocatorRef allocator, CFURLRef 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; + return false; } -#else -#define __CFFSRefForVolumeName(A, B, C) (-3296) -#endif static CFArrayRef HFSPathToURLComponents(CFStringRef path, CFAllocatorRef alloc, Boolean isDir) { CFArrayRef components = CFStringCreateArrayBySeparatingStrings(alloc, path, CFSTR(":")); @@ -4008,9 +4056,6 @@ static CFArrayRef HFSPathToURLComponents(CFStringRef path, CFAllocatorRef alloc, UInt32 i, cnt; CFRelease(components); -#if defined(HAVE_CARBONCORE) && !defined(__MACOS8__) - doSpecialLeadingColon = true; -#endif if (!doSpecialLeadingColon && firstChar != ':') { CFArrayInsertValueAtIndex(newComponents, 0, CFSTR("")); @@ -4030,7 +4075,7 @@ static CFArrayRef HFSPathToURLComponents(CFStringRef path, CFAllocatorRef alloc, 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)) { + if (__CFFSRefForVolumeName(firstComp, &volSpec, alloc) && (__CFCarbonCore_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 @@ -4106,337 +4151,9 @@ static CFStringRef HFSPathToURLPath(CFStringRef path, CFAllocatorRef alloc, Bool 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 index 445936f..18fedb2 100644 --- a/URL.subproj/CFURL.h +++ b/URL.subproj/CFURL.h @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -23,7 +21,7 @@ * @APPLE_LICENSE_HEADER_END@ */ /* CFURL.h - Copyright (c) 1998-2003, Apple, Inc. All rights reserved. + Copyright (c) 1998-2005, Apple, Inc. All rights reserved. */ #if !defined(__COREFOUNDATION_CFURL__) @@ -91,6 +89,7 @@ CFURLRef CFURLCreateWithString(CFAllocatorRef allocator, CFStringRef URLString, /* 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 */ +CF_EXPORT 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 @@ -291,6 +290,7 @@ CFURLRef CFURLCreateCopyDeletingPathExtension(CFAllocatorRef allocator, CFURLRef /* 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 */ +CF_EXPORT CFIndex CFURLGetBytes(CFURLRef url, UInt8 *buffer, CFIndex bufferLength) AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; typedef enum { @@ -372,6 +372,7 @@ parameter (47, 6) (46, 8) query (54, 5) (53, 7) fragment (60, 8) (59, 9) */ +CF_EXPORT CFRange CFURLGetByteRangeForComponent(CFURLRef url, CFURLComponentType component, CFRange *rangeIncludingSeparators) AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; #endif diff --git a/URL.subproj/CFURLAccess.c b/URL.subproj/CFURLAccess.c index ea55c0c..2559602 100644 --- a/URL.subproj/CFURLAccess.c +++ b/URL.subproj/CFURLAccess.c @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -41,7 +39,7 @@ CFData read/write routines #include #if defined(__WIN32__) -#include +#include #include #include #include @@ -53,76 +51,21 @@ CFData read/write routines #undef BOOLEAN #undef timeval #else +#include #include #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; - } -} +DEFINE_WEAK_CFNETWORK_FUNC(Boolean, _CFURLCreateDataAndPropertiesFromResource, (CFAllocatorRef A, CFURLRef B, CFDataRef *C, CFDictionaryRef *D, CFArrayRef E, SInt32 *F), (A, B, C, D, E, F), false) +DEFINE_WEAK_CFNETWORK_FUNC(Boolean, _CFURLWriteDataAndPropertiesToResource, (CFURLRef A, CFDataRef B, CFDictionaryRef C, SInt32 *D), (A, B, C, D), false) +DEFINE_WEAK_CFNETWORK_FUNC(Boolean, _CFURLDestroyResource, (CFURLRef A, SInt32 *B), (A, B), false) #endif @@ -293,7 +236,7 @@ static Boolean _CFFileURLWritePropertiesToResource(CFURLRef url, CFDictionaryRef CFNumberGetValue(modeNum, kCFNumberSInt32Type, &mode); } else { #if defined(__WIN32__) - const unsigned short *modePtr = (const unsigned short *)CFDataGetBytePtr((CFDataRef)value); + const uint16_t *modePtr = (const uint16_t *)CFDataGetBytePtr((CFDataRef)value); #else const mode_t *modePtr = (const mode_t *)CFDataGetBytePtr((CFDataRef)value); #endif @@ -323,7 +266,8 @@ static Boolean _CFFileURLCreateDataAndPropertiesFromResource(CFAllocatorRef allo Boolean releaseAlloc = false; if (alloc == NULL) { - // We need a real allocator to pass to _CFReadBytesFromFile + // We need a real allocator to pass to _CFReadBytesFromFile so that the CFDataRef we create with + // CFDataCreateWithBytesNoCopy() can free up the object _CFReadBytesFromFile() returns. alloc = CFRetain(__CFGetDefaultAllocator()); releaseAlloc = true; } @@ -365,7 +309,12 @@ Boolean CFURLCreateDataAndPropertiesFromResource(CFAllocatorRef alloc, CFURLRef result = _CFFileURLCreateDataAndPropertiesFromResource(alloc, url, fetchedData, desiredProperties, fetchedProperties, errorCode); } else { #if defined(__MACH__) - result = __CFURLCreateDataAndPropertiesFromResource(alloc, url, fetchedData, fetchedProperties, desiredProperties, errorCode); + result = __CFNetwork__CFURLCreateDataAndPropertiesFromResource(alloc, url, fetchedData, fetchedProperties, desiredProperties, errorCode); + if (!result) { + if (fetchedData) *fetchedData = NULL; + if (fetchedProperties) *fetchedProperties = NULL; + if (errorCode) *errorCode = kCFURLUnknownSchemeError; + } #else if (fetchedData) *fetchedData = NULL; if (fetchedProperties) *fetchedProperties = NULL; @@ -431,7 +380,11 @@ Boolean CFURLWriteDataAndPropertiesToResource(CFURLRef url, CFDataRef data, CFDi } else { CFRelease(scheme); #if defined(__MACH__) - return __CFURLWriteDataAndPropertiesToResource(url, data, propertyDict, errorCode); + Boolean result = __CFNetwork__CFURLWriteDataAndPropertiesToResource(url, data, propertyDict, errorCode); + if (!result) { + if (errorCode) *errorCode = kCFURLUnknownSchemeError; + } + return result; #else if (errorCode) *errorCode = kCFURLUnknownSchemeError; return false; @@ -473,7 +426,11 @@ Boolean CFURLDestroyResource(CFURLRef url, SInt32 *errorCode) { } else { CFRelease(scheme); #if defined(__MACH__) - return __CFURLDestroyResource(url, errorCode); + Boolean result = __CFNetwork__CFURLDestroyResource(url, errorCode); + if (!result) { + if (errorCode) *errorCode = kCFURLUnknownSchemeError; + } + return result; #else if (errorCode) *errorCode = kCFURLUnknownSchemeError; return false; diff --git a/URL.subproj/CFURLAccess.h b/URL.subproj/CFURLAccess.h index 079a254..b1d094b 100644 --- a/URL.subproj/CFURLAccess.h +++ b/URL.subproj/CFURLAccess.h @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -23,7 +21,7 @@ * @APPLE_LICENSE_HEADER_END@ */ /* CFURLAccess.h - Copyright (c) 1998-2003, Apple, Inc. All rights reserved. + Copyright (c) 1998-2005, Apple, Inc. All rights reserved. */ #if !defined(__COREFOUNDATION_CFURLACCESS__) @@ -55,6 +53,12 @@ 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. + +NOTE: When asking for the resource data, this call will allocate the entire +resource in memory. This can be very expensive, depending on the size of the +resource (file). Please use CFStream or other techniques if you are downloading +large files. + */ CF_EXPORT Boolean CFURLCreateDataAndPropertiesFromResource(CFAllocatorRef alloc, CFURLRef url, CFDataRef *resourceData, CFDictionaryRef *properties, CFArrayRef desiredProperties, SInt32 *errorCode); diff --git a/framework.make b/framework.make new file mode 100755 index 0000000..34431c8 --- /dev/null +++ b/framework.make @@ -0,0 +1,626 @@ +# Simple makefile for building a framework or library on platforms other than OS X. +# the open source subset used in 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". +# +# Interesting variables to be set by the including Makefile: +# NAME base name of the framework or library +# CFILES .c to build +# CPP_FILES .cpp to build +# PUBLIC_HFILES .h files that will be installed for clients of API +# PRIVATE_HFILES .h files that will be installed for clients of SPI +# PROJECT_HFILES the rest of the .h files in the project +# PUBLIC_IFILES .i with API +# PRIVATE_IFILES .i files with SPI +# IFILES_DIR directory holding all the .i files +# MASTER_INTERFACE_DIR location of .i files we depend on +# +# We now follow the model of modern PB builds, which allow SYMROOT and OBJROOT to be shared +# across projects during development. This provides the benefit that one set of build flags +# (-F on Mach, -I and -L on Unix or Cygwin) can be used to share build products across projects. +# For release builds, the directories are always separate per project. +# +# 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 +# +# (Note: lame "#*/" tacked onto some lines is to get PB to stop syntax coloring the entire rest of the file as a comment.) + +# 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, variants of CYGWIN +ifeq "$(PLATFORM)" "" +PLATFORM := $(shell uname) +endif + +ifeq "$(PLATFORM)" "Darwin" +# Darwin platforms always define __MACH__ +else +ifneq "" "$(findstring CYGWIN, $(PLATFORM))" +# The windows platforms all define one cpp symbol or another, which CFBase.h funnels to __WIN32__. +# Simplify later checks, since we don't care about different versions of CYGWIN. +PLATFORM = CYGWIN +else +ifeq "$(PLATFORM)" "Linux" +PLATFORM_CFLAGS = -D__LINUX__=1 +else +ifeq "$(PLATFORM)" "FreeBSD" +PLATFORM_CFLAGS = -D__FREEBSD__=1 +else +$(error Platform could not be identified. Neither $$PLATFORM was set, nor the result of uname was recognized) +endif +endif +endif +endif + +# +# Set up basic variables, commands we use +# + +ifndef SRCROOT +SRCROOT = . +endif + +ifndef OBJROOT +OBJROOT = /tmp/$(NAME).obj +endif + +ifndef SYMROOT +SYMROOT = /tmp/$(NAME).sym +endif + +ifndef DSTROOT +DSTROOT = /tmp/$(NAME).dst +endif + +SILENT = @ +ifeq "$(PLATFORM)" "CYGWIN" +CC = gcc +CPLUSPLUS = g++ +ECHO = echo +MKDIRS = mkdir -p +COPY = cp +COPY_RECUR = cp -r +REMOVE = rm +REMOVE_RECUR = rm -rf +SYMLINK = ln -sfh +CHMOD = chmod +CHOWN = chown +TAR = tar +TOUCH = touch +STRIP = strip +DLLTOOL = dlltool +INTERFACER = Interfacer +else +ifeq "$(PLATFORM)" "Darwin" +CC = /usr/bin/cc +else +CC = /usr/bin/gcc +endif +CPLUSPLUS = /usr/bin/g++ +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 -sfh +CHMOD = /bin/chmod +CHOWN = /usr/sbin/chown +TAR = /usr/bin/tar +TOUCH = /usr/bin/touch +STRIP = /usr/bin/strip +INTERFACER = /AppleInternal/Developer/Tools/Interfacer +endif + +# +# Set up CC flags +# + +ifeq "$(PLATFORM)" "Darwin" +C_WARNING_FLAGS += -Wno-precomp -Wno-four-char-constants -Wall +CPP_WARNING_FLAGS += -Wno-precomp -Wno-four-char-constants -Wall +endif + +ifeq "$(PLATFORM)" "CYGWIN" +C_WARNING_FLAGS += -Wall +CPP_WARNING_FLAGS += -Wall +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 = -fno-common -pipe $(PLATFORM_CFLAGS) $(C_WARNING_FLAGS) -I. +CPPFLAGS = -fno-common -pipe $(PLATFORM_CFLAGS) $(CPP_WARNING_FLAGS) -I. + +ifeq "$(PLATFORM)" "Darwin" +CFLAGS += $(ARCH_FLAGS) -F$(SYMROOT) -fconstant-cfstrings +CPPFLAGS += $(ARCH_FLAGS) -F$(SYMROOT) -fconstant-cfstrings +endif + +ifeq "$(PLATFORM)" "CYGWIN" +# -mno-cygwin can be left out to build using the CYGWIN unix emulation libs +CFLAGS += -mno-cygwin +CPPFLAGS += -mno-cygwin +endif + + + +# +# Set style of building the library/framework, and the linker flags +# + +ifeq "$(wildcard /System/Library/Frameworks)" "" +LIBRARY_STYLE = Library +LIBRARY_EXT = .so +RELEASE_LIB = lib$(NAME)$(LIBRARY_EXT) +DEBUG_LIB = lib$(NAME)_debug$(LIBRARY_EXT) +PROFILE_LIB = lib$(NAME)_profile$(LIBRARY_EXT) +ifeq "$(PLATFORM)" "Linux" +LIBRARY_EXT = .a +endif +INSTALLDIR = /usr/local/lib +ifeq "$(PLATFORM)" "CYGWIN" +LIBRARY_EXT = .dll +RELEASE_LIB = $(NAME)$(LIBRARY_EXT) +DEBUG_LIB = $(NAME)_debug$(LIBRARY_EXT) +PROFILE_LIB = $(NAME)_profile$(LIBRARY_EXT) +RELEASE_IMPLIB = lib$(RELEASE_LIB:.dll=.a) +DEBUG_IMPLIB = lib$(DEBUG_LIB:.dll=.a) +PROFILE_IMPLIB = lib$(PROFILE_LIB:.dll=.a) +INSTALLDIR = /usr/local/bin +LIB_INSTALLDIR = /usr/local/lib +endif +HEADER_INSTALLDIR = /usr/local/include/$(NAME) +INSTALLDIR = /usr/local/lib +MASTER_INTERFACE_DIR = $(SYMROOT)/interfaces +# Next four dirs are used at build time, but not install time +PUBLIC_HEADER_DIR = $(SYMROOT)/Headers/$(NAME) +PRIVATE_HEADER_DIR = $(SYMROOT)/PrivateHeaders/$(NAME) +PROJECT_HEADER_DIR = $(OBJROOT)/$(NAME).build/ProjectHeaders/$(NAME) +RESOURCE_DIR = $(SYMROOT) +else +LIBRARY_STYLE = Framework +RELEASE_LIB = $(NAME) +DEBUG_LIB = $(NAME)_debug +PROFILE_LIB = $(NAME)_profile +INSTALLDIR = /System/Library/Frameworks +FRAMEWORK_DIR = /System/Library/Frameworks/$(NAME).framework +MASTER_INTERFACE_DIR = /AppleInternal/Carbon/interfaces +# Next three dirs are used at build time, but not install time +PUBLIC_HEADER_DIR = $(SYMROOT)/$(NAME).framework/Versions/A/Headers +PRIVATE_HEADER_DIR = $(SYMROOT)/$(NAME).framework/Versions/A/PrivateHeaders +PROJECT_HEADER_DIR = $(OBJROOT)/$(NAME).build/ProjectHeaders +endif + +ifeq "$(PLATFORM)" "Darwin" +LFLAGS = $(ARCH_FLAGS) -dynamiclib -dynamic +endif + +ifeq "$(PLATFORM)" "FreeBSD" +LFLAGS = -shared +endif + +ifeq "$(PLATFORM)" "CYGWIN" +# -mno-cygwin can be left out to build using the CYGWIN unix emulation libs +LFLAGS = -mno-cygwin -L$(SYMROOT) +endif + +# other flags passed in from the make command line, and RC +CFLAGS += $(OTHER_CFLAGS) $(RC_CFLAGS) +CPPFLAGS += $(OTHER_CPPFLAGS) $(RC_CFLAGS) +LFLAGS += $(OTHER_LFLAGS) + + +# Needed to find Project Headers, which work in PB because of the fancy -header-mapfile feature. +CFLAGS += -I$(PROJECT_HEADER_DIR) +CPPFLAGS += -I$(PROJECT_HEADER_DIR) +# Needed for cases when a private header is included as "Foo.h" instead of +CFLAGS += -I$(PRIVATE_HEADER_DIR) +CPPFLAGS += -I$(PRIVATE_HEADER_DIR) +ifeq "$(LIBRARY_STYLE)" "Library" +# Needed for headers included as , since there is no -FframeworkDir mechanism at work +CFLAGS += -I$(PUBLIC_HEADER_DIR)/.. -I$(PRIVATE_HEADER_DIR)/.. +CPPFLAGS += -I$(PUBLIC_HEADER_DIR)/.. -I$(PRIVATE_HEADER_DIR)/.. +endif + + +.PHONY: build all prebuild release debug profile debug-build release-build profile-build build-realwork test +default: build +all: build +build: prebuild debug-build release-build profile-build +release: prebuild release-build +debug: prebuild debug-build +profile: prebuild profile-build + +# 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 +# test invoke items in Tests subdirectory + +#-------------------------------------------------------------------------------- +# INSTALL +#-------------------------------------------------------------------------------- + +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)_PRIVHEADERS), $(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) $(PUBLIC_HFILES) $(DSTROOT)/$(FRAMEWORK_DIR)/Versions/A/Headers +# Install two private headers for internal Apple projects' use + $(SILENT) $(COPY) Base.subproj/CFPriv.h Base.subproj/CFRuntime.h PlugIn.subproj/CFBundlePriv.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) $(PUBLIC_HFILES) $(DSTROOT)/$(HEADER_INSTALLDIR) + $(SILENT) $(CHMOD) -w $(DSTROOT)/$(HEADER_INSTALLDIR)/*.h #*/ +endif + +install: build install_before install_builtin install_after +install_before:: +install_after:: + +install_builtin: + $(SILENT) $(ECHO) "Installing..." +ifeq "$(LIBRARY_STYLE)" "Framework" + $(SILENT) $(REMOVE_RECUR) $(DSTROOT)/$(FRAMEWORK_DIR) + $(SILENT) $(MKDIRS) $(DSTROOT)/$(FRAMEWORK_DIR) + -$(SILENT) $(CHMOD) -R +w $(DSTROOT)/$(FRAMEWORK_DIR) + $(SILENT) (cd $(SYMROOT) && $(TAR) -cf - $(NAME).framework) | (cd $(DSTROOT)/$(INSTALLDIR) && $(TAR) -xf -) + $(SILENT) $(STRIP) -S $(DSTROOT)/$(FRAMEWORK_DIR)/Versions/A/$(RELEASE_LIB) + $(SILENT) $(STRIP) -S $(DSTROOT)/$(FRAMEWORK_DIR)/Versions/A/$(DEBUG_LIB) + $(SILENT) $(STRIP) -S $(DSTROOT)/$(FRAMEWORK_DIR)/Versions/A/$(PROFILE_LIB) + $(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)/$(RELEASE_LIB) + $(SILENT) $(REMOVE) -f $(DSTROOT)/$(INSTALLDIR)/$(DEBUG_LIB) + $(SILENT) $(REMOVE) -f $(DSTROOT)/$(INSTALLDIR)/$(PROFILE_LIB) + $(SILENT) $(COPY) $(SYMROOT)/$(RELEASE_LIB) $(DSTROOT)/$(INSTALLDIR)/$(RELEASE_LIB) + $(SILENT) $(COPY) $(SYMROOT)/$(DEBUG_LIB) $(DSTROOT)/$(INSTALLDIR)/$(DEBUG_LIB) + $(SILENT) $(COPY) $(SYMROOT)/$(PROFILE_LIB) $(DSTROOT)/$(INSTALLDIR)/$(PROFILE_LIB) +ifneq "$(PLATFORM)" "CYGWIN" + -$(SILENT) $(CHOWN) root:wheel $(DSTROOT)/$(INSTALLDIR)/$(RELEASE_LIB) + -$(SILENT) $(CHOWN) root:wheel $(DSTROOT)/$(INSTALLDIR)/$(DEBUG_LIB) + -$(SILENT) $(CHOWN) root:wheel $(DSTROOT)/$(INSTALLDIR)/$(PROFILE_LIB) +endif + $(SILENT) $(CHMOD) 755 $(DSTROOT)/$(INSTALLDIR)/$(RELEASE_LIB) + $(SILENT) $(CHMOD) 755 $(DSTROOT)/$(INSTALLDIR)/$(DEBUG_LIB) + $(SILENT) $(CHMOD) 755 $(DSTROOT)/$(INSTALLDIR)/$(PROFILE_LIB) +ifeq "$(PLATFORM)" "CYGWIN" + $(SILENT) $(MKDIRS) $(DSTROOT)/$(LIB_INSTALLDIR) + -$(SILENT) $(CHMOD) +w $(DSTROOT)/$(LIB_INSTALLDIR) + $(SILENT) $(REMOVE) -f $(DSTROOT)/$(LIB_INSTALLDIR)/$(RELEASE_IMPLIB) + $(SILENT) $(REMOVE) -f $(DSTROOT)/$(LIB_INSTALLDIR)/$(DEBUG_IMPLIB) + $(SILENT) $(REMOVE) -f $(DSTROOT)/$(LIB_INSTALLDIR)/$(PROFILE_IMPLIB) + $(SILENT) $(COPY) $(SYMROOT)/$(RELEASE_IMPLIB) $(DSTROOT)/$(LIB_INSTALLDIR)/$(RELEASE_IMPLIB) + $(SILENT) $(COPY) $(SYMROOT)/$(DEBUG_IMPLIB) $(DSTROOT)/$(LIB_INSTALLDIR)/$(DEBUG_IMPLIB) + $(SILENT) $(COPY) $(SYMROOT)/$(PROFILE_IMPLIB) $(DSTROOT)/$(LIB_INSTALLDIR)/$(PROFILE_IMPLIB) + $(SILENT) $(CHMOD) 755 $(DSTROOT)/$(LIB_INSTALLDIR)/$(RELEASE_IMPLIB) + $(SILENT) $(CHMOD) 755 $(DSTROOT)/$(LIB_INSTALLDIR)/$(DEBUG_IMPLIB) + $(SILENT) $(CHMOD) 755 $(DSTROOT)/$(LIB_INSTALLDIR)/$(PROFILE_IMPLIB) +endif + $(SILENT) $(MKDIRS) $(DSTROOT)/$(HEADER_INSTALLDIR) + -$(SILENT) $(CHMOD) +w $(DSTROOT)/$(HEADER_INSTALLDIR)/*.h #*/ + $(SILENT) $(COPY) $(PUBLIC_HFILES) $(DSTROOT)/$(HEADER_INSTALLDIR) + -$(SILENT) $(CHMOD) -w $(DSTROOT)/$(HEADER_INSTALLDIR)/*.h #*/ +endif + +#-------------------------------------------------------------------------------- +# CLEAN +#-------------------------------------------------------------------------------- + +clean: clean_before clean_builtin clean_after +clean_before:: +clean_after:: + +clean_builtin: + $(SILENT) $(ECHO) "Deleting build products..." + $(REMOVE_RECUR) $(OBJROOT)/$(NAME).build +ifeq "$(LIBRARY_STYLE)" "Framework" + $(REMOVE_RECUR) $(SYMROOT)/$(NAME).framework +endif +ifeq "$(LIBRARY_STYLE)" "Library" + $(REMOVE) -f $(SYMROOT)/$(RELEASE_LIB) + $(REMOVE) -f $(SYMROOT)/$(DEBUG_LIB) + $(REMOVE) -f $(SYMROOT)/$(PROFILE_LIB) + $(REMOVE_RECUR) -f $(PUBLIC_HEADER_DIR) $(PRIVATE_HEADER_DIR) +ifeq "$(PLATFORM)" "CYGWIN" + $(REMOVE) -f $(SYMROOT)/$(RELEASE_IMPLIB) + $(REMOVE) -f $(SYMROOT)/$(DEBUG_IMPLIB) + $(REMOVE) -f $(SYMROOT)/$(PROFILE_IMPLIB) + $(REMOVE) -f $(SYMROOT)/$(RELEASE_LIB:.dll=.lib) + $(REMOVE) -f $(SYMROOT)/$(DEBUG_LIB:.dll=.lib) + $(REMOVE) -f $(SYMROOT)/$(PROFILE_LIB:.dll=.lib) + $(REMOVE) -f $(SYMROOT)/$(RELEASE_LIB:.dll=.defs) + $(REMOVE) -f $(SYMROOT)/$(DEBUG_LIB:.dll=.defs) + $(REMOVE) -f $(SYMROOT)/$(PROFILE_LIB:.dll=.exp) + $(REMOVE) -f $(SYMROOT)/$(RELEASE_LIB:.dll=.exp) + $(REMOVE) -f $(SYMROOT)/$(DEBUG_LIB:.dll=.exp) + $(REMOVE) -f $(SYMROOT)/$(PROFILE_LIB:.dll=.defs) +endif +endif + +#-------------------------------------------------------------------------------- +# PREBUILD +#-------------------------------------------------------------------------------- + +prebuild: prebuild_before prebuild_setup prebuild_headers prebuild_after +prebuild_before:: +prebuild_after:: + +# build the framework, or other basic dir structure +prebuild_setup:: + $(SILENT) $(ECHO) "Prebuild-setup..." + $(SILENT) $(MKDIRS) $(SYMROOT) +ifeq "$(LIBRARY_STYLE)" "Framework" +prebuild_setup:: + $(SILENT) $(MKDIRS) $(SYMROOT)/$(NAME).framework/Versions/A/Resources + $(SILENT) $(SYMLINK) A $(SYMROOT)/$(NAME).framework/Versions/Current + $(SILENT) $(SYMLINK) Versions/Current/Headers $(SYMROOT)/$(NAME).framework/Headers + $(SILENT) $(SYMLINK) Versions/Current/PrivateHeaders $(SYMROOT)/$(NAME).framework/PrivateHeaders + $(SILENT) $(SYMLINK) Versions/Current/Resources $(SYMROOT)/$(NAME).framework/Resources +endif + +ifeq "$(LIBRARY_STYLE)" "Framework" +PLATFORM_IFLAGS = -framework $(NAME) -frameworkInterfaces $(IFILES_DIR) +ALL_IFILES = $(foreach F,$(PUBLIC_IFILES) $(PRIVATE_IFILES),$(IFILES_DIR)/$(F)) + +# Since they share output directories, if either the ifiles or hfiles change we must redo both +prebuild_headers: $(OBJROOT)/$(NAME).build/Headers.touch +$(OBJROOT)/$(NAME).build/Headers.touch: $(PUBLIC_HFILES) $(PRIVATE_HFILES) $(PROJECT_HFILES) $(ALL_IFILES) + $(SILENT) $(REMOVE_RECUR) $(PUBLIC_HEADER_DIR) + $(SILENT) $(REMOVE_RECUR) $(PRIVATE_HEADER_DIR) + $(SILENT) $(REMOVE_RECUR) $(PROJECT_HEADER_DIR) + $(SILENT) $(MKDIRS) $(PUBLIC_HEADER_DIR) + $(SILENT) $(MKDIRS) $(PRIVATE_HEADER_DIR) + $(SILENT) $(MKDIRS) $(PROJECT_HEADER_DIR) + $(SILENT) $(MAKE) prebuild_copy_headers +ifneq "$(ALL_IFILES)" "" + $(SILENT) $(MAKE) prebuild_gen_headers +endif + $(SILENT) $(TOUCH) $(OBJROOT)/$(NAME).build/Headers.touch +else + +ALL_IFILES = $(foreach F,$(PUBLIC_IFILES) $(PRIVATE_IFILES),$(IFILES_DIR)/$(F)) + +# Since they share output directories, if either the ifiles or hfiles change we must redo both +prebuild_headers: $(OBJROOT)/$(NAME).build/Headers.touch +$(OBJROOT)/$(NAME).build/Headers.touch: $(PUBLIC_HFILES) $(PRIVATE_HFILES) $(PROJECT_HFILES) $(ALL_IFILES) + $(SILENT) $(REMOVE_RECUR) $(PUBLIC_HEADER_DIR) + $(SILENT) $(REMOVE_RECUR) $(PRIVATE_HEADER_DIR) + $(SILENT) $(REMOVE_RECUR) $(PROJECT_HEADER_DIR) + $(SILENT) $(MKDIRS) $(PUBLIC_HEADER_DIR) + $(SILENT) $(MKDIRS) $(PRIVATE_HEADER_DIR) + $(SILENT) $(MKDIRS) $(PROJECT_HEADER_DIR) + $(SILENT) $(MAKE) prebuild_copy_headers +ifneq "$(ALL_IFILES)" "" + $(SILENT) $(MAKE) prebuild_gen_headers +endif + $(SILENT) $(TOUCH) $(OBJROOT)/$(NAME).build/Headers.touch + +# First try was not using -framework, so we get EXTERN_API to leverage for __declspec trickery. +# But that didn't help us for externed data, and the imports changed to omit the framework name. +# As best I can tell, when not using -framework you need to cd into the IFILES_DIR for the +# inter-file references to work. +# -update and -deepUpdate don't seem to work on WIN32, so just use a touch file +#ALL_IFILES = $(PUBLIC_IFILES) $(PRIVATE_IFILES) +#PLATFORM_IFLAGS = $(foreach F, $(ALL_IFILES), `cygpath -w $(F)`) +PLATFORM_IFLAGS = -framework $(NAME) -frameworkInterfaces `cygpath -w $(IFILES_DIR)/` +endif +prebuild_gen_headers: + $(SILENT) $(ECHO) "Processing interface files..." + $(SILENT) $(INTERFACER) $(PLATFORM_IFLAGS) -c -rez -update \ + -masterInterfaces `cygpath -w $(MASTER_INTERFACE_DIR)/` \ + -cacheFolder `cygpath -w $(OBJROOT)/$(NAME).build/InterfacerCache/` \ + -generated c=`cygpath -w $(PUBLIC_HEADER_DIR)/` \ + -generatedPriv c=`cygpath -w $(PRIVATE_HEADER_DIR)/` \ + -generated rez=`cygpath -w $(PUBLIC_HEADER_DIR)/` \ + -generatedPriv rez=`cygpath -w $(PRIVATE_HEADER_DIR)/` +ifeq "$(PLATFORM)" "CYGWIN" +# Replace externs with a symbol we can use for declspec purposes, except not extern "C" +# Get rid of non-standard pragma + $(SILENT) perl -p -i \ + -e 's/^extern ([^"].[^"])/$(NAME)_EXPORT $$1/ ;' \ + -e 's/^(#pragma options)/\/\/$$1/' \ + $(PUBLIC_HEADER_DIR)/*.h $(PRIVATE_HEADER_DIR)/*.h #*/ + $(SILENT) $(REMOVE) -f $(PUBLIC_HEADER_DIR)/*.bak $(PRIVATE_HEADER_DIR)/*.bak #*/ +endif + +# This is the line from a CFNetwork build in PB +# /AppleInternal/Developer/Tools/Interfacer -masterInterfaces "/AppleInternal/Carbon/interfaces/" -cacheFolder "/Volumes/Whopper/symroots/CFNetwork.build/CFNetwork.build/InterfacerCache/" -c -rez -framework "CFNetwork" -p -generated "c=/Volumes/Whopper/symroots/CFNetwork.framework/Versions/A/Headers/" -generatedPriv "c=/Volumes/Whopper/symroots/CFNetwork.framework/Versions/A/PrivateHeaders/" -generated "rez=/Volumes/Whopper/symroots/CFNetwork.framework/Versions/A/Headers/" -generatedPriv "rez=/Volumes/Whopper/symroots/CFNetwork.framework/Versions/A/PrivateHeaders/" -frameworkInterfaces /Volumes/Whale/trey/CFNetwork-Windows/Interfaces/ -installMasterInterfaces /tmp/CFNetwork.dst/AppleInternal/Carbon/interfaces/ + + +prebuild_copy_headers: + $(SILENT) $(ECHO) "Copying headers..." +ifneq "$(strip $(PUBLIC_HFILES))" "" + $(SILENT) $(COPY) $(PUBLIC_HFILES) $(PUBLIC_HEADER_DIR) +endif +ifneq "$(strip $(PRIVATE_HFILES))" "" + $(SILENT) $(COPY) $(PRIVATE_HFILES) $(PRIVATE_HEADER_DIR) +endif +ifneq "$(strip $(PROJECT_HFILES))" "" + $(SILENT) $(COPY) $(PROJECT_HFILES) $(PROJECT_HEADER_DIR) +endif + + +#-------------------------------------------------------------------------------- +# BUILD +#-------------------------------------------------------------------------------- + +# ??? should use VPATH, should use generic rules +# ??? should use cc -MM to generate dependencies +# ??? should separate private from project headers, for proper installation + +# Set some parameters of the build-realwork target, then call it with a recursive make +release-build: + $(SILENT) $(MAKE) \ + BUILD_TYPE=release \ + BUILD_PRODUCT=$(RELEASE_LIB) \ + BUILD_IMPLIB=$(RELEASE_IMPLIB) \ + OTHER_CFLAGS="-O $(OTHER_CFLAGS)" \ + OTHER_CPPFLAGS="-O $(OTHER_CPPFLAGS)" \ + OTHER_LFLAGS="-O $(OTHER_LFLAGS)" \ + build-realwork +debug-build: + $(SILENT) $(MAKE) \ + BUILD_TYPE=debug \ + BUILD_PRODUCT=$(DEBUG_LIB) \ + BUILD_IMPLIB=$(DEBUG_IMPLIB) \ + LIBRARY_SUFFIX=_debug \ + OTHER_CFLAGS="-DDEBUG -g $(OTHER_CFLAGS)" \ + OTHER_CPPFLAGS="-DDEBUG -g $(OTHER_CPPFLAGS)" \ + OTHER_LFLAGS="-g $(OTHER_LFLAGS)" \ + build-realwork +profile-build: + $(SILENT) $(MAKE) \ + BUILD_TYPE=profile \ + BUILD_PRODUCT=$(PROFILE_LIB) \ + BUILD_IMPLIB=$(PROFILE_IMPLIB) \ + LIBRARY_SUFFIX=_profile \ + OTHER_CFLAGS="-DPROFILE -pg -O $(OTHER_CFLAGS)" \ + OTHER_CPPFLAGS="-DPROFILE -pg -O $(OTHER_CPPFLAGS)" \ + OTHER_LFLAGS="-pg -O $(OTHER_LFLAGS)" \ + build-realwork + +OFILE_DIR = $(OBJROOT)/$(NAME).build/$(BUILD_TYPE)_ofiles + +build-realwork: check-vars-defined compile-before build-compile compile-after build-link +compile-before:: +compile-after:: + +build-compile: + $(SILENT) $(ECHO) "Building $(BUILD_TYPE)..." + $(SILENT) $(MKDIRS) $(OFILE_DIR) + $(SILENT) cumulativeError=0; \ + for x in $(CFILES) ; do \ + ofile=$(OFILE_DIR)/`basename $$x .c`.o ; \ + if [ ! $$ofile -nt $$x ] ; then \ + $(ECHO) " ..." $$x " ($(BUILD_TYPE))" ; \ + $(CC) $(CFLAGS) -c $$x -o $$ofile ; \ + ccError=$$? ; \ + if [ $$ccError != 0 ] ; then cumulativeError=$$ccError; fi;\ + fi ; \ + done; \ + exit $$cumulativeError + $(SILENT) cumulativeError=0; \ + for x in $(CPP_FILES) ; do \ + ofile=$(OFILE_DIR)/`basename $$x .c`.o ; \ + if [ ! $$ofile -nt $$x ] ; then \ + $(ECHO) " ..." $$x " ($(BUILD_TYPE))" ; \ + $(CPLUSPLUS) $(CPPFLAGS) -c $$x -o $$ofile ; \ + ccError=$$? ; \ + if [ $$ccError != 0 ] ; then cumulativeError=$$ccError; fi;\ + fi ; \ + done; \ + exit $$cumulativeError + +ifeq "$(CPP_FILES)" "" +LINKER_CMD = $(CC) +else +LINKER_CMD = $(CPLUSPLUS) +endif + +build-link: + $(SILENT) $(ECHO) "Linking..." +ifeq "$(PLATFORM)" "Darwin" + $(SILENT) $(LINKER_CMD) $(LFLAGS) -O -install_name $(FRAMEWORK_DIR)/Versions/A/$(BUILD_PRODUCT) $(LIBS) -o $(SYMROOT)/$(NAME).framework/Versions/A/$(BUILD_PRODUCT) $(OFILE_DIR)/*.o #*/ + $(SILENT) $(SYMLINK) Versions/Current/$(BUILD_PRODUCT) $(SYMROOT)/$(NAME).framework/$(BUILD_PRODUCT) +endif +ifeq "$(PLATFORM)" "Linux" + $(SILENT) $(ECHO) "NOTE: Producing static libraries on Linux" + $(SILENT) ar cr $(SYMROOT)/$(BUILD_PRODUCT) $(OFILE_DIR)/*.o #*/ +endif +ifeq "$(PLATFORM)" "FreeBSD" + $(SILENT) $(LINKER_CMD) $(LFLAGS) -O -o $(SYMROOT)/$(BUILD_PRODUCT) $(OFILE_DIR)/*.o $(LIBS) #*/ +endif +ifeq "$(PLATFORM)" "CYGWIN" + $(SILENT) $(DLLTOOL) --no-export-all-symbols -z $(SYMROOT)/$(BUILD_PRODUCT:.dll=.defs) -e $(OFILE_DIR)/$(BUILD_PRODUCT:.dll=.exports.o) -l $(SYMROOT)/$(BUILD_IMPLIB) -D $(BUILD_PRODUCT) $(OFILE_DIR)/*.o #*/ + $(SILENT) $(LINKER_CMD) $(LFLAGS) -mdll $(OFILE_DIR)/*.o $(OFILE_DIR)/$(BUILD_PRODUCT:.dll=.exports.o) $(LIBS) -o $(SYMROOT)/$(BUILD_PRODUCT) #*/ +# generate a MS VC compatible import library + $(SILENT) if [ "$$MSVCDIR" != "" ] ; then \ + defFile=`cygpath -w $(SYMROOT)/$(BUILD_PRODUCT:.dll=.defs)`; \ + outFile=`cygpath -w $(SYMROOT)/$(BUILD_PRODUCT:.dll=.lib)`; \ + cmd /C "$$MSVCDIR\BIN\VCVARS32" "&&" lib /MACHINE:i386 "/DEF:$$defFile" "/OUT:$$outFile"; \ + else \ + $(ECHO) WARNING: \$$MSVCDIR is not set - no MS Visual C++ compatible import lib will be generated; \ + fi +endif + $(SILENT) $(ECHO) "Done!" + +# Make sure a couple variables are defined. +check-vars-defined: + $(SILENT) if [ "" = "$(BUILD_TYPE)" ] || [ "" = "$(BUILD_PRODUCT)" ]; then \ + echo ERROR: That target cannot be directly invoked. It is used only internally for recursive makes.; \ + exit 1; \ + fi diff --git a/version.c b/version.c index c86fd5c..2e80f3e 100644 --- a/version.c +++ b/version.c @@ -1,10 +1,8 @@ /* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005 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 @@ -22,6 +20,9 @@ * * @APPLE_LICENSE_HEADER_END@ */ +/* + Note that this file is only used to build the CFLite version of CF using the makefile, but not the version that ships with OS X. + */ #define _STRINGIFY(X) #X #define STRINGIFY(V) _STRINGIFY(V) -- 2.45.2