|
Wired Foundation
2.0
A foundation framework for the Wired implementation on Mac OS X
|
00001 // 00002 // RegexKitLite.m 00003 // http://regexkit.sourceforge.net/ 00004 // Licensed under the terms of the BSD License, as specified below. 00005 // 00006 00007 /* 00008 Copyright (c) 2008-2010, John Engelhart 00009 00010 All rights reserved. 00011 00012 Redistribution and use in source and binary forms, with or without 00013 modification, are permitted provided that the following conditions are met: 00014 00015 * Redistributions of source code must retain the above copyright 00016 notice, this list of conditions and the following disclaimer. 00017 00018 * Redistributions in binary form must reproduce the above copyright 00019 notice, this list of conditions and the following disclaimer in the 00020 documentation and/or other materials provided with the distribution. 00021 00022 * Neither the name of the Zang Industries nor the names of its 00023 contributors may be used to endorse or promote products derived from 00024 this software without specific prior written permission. 00025 00026 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 00027 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 00028 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 00029 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 00030 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 00031 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 00032 TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 00033 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 00034 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 00035 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 00036 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 00037 */ 00038 00039 #include <CoreFoundation/CFBase.h> 00040 #include <CoreFoundation/CFArray.h> 00041 #include <CoreFoundation/CFString.h> 00042 #import <Foundation/NSArray.h> 00043 #import <Foundation/NSDictionary.h> 00044 #import <Foundation/NSError.h> 00045 #import <Foundation/NSException.h> 00046 #import <Foundation/NSNotification.h> 00047 #import <Foundation/NSRunLoop.h> 00048 #ifdef __OBJC_GC__ 00049 #import <Foundation/NSGarbageCollector.h> 00050 #define RKL_STRONG_REF __strong 00051 #define RKL_GC_VOLATILE volatile 00052 #else // __OBJC_GC__ 00053 #define RKL_STRONG_REF 00054 #define RKL_GC_VOLATILE 00055 #endif // __OBJC_GC__ 00056 00057 #if (defined(TARGET_OS_EMBEDDED) && (TARGET_OS_EMBEDDED != 0)) || (defined(TARGET_OS_IPHONE) && (TARGET_OS_IPHONE != 0)) || (defined(MAC_OS_X_VERSION_MIN_REQUIRED) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1050)) 00058 #include <objc/runtime.h> 00059 #else 00060 #include <objc/objc-runtime.h> 00061 #endif 00062 00063 #include <libkern/OSAtomic.h> 00064 #include <mach-o/loader.h> 00065 #include <AvailabilityMacros.h> 00066 #include <dlfcn.h> 00067 #include <string.h> 00068 #include <stdarg.h> 00069 #include <stdlib.h> 00070 #include <stdio.h> 00071 00072 #import "RegexKitLite.h" 00073 00074 // If the gcc flag -mmacosx-version-min is used with, for example, '=10.2', give a warning that the libicucore.dylib is only available on >= 10.3. 00075 // If you are reading this comment because of this warning, this is to let you know that linking to /usr/lib/libicucore.dylib will cause your executable to fail on < 10.3. 00076 // You will need to build your own version of the ICU library and link to that in order for RegexKitLite to work successfully on < 10.3. This is not simple. 00077 00078 #if MAC_OS_X_VERSION_MIN_REQUIRED < 1030 00079 #warning The ICU dynamic shared library, /usr/lib/libicucore.dylib, is only available on Mac OS X 10.3 and later. 00080 #warning You will need to supply a version of the ICU library to use RegexKitLite on Mac OS X 10.2 and earlier. 00081 #endif 00082 00084 #pragma mark Compile time tunables 00085 00086 #ifndef RKL_CACHE_SIZE 00087 #define RKL_CACHE_SIZE (13UL) 00088 #endif 00089 00090 #if RKL_CACHE_SIZE < 1 00091 #error RKL_CACHE_SIZE must be a non-negative number greater than 0. 00092 #endif // RKL_CACHE_SIZE < 1 00093 00094 #ifndef RKL_FIXED_LENGTH 00095 #define RKL_FIXED_LENGTH (2048UL) 00096 #endif 00097 00098 #if RKL_FIXED_LENGTH < 1 00099 #error RKL_FIXED_LENGTH must be a non-negative number greater than 0. 00100 #endif // RKL_FIXED_LENGTH < 1 00101 00102 #ifndef RKL_STACK_LIMIT 00103 #define RKL_STACK_LIMIT (128UL * 1024UL) 00104 #endif 00105 00106 #if RKL_STACK_LIMIT < 0 00107 #error RKL_STACK_LIMIT must be a non-negative number. 00108 #endif // RKL_STACK_LIMIT < 0 00109 00110 #ifdef RKL_APPEND_TO_ICU_FUNCTIONS 00111 #define RKL_ICU_FUNCTION_APPEND(x) _RKL_CONCAT(x, RKL_APPEND_TO_ICU_FUNCTIONS) 00112 #else // RKL_APPEND_TO_ICU_FUNCTIONS 00113 #define RKL_ICU_FUNCTION_APPEND(x) x 00114 #endif // RKL_APPEND_TO_ICU_FUNCTIONS 00115 00116 #if defined(RKL_DTRACE) && (RKL_DTRACE != 0) 00117 #define _RKL_DTRACE_ENABLED 1 00118 #endif // defined(RKL_DTRACE) && (RKL_DTRACE != 0) 00119 00120 // These are internal, non-public tunables. 00121 #define _RKL_FIXED_LENGTH ((NSUInteger)RKL_FIXED_LENGTH) 00122 #define _RKL_STACK_LIMIT ((NSUInteger)RKL_STACK_LIMIT) 00123 #define _RKL_SCRATCH_BUFFERS (5UL) 00124 #if _RKL_SCRATCH_BUFFERS != 5 00125 #error _RKL_SCRATCH_BUFFERS is not tunable, it must be set to 5. 00126 #endif // _RKL_SCRATCH_BUFFERS != 5 00127 #define _RKL_PREFETCH_SIZE (64UL) 00128 #define _RKL_DTRACE_REGEXUTF8_SIZE (64UL) 00129 00130 // A LRU Cache Set holds 4 lines, and the LRU algorithm uses 4 bits per line. 00131 // A LRU Cache Set has a type of RKLLRUCacheSet_t and is 16 bits wide (4 lines * 4 bits per line). 00132 // RKLLRUCacheSet_t must be initialized to a value of 0x0137 in order to work correctly. 00133 typedef uint16_t RKLLRUCacheSet_t; 00134 #define _RKL_LRU_CACHE_SET_INIT ((RKLLRUCacheSet_t)0x0137U) 00135 #define _RKL_LRU_CACHE_SET_WAYS (4UL) 00136 #if _RKL_LRU_CACHE_SET_WAYS != 4 00137 #error _RKL_LRU_CACHE_SET_WAYS is not tunable, it must be set to 4. 00138 #endif // _RKL_LRU_CACHE_SET_WAYS != 4 00139 00140 #define _RKL_REGEX_LRU_CACHE_SETS ((NSUInteger)(RKL_CACHE_SIZE)) 00141 #define _RKL_REGEX_CACHE_LINES ((NSUInteger)((NSUInteger)(_RKL_REGEX_LRU_CACHE_SETS) * (NSUInteger)(_RKL_LRU_CACHE_SET_WAYS))) 00142 00143 // Regex String Lookaside Cache parameters. 00144 #define _RKL_REGEX_LOOKASIDE_CACHE_BITS (6UL) 00145 #if _RKL_REGEX_LOOKASIDE_CACHE_BITS < 0 00146 #error _RKL_REGEX_LOOKASIDE_CACHE_BITS must be a non-negative number and is not intended to be user tunable. 00147 #endif // _RKL_REGEX_LOOKASIDE_CACHE_BITS < 0 00148 #define _RKL_REGEX_LOOKASIDE_CACHE_SIZE (1LU << _RKL_REGEX_LOOKASIDE_CACHE_BITS) 00149 #define _RKL_REGEX_LOOKASIDE_CACHE_MASK ((1LU << _RKL_REGEX_LOOKASIDE_CACHE_BITS) - 1LU) 00150 // RKLLookasideCache_t should be large enough to to hold the maximum number of cached regexes, or (RKL_CACHE_SIZE * _RKL_LRU_CACHE_SET_WAYS). 00151 #if (RKL_CACHE_SIZE * _RKL_LRU_CACHE_SET_WAYS) <= (1 << 8) 00152 typedef uint8_t RKLLookasideCache_t; 00153 #elif (RKL_CACHE_SIZE * _RKL_LRU_CACHE_SET_WAYS) <= (1 << 16) 00154 typedef uint16_t RKLLookasideCache_t; 00155 #else // (RKL_CACHE_SIZE * _RKL_LRU_CACHE_SET_WAYS) > (1 << 16) 00156 typedef uint32_t RKLLookasideCache_t; 00157 #endif // (RKL_CACHE_SIZE * _RKL_LRU_CACHE_SET_WAYS) 00158 00160 #pragma mark - 00161 #pragma mark GCC / Compiler macros 00162 00163 #if defined (__GNUC__) && (__GNUC__ >= 4) 00164 #define RKL_ATTRIBUTES(attr, ...) __attribute__((attr, ##__VA_ARGS__)) 00165 #define RKL_EXPECTED(cond, expect) __builtin_expect((long)(cond), (expect)) 00166 #define RKL_PREFETCH(ptr) __builtin_prefetch(ptr) 00167 #define RKL_PREFETCH_UNICHAR(ptr, off) { const char *p = ((const char *)(ptr)) + ((off) * sizeof(UniChar)) + _RKL_PREFETCH_SIZE; RKL_PREFETCH(p); RKL_PREFETCH(p + _RKL_PREFETCH_SIZE); } 00168 #define RKL_HAVE_CLEANUP 00169 #define RKL_CLEANUP(func) RKL_ATTRIBUTES(cleanup(func)) 00170 #else // defined (__GNUC__) && (__GNUC__ >= 4) 00171 #define RKL_ATTRIBUTES(attr, ...) 00172 #define RKL_EXPECTED(cond, expect) (cond) 00173 #define RKL_PREFETCH(ptr) 00174 #define RKL_PREFETCH_UNICHAR(ptr, off) 00175 #define RKL_CLEANUP(func) 00176 #endif // defined (__GNUC__) && (__GNUC__ >= 4) 00177 00178 #define RKL_STATIC_INLINE static __inline__ RKL_ATTRIBUTES(always_inline) 00179 #define RKL_ALIGNED(arg) RKL_ATTRIBUTES(aligned(arg)) 00180 #define RKL_UNUSED_ARG RKL_ATTRIBUTES(unused) 00181 #define RKL_WARN_UNUSED RKL_ATTRIBUTES(warn_unused_result) 00182 #define RKL_WARN_UNUSED_CONST RKL_ATTRIBUTES(warn_unused_result, const) 00183 #define RKL_WARN_UNUSED_PURE RKL_ATTRIBUTES(warn_unused_result, pure) 00184 #define RKL_WARN_UNUSED_SENTINEL RKL_ATTRIBUTES(warn_unused_result, sentinel) 00185 #define RKL_NONNULL_ARGS(arg, ...) RKL_ATTRIBUTES(nonnull(arg, ##__VA_ARGS__)) 00186 #define RKL_WARN_UNUSED_NONNULL_ARGS(arg, ...) RKL_ATTRIBUTES(warn_unused_result, nonnull(arg, ##__VA_ARGS__)) 00187 #define RKL_WARN_UNUSED_CONST_NONNULL_ARGS(arg, ...) RKL_ATTRIBUTES(warn_unused_result, const, nonnull(arg, ##__VA_ARGS__)) 00188 #define RKL_WARN_UNUSED_PURE_NONNULL_ARGS(arg, ...) RKL_ATTRIBUTES(warn_unused_result, pure, nonnull(arg, ##__VA_ARGS__)) 00189 00190 #if defined (__GNUC__) && (__GNUC__ >= 4) && (__GNUC_MINOR__ >= 3) 00191 #define RKL_ALLOC_SIZE_NON_NULL_ARGS_WARN_UNUSED(as, nn, ...) RKL_ATTRIBUTES(warn_unused_result, nonnull(nn, ##__VA_ARGS__), alloc_size(as)) 00192 #else // defined (__GNUC__) && (__GNUC__ >= 4) && (__GNUC_MINOR__ >= 3) 00193 #define RKL_ALLOC_SIZE_NON_NULL_ARGS_WARN_UNUSED(as, nn, ...) RKL_ATTRIBUTES(warn_unused_result, nonnull(nn, ##__VA_ARGS__)) 00194 #endif // defined (__GNUC__) && (__GNUC__ >= 4) && (__GNUC_MINOR__ >= 3) 00195 00196 #ifdef _RKL_DTRACE_ENABLED 00197 #define RKL_UNUSED_DTRACE_ARG 00198 #else // _RKL_DTRACE_ENABLED 00199 #define RKL_UNUSED_DTRACE_ARG RKL_ATTRIBUTES(unused) 00200 #endif // _RKL_DTRACE_ENABLED 00201 00203 #pragma mark - 00204 #pragma mark Assertion macros 00205 00206 // These macros are nearly identical to their NSCParameterAssert siblings. 00207 // This is required because nearly everything is done while rkl_cacheSpinLock is locked. 00208 // We need to safely unlock before throwing any of these exceptions. 00209 // @try {} @finally {} significantly slows things down so it's not used. 00210 00211 #define RKLCHardAbortAssert(c) do { int _c=(c); if(RKL_EXPECTED(!_c, 0L)) { NSLog(@"%@:%ld: Invalid parameter not satisfying: %s\n", [NSString stringWithUTF8String:__FILE__], (long)__LINE__, #c); abort(); } } while(0) 00212 #define RKLCAssertDictionary(d, ...) rkl_makeAssertDictionary(__PRETTY_FUNCTION__, __FILE__, __LINE__, (d), ##__VA_ARGS__) 00213 #define RKLCDelayedHardAssert(c, e, g) do { id *_e=(e); int _c=(c); if(RKL_EXPECTED(_e == NULL, 0L) || RKL_EXPECTED(*_e != NULL, 0L)) { goto g; } if(RKL_EXPECTED(!_c, 0L)) { *_e = RKLCAssertDictionary(@"Invalid parameter not satisfying: %s", #c); goto g; } } while(0) 00214 00215 #ifdef NS_BLOCK_ASSERTIONS 00216 #define RKLCAbortAssert(c) 00217 #define RKLCDelayedAssert(c, e, g) 00218 #define RKL_UNUSED_ASSERTION_ARG RKL_ATTRIBUTES(unused) 00219 #else // NS_BLOCK_ASSERTIONS 00220 #define RKLCAbortAssert(c) RKLCHardAbortAssert(c) 00221 #define RKLCDelayedAssert(c, e, g) RKLCDelayedHardAssert(c, e, g) 00222 #define RKL_UNUSED_ASSERTION_ARG 00223 #endif // NS_BLOCK_ASSERTIONS 00224 00225 #define RKL_EXCEPTION(e, f, ...) [NSException exceptionWithName:(e) reason:rkl_stringFromClassAndMethod((self), (_cmd), (f), ##__VA_ARGS__) userInfo:NULL] 00226 #define RKL_RAISE_EXCEPTION(e, f, ...) [RKL_EXCEPTION(e, f, ##__VA_ARGS__) raise] 00227 00229 #pragma mark - 00230 #pragma mark Utility functions and macros 00231 00232 RKL_STATIC_INLINE BOOL NSRangeInsideRange(NSRange cin, NSRange win) RKL_WARN_UNUSED; 00233 RKL_STATIC_INLINE BOOL NSRangeInsideRange(NSRange cin, NSRange win) { return((((cin.location - win.location) <= win.length) && ((NSMaxRange(cin) - win.location) <= win.length)) ? YES : NO); } 00234 00235 #define NSMakeRange(loc, len) ((NSRange){.location=(NSUInteger)(loc), .length=(NSUInteger)(len)}) 00236 #define CFMakeRange(loc, len) ((CFRange){.location= (CFIndex)(loc), .length= (CFIndex)(len)}) 00237 #define NSNotFoundRange ((NSRange){.location=(NSUInteger)NSNotFound, .length= 0UL}) 00238 #define NSMaxiumRange ((NSRange){.location= 0UL, .length= NSUIntegerMax}) 00239 // These values are used to help tickle improper usage. 00240 #define RKLIllegalRange ((NSRange){.location= NSIntegerMax, .length= NSIntegerMax}) 00241 #define RKLIllegalPointer ((void * RKL_GC_VOLATILE)0xBAD0C0DE) 00242 00244 #pragma mark - 00245 #pragma mark Exported NSString symbols for exception names, error domains, error keys, etc 00246 00247 NSString * const RKLICURegexException = @"RKLICURegexException"; 00248 00249 NSString * const RKLICURegexErrorDomain = @"RKLICURegexErrorDomain"; 00250 00251 NSString * const RKLICURegexEnumerationOptionsErrorKey = @"RKLICURegexEnumerationOptions"; 00252 NSString * const RKLICURegexErrorCodeErrorKey = @"RKLICURegexErrorCode"; 00253 NSString * const RKLICURegexErrorNameErrorKey = @"RKLICURegexErrorName"; 00254 NSString * const RKLICURegexLineErrorKey = @"RKLICURegexLine"; 00255 NSString * const RKLICURegexOffsetErrorKey = @"RKLICURegexOffset"; 00256 NSString * const RKLICURegexPreContextErrorKey = @"RKLICURegexPreContext"; 00257 NSString * const RKLICURegexPostContextErrorKey = @"RKLICURegexPostContext"; 00258 NSString * const RKLICURegexRegexErrorKey = @"RKLICURegexRegex"; 00259 NSString * const RKLICURegexRegexOptionsErrorKey = @"RKLICURegexRegexOptions"; 00260 NSString * const RKLICURegexReplacedCountErrorKey = @"RKLICURegexReplacedCount"; 00261 NSString * const RKLICURegexReplacedStringErrorKey = @"RKLICURegexReplacedString"; 00262 NSString * const RKLICURegexReplacementStringErrorKey = @"RKLICURegexReplacementString"; 00263 NSString * const RKLICURegexSubjectRangeErrorKey = @"RKLICURegexSubjectRange"; 00264 NSString * const RKLICURegexSubjectStringErrorKey = @"RKLICURegexSubjectString"; 00265 00266 // Used internally by rkl_userInfoDictionary to specify which arguments should be set in the NSError userInfo dictionary. 00267 enum { 00268 RKLUserInfoNone = 0UL, 00269 RKLUserInfoSubjectRange = 1UL << 0, 00270 RKLUserInfoReplacedCount = 1UL << 1, 00271 RKLUserInfoRegexEnumerationOptions = 1UL << 2, 00272 }; 00273 typedef NSUInteger RKLUserInfoOptions; 00274 00276 #pragma mark - 00277 #pragma mark Type / struct definitions 00278 00279 // In general, the ICU bits and pieces here must exactly match the definition in the ICU sources. 00280 00281 #define U_STRING_NOT_TERMINATED_WARNING -124 00282 #define U_ZERO_ERROR 0 00283 #define U_INDEX_OUTOFBOUNDS_ERROR 8 00284 #define U_BUFFER_OVERFLOW_ERROR 15 00285 #define U_PARSE_CONTEXT_LEN 16 00286 00287 typedef struct uregex uregex; // Opaque ICU regex type. 00288 00289 typedef struct UParseError { // This must be exactly the same as the 'real' ICU declaration. 00290 int32_t line; 00291 int32_t offset; 00292 UniChar preContext[U_PARSE_CONTEXT_LEN]; 00293 UniChar postContext[U_PARSE_CONTEXT_LEN]; 00294 } UParseError; 00295 00296 // For use with GCC's cleanup() __attribute__. 00297 enum { 00298 RKLLockedCacheSpinLock = 1UL << 0, 00299 RKLUnlockedCacheSpinLock = 1UL << 1, 00300 }; 00301 00302 enum { 00303 RKLSplitOp = 1UL, 00304 RKLReplaceOp = 2UL, 00305 RKLRangeOp = 3UL, 00306 RKLArrayOfStringsOp = 4UL, 00307 RKLArrayOfCapturesOp = 5UL, 00308 RKLCapturesArrayOp = 6UL, 00309 RKLDictionaryOfCapturesOp = 7UL, 00310 RKLArrayOfDictionariesOfCapturesOp = 8UL, 00311 RKLMaskOp = 0xFUL, 00312 RKLReplaceMutable = 1UL << 4, 00313 RKLSubcapturesArray = 1UL << 5, 00314 }; 00315 typedef NSUInteger RKLRegexOp; 00316 00317 enum { 00318 RKLBlockEnumerationMatchOp = 1UL, 00319 RKLBlockEnumerationReplaceOp = 2UL, 00320 }; 00321 typedef NSUInteger RKLBlockEnumerationOp; 00322 00323 typedef struct { 00324 RKL_STRONG_REF NSRange * RKL_GC_VOLATILE ranges; 00325 NSRange findInRange, remainingRange; 00326 NSInteger capacity, found, findUpTo, capture, addedSplitRanges; 00327 size_t size, stackUsed; 00328 RKL_STRONG_REF void ** RKL_GC_VOLATILE rangesScratchBuffer; 00329 RKL_STRONG_REF void ** RKL_GC_VOLATILE stringsScratchBuffer; 00330 RKL_STRONG_REF void ** RKL_GC_VOLATILE arraysScratchBuffer; 00331 RKL_STRONG_REF void ** RKL_GC_VOLATILE dictionariesScratchBuffer; 00332 RKL_STRONG_REF void ** RKL_GC_VOLATILE keysScratchBuffer; 00333 } RKLFindAll; 00334 00335 typedef struct { 00336 CFStringRef string; 00337 CFHashCode hash; 00338 CFIndex length; 00339 RKL_STRONG_REF UniChar * RKL_GC_VOLATILE uniChar; 00340 } RKLBuffer; 00341 00342 typedef struct { 00343 CFStringRef regexString; 00344 CFHashCode regexHash; 00345 RKLRegexOptions options; 00346 uregex *icu_regex; 00347 NSInteger captureCount; 00348 00349 CFStringRef setToString; 00350 CFHashCode setToHash; 00351 CFIndex setToLength; 00352 NSUInteger setToIsImmutable:1; 00353 NSUInteger setToNeedsConversion:1; 00354 RKL_STRONG_REF const UniChar * RKL_GC_VOLATILE setToUniChar; 00355 NSRange setToRange, lastFindRange, lastMatchRange; 00356 00357 RKLBuffer *buffer; 00358 } RKLCachedRegex; 00359 00361 #pragma mark - 00362 #pragma mark Translation unit scope global variables 00363 00364 static RKLLRUCacheSet_t rkl_lruFixedBufferCacheSet = _RKL_LRU_CACHE_SET_INIT, rkl_lruDynamicBufferCacheSet = _RKL_LRU_CACHE_SET_INIT; 00365 static RKLBuffer rkl_lruDynamicBuffer[_RKL_LRU_CACHE_SET_WAYS]; 00366 static UniChar rkl_lruFixedUniChar[_RKL_LRU_CACHE_SET_WAYS][_RKL_FIXED_LENGTH]; // This is the fixed sized UTF-16 conversion buffer. 00367 static RKLBuffer rkl_lruFixedBuffer[_RKL_LRU_CACHE_SET_WAYS] = {{NULL, 0UL, 0L, &rkl_lruFixedUniChar[0][0]}, {NULL, 0UL, 0L, &rkl_lruFixedUniChar[1][0]}, {NULL, 0UL, 0L, &rkl_lruFixedUniChar[2][0]}, {NULL, 0UL, 0L, &rkl_lruFixedUniChar[3][0]}}; 00368 static RKLCachedRegex rkl_cachedRegexes[_RKL_REGEX_CACHE_LINES]; 00369 #if defined(__GNUC__) && (__GNUC__ == 4) && defined(__GNUC_MINOR__) && (__GNUC_MINOR__ == 2) 00370 static RKLCachedRegex * volatile rkl_lastCachedRegex; // XXX This is a work around for what appears to be a optimizer code generation bug in GCC 4.2. 00371 #else 00372 static RKLCachedRegex *rkl_lastCachedRegex; 00373 #endif // defined(__GNUC__) && (__GNUC__ == 4) && defined(__GNUC_MINOR__) && (__GNUC_MINOR__ == 2) 00374 static RKLLRUCacheSet_t rkl_cachedRegexCacheSets[_RKL_REGEX_LRU_CACHE_SETS] = { [0 ... (_RKL_REGEX_LRU_CACHE_SETS - 1UL)] = _RKL_LRU_CACHE_SET_INIT }; 00375 static RKLLookasideCache_t rkl_regexLookasideCache[_RKL_REGEX_LOOKASIDE_CACHE_SIZE] RKL_ALIGNED(64); 00376 static OSSpinLock rkl_cacheSpinLock = OS_SPINLOCK_INIT; 00377 static const UniChar rkl_emptyUniCharString[1]; // For safety, icu_regexes are 'set' to this when the string they were searched is cleared. 00378 static RKL_STRONG_REF void * RKL_GC_VOLATILE rkl_scratchBuffer[_RKL_SCRATCH_BUFFERS]; // Used to hold temporary allocations that are allocated via reallocf(). 00379 00381 #pragma mark - 00382 #pragma mark CFArray and CFDictionary call backs 00383 00384 // These are used when running under manual memory management for the array that rkl_splitArray creates. 00385 // The split strings are created, but not autoreleased. The (immutable) array is created using these callbacks, which skips the CFRetain() call, effectively transferring ownership to the CFArray object. 00386 // For each split string this saves the overhead of an autorelease, then an array retain, then an NSAutoreleasePool release. This is good for a ~30% speed increase. 00387 00388 static void rkl_CFCallbackRelease(CFAllocatorRef allocator RKL_UNUSED_ARG, const void *ptr) { CFRelease((CFTypeRef)ptr); } 00389 static const CFArrayCallBacks rkl_transferOwnershipArrayCallBacks = { (CFIndex)0L, NULL, rkl_CFCallbackRelease, CFCopyDescription, CFEqual }; 00390 static const CFDictionaryKeyCallBacks rkl_transferOwnershipDictionaryKeyCallBacks = { (CFIndex)0L, NULL, rkl_CFCallbackRelease, CFCopyDescription, CFEqual, CFHash }; 00391 static const CFDictionaryValueCallBacks rkl_transferOwnershipDictionaryValueCallBacks = { (CFIndex)0L, NULL, rkl_CFCallbackRelease, CFCopyDescription, CFEqual }; 00392 00393 #ifdef __OBJC_GC__ 00394 00395 #pragma mark - 00396 #pragma mark Low-level Garbage Collection aware memory/resource allocation utilities 00397 // If compiled with Garbage Collection, we need to be able to do a few things slightly differently. 00398 // The basic premiss is that under GC we use a trampoline function pointer which is set to a _start function to catch the first invocation. 00399 // The _start function checks if GC is running and then overwrites the function pointer with the appropriate routine. Think of it as 'lazy linking'. 00400 00401 enum { RKLScannedOption = NSScannedOption }; 00402 00403 // rkl_collectingEnabled uses objc_getClass() to get the NSGarbageCollector class, which doesn't exist on earlier systems. 00404 // This allows for graceful failure should we find ourselves running on an earlier version of the OS without NSGarbageCollector. 00405 static BOOL rkl_collectingEnabled_first (void); 00406 static BOOL rkl_collectingEnabled_yes (void) { return(YES); } 00407 static BOOL rkl_collectingEnabled_no (void) { return(NO); } 00408 static BOOL(*rkl_collectingEnabled) (void) = rkl_collectingEnabled_first; 00409 static BOOL rkl_collectingEnabled_first (void) { 00410 BOOL gcEnabled = ([objc_getClass("NSGarbageCollector") defaultCollector] != NULL) ? YES : NO; 00411 if(gcEnabled == YES) { 00412 // This section of code is required due to what I consider to be a fundamental design flaw in Cocoas GC system. 00413 // Earlier versions of "Garbage Collection Programming Guide" stated that (paraphrased) "all globals are automatically roots". 00414 // Current versions of the guide now include the following warning: 00415 // "You may pass addresses of strong globals or statics into routines expecting pointers to object pointers (such as id* or NSError**) 00416 // only if they have first been assigned to directly, rather than through a pointer dereference." 00417 // This is a surprisingly non-trivial condition to actually meet in practice and is a recipe for impossible to debug race condition bugs. 00418 // We just happen to be very, very, very lucky in the fact that we can initialize our root set before the first use. 00419 NSUInteger x = 0UL; 00420 for(x = 0UL; x < _RKL_SCRATCH_BUFFERS; x++) { rkl_scratchBuffer[x] = NSAllocateCollectable(16UL, 0UL); rkl_scratchBuffer[x] = NULL; } 00421 for(x = 0UL; x < _RKL_LRU_CACHE_SET_WAYS; x++) { rkl_lruDynamicBuffer[x].uniChar = NSAllocateCollectable(16UL, 0UL); rkl_lruDynamicBuffer[x].uniChar = NULL; } 00422 } 00423 return((rkl_collectingEnabled = (gcEnabled == YES) ? rkl_collectingEnabled_yes : rkl_collectingEnabled_no)()); 00424 } 00425 00426 // rkl_realloc() 00427 static void *rkl_realloc_first (RKL_STRONG_REF void ** RKL_GC_VOLATILE ptr, size_t size, NSUInteger flags); 00428 static void *rkl_realloc_std (RKL_STRONG_REF void ** RKL_GC_VOLATILE ptr, size_t size, NSUInteger flags RKL_UNUSED_ARG) { return((*ptr = reallocf(*ptr, size))); } 00429 static void *rkl_realloc_gc (RKL_STRONG_REF void ** RKL_GC_VOLATILE ptr, size_t size, NSUInteger flags) { return((*ptr = NSReallocateCollectable(*ptr, (NSUInteger)size, flags))); } 00430 static void *(*rkl_realloc) (RKL_STRONG_REF void ** RKL_GC_VOLATILE ptr, size_t size, NSUInteger flags) RKL_ALLOC_SIZE_NON_NULL_ARGS_WARN_UNUSED(2,1) = rkl_realloc_first; 00431 static void *rkl_realloc_first (RKL_STRONG_REF void ** RKL_GC_VOLATILE ptr, size_t size, NSUInteger flags) { if(rkl_collectingEnabled()==YES) { rkl_realloc = rkl_realloc_gc; } else { rkl_realloc = rkl_realloc_std; } return(rkl_realloc(ptr, size, flags)); } 00432 00433 // rkl_free() 00434 static void * rkl_free_first (RKL_STRONG_REF void ** RKL_GC_VOLATILE ptr); 00435 static void * rkl_free_std (RKL_STRONG_REF void ** RKL_GC_VOLATILE ptr) { if(*ptr != NULL) { free(*ptr); *ptr = NULL; } return(NULL); } 00436 static void * rkl_free_gc (RKL_STRONG_REF void ** RKL_GC_VOLATILE ptr) { if(*ptr != NULL) { *ptr = NULL; } return(NULL); } 00437 static void *(*rkl_free) (RKL_STRONG_REF void ** RKL_GC_VOLATILE ptr) RKL_NONNULL_ARGS(1) = rkl_free_first; 00438 static void *rkl_free_first (RKL_STRONG_REF void ** RKL_GC_VOLATILE ptr) { if(rkl_collectingEnabled()==YES) { rkl_free = rkl_free_gc; } else { rkl_free = rkl_free_std; } return(rkl_free(ptr)); } 00439 00440 // rkl_CFAutorelease() 00441 static id rkl_CFAutorelease_first (CFTypeRef obj); 00442 static id rkl_CFAutorelease_std (CFTypeRef obj) { return([(id)obj autorelease]); } 00443 static id rkl_CFAutorelease_gc (CFTypeRef obj) { return(NSMakeCollectable(obj)); } 00444 static id(*rkl_CFAutorelease) (CFTypeRef obj) = rkl_CFAutorelease_first; 00445 static id rkl_CFAutorelease_first (CFTypeRef obj) { return((rkl_CFAutorelease = (rkl_collectingEnabled()==YES) ? rkl_CFAutorelease_gc : rkl_CFAutorelease_std)(obj)); } 00446 00447 // rkl_CreateStringWithSubstring() 00448 static id rkl_CreateStringWithSubstring_first (id string, NSRange range); 00449 static id rkl_CreateStringWithSubstring_std (id string, NSRange range) { return((id)CFStringCreateWithSubstring(NULL, (CFStringRef)string, CFMakeRange((CFIndex)range.location, (CFIndex)range.length))); } 00450 static id rkl_CreateStringWithSubstring_gc (id string, NSRange range) { return([string substringWithRange:range]); } 00451 static id(*rkl_CreateStringWithSubstring) (id string, NSRange range) RKL_WARN_UNUSED_NONNULL_ARGS(1) = rkl_CreateStringWithSubstring_first; 00452 static id rkl_CreateStringWithSubstring_first (id string, NSRange range) { return((rkl_CreateStringWithSubstring = (rkl_collectingEnabled()==YES) ? rkl_CreateStringWithSubstring_gc : rkl_CreateStringWithSubstring_std)(string, range)); } 00453 00454 // rkl_ReleaseObject() 00455 static id rkl_ReleaseObject_first (id obj); 00456 static id rkl_ReleaseObject_std (id obj) { CFRelease((CFTypeRef)obj); return(NULL); } 00457 static id rkl_ReleaseObject_gc (id obj RKL_UNUSED_ARG) { return(NULL); } 00458 static id (*rkl_ReleaseObject) (id obj) RKL_NONNULL_ARGS(1) = rkl_ReleaseObject_first; 00459 static id rkl_ReleaseObject_first (id obj) { return((rkl_ReleaseObject = (rkl_collectingEnabled()==YES) ? rkl_ReleaseObject_gc : rkl_ReleaseObject_std)(obj)); } 00460 00461 // rkl_CreateArrayWithObjects() 00462 static id rkl_CreateArrayWithObjects_first (void **objects, NSUInteger count); 00463 static id rkl_CreateArrayWithObjects_std (void **objects, NSUInteger count) { return((id)CFArrayCreate(NULL, (const void **)objects, (CFIndex)count, &rkl_transferOwnershipArrayCallBacks)); } 00464 static id rkl_CreateArrayWithObjects_gc (void **objects, NSUInteger count) { return([NSArray arrayWithObjects:(const id *)objects count:count]); } 00465 static id(*rkl_CreateArrayWithObjects) (void **objects, NSUInteger count) RKL_WARN_UNUSED_NONNULL_ARGS(1) = rkl_CreateArrayWithObjects_first; 00466 static id rkl_CreateArrayWithObjects_first (void **objects, NSUInteger count) { return((rkl_CreateArrayWithObjects = (rkl_collectingEnabled()==YES) ? rkl_CreateArrayWithObjects_gc : rkl_CreateArrayWithObjects_std)(objects, count)); } 00467 00468 // rkl_CreateAutoreleasedArray() 00469 static id rkl_CreateAutoreleasedArray_first (void **objects, NSUInteger count); 00470 static id rkl_CreateAutoreleasedArray_std (void **objects, NSUInteger count) { return((id)rkl_CFAutorelease(rkl_CreateArrayWithObjects(objects, count))); } 00471 static id rkl_CreateAutoreleasedArray_gc (void **objects, NSUInteger count) { return( rkl_CreateArrayWithObjects(objects, count) ); } 00472 static id(*rkl_CreateAutoreleasedArray) (void **objects, NSUInteger count) RKL_WARN_UNUSED_NONNULL_ARGS(1) = rkl_CreateAutoreleasedArray_first; 00473 static id rkl_CreateAutoreleasedArray_first (void **objects, NSUInteger count) { return((rkl_CreateAutoreleasedArray = (rkl_collectingEnabled()==YES) ? rkl_CreateAutoreleasedArray_gc : rkl_CreateAutoreleasedArray_std)(objects, count)); } 00474 00475 #else // __OBJC_GC__ not defined 00476 00477 #pragma mark - 00478 #pragma mark Low-level explicit memory/resource allocation utilities 00479 00480 enum { RKLScannedOption = 0 }; 00481 00482 #define rkl_collectingEnabled() (NO) 00483 00484 RKL_STATIC_INLINE void *rkl_realloc (void **ptr, size_t size, NSUInteger flags) RKL_ALLOC_SIZE_NON_NULL_ARGS_WARN_UNUSED(2,1); 00485 RKL_STATIC_INLINE void *rkl_free (void **ptr) RKL_NONNULL_ARGS(1); 00486 RKL_STATIC_INLINE id rkl_CFAutorelease (CFTypeRef obj) RKL_WARN_UNUSED_NONNULL_ARGS(1); 00487 RKL_STATIC_INLINE id rkl_CreateAutoreleasedArray (void **objects, NSUInteger count) RKL_WARN_UNUSED_NONNULL_ARGS(1); 00488 RKL_STATIC_INLINE id rkl_CreateArrayWithObjects (void **objects, NSUInteger count) RKL_WARN_UNUSED_NONNULL_ARGS(1); 00489 RKL_STATIC_INLINE id rkl_CreateStringWithSubstring (id string, NSRange range) RKL_WARN_UNUSED_NONNULL_ARGS(1); 00490 RKL_STATIC_INLINE id rkl_ReleaseObject (id obj) RKL_NONNULL_ARGS(1); 00491 00492 RKL_STATIC_INLINE void *rkl_realloc (void **ptr, size_t size, NSUInteger flags RKL_UNUSED_ARG) { return((*ptr = reallocf(*ptr, size))); } 00493 RKL_STATIC_INLINE void *rkl_free (void **ptr) { if(*ptr != NULL) { free(*ptr); *ptr = NULL; } return(NULL); } 00494 RKL_STATIC_INLINE id rkl_CFAutorelease (CFTypeRef obj) { return([(id)obj autorelease]); } 00495 RKL_STATIC_INLINE id rkl_CreateArrayWithObjects (void **objects, NSUInteger count) { return((id)CFArrayCreate(NULL, (const void **)objects, (CFIndex)count, &rkl_transferOwnershipArrayCallBacks)); } 00496 RKL_STATIC_INLINE id rkl_CreateAutoreleasedArray (void **objects, NSUInteger count) { return(rkl_CFAutorelease(rkl_CreateArrayWithObjects(objects, count))); } 00497 RKL_STATIC_INLINE id rkl_CreateStringWithSubstring (id string, NSRange range) { return((id)CFStringCreateWithSubstring(NULL, (CFStringRef)string, CFMakeRange((CFIndex)range.location, (CFIndex)range.length))); } 00498 RKL_STATIC_INLINE id rkl_ReleaseObject (id obj) { CFRelease((CFTypeRef)obj); return(NULL); } 00499 00500 #endif // __OBJC_GC__ 00501 00503 #pragma mark - 00504 #pragma mark ICU function prototypes 00505 00506 // ICU functions. See http://www.icu-project.org/apiref/icu4c/uregex_8h.html Tweaked slightly from the originals, but functionally identical. 00507 const char *RKL_ICU_FUNCTION_APPEND(u_errorName) ( int32_t status) RKL_WARN_UNUSED_PURE; 00508 int32_t RKL_ICU_FUNCTION_APPEND(u_strlen) (const UniChar *s) RKL_WARN_UNUSED_PURE_NONNULL_ARGS(1); 00509 int32_t RKL_ICU_FUNCTION_APPEND(uregex_appendReplacement) ( uregex *regexp, const UniChar *replacementText, int32_t replacementLength, UniChar **destBuf, int32_t *destCapacity, int32_t *status) RKL_WARN_UNUSED_NONNULL_ARGS(1,2,4,5,6); 00510 int32_t RKL_ICU_FUNCTION_APPEND(uregex_appendTail) ( uregex *regexp, UniChar **destBuf, int32_t *destCapacity, int32_t *status) RKL_WARN_UNUSED_NONNULL_ARGS(1,2,3,4); 00511 void RKL_ICU_FUNCTION_APPEND(uregex_close) ( uregex *regexp) RKL_NONNULL_ARGS(1); 00512 int32_t RKL_ICU_FUNCTION_APPEND(uregex_end) ( uregex *regexp, int32_t groupNum, int32_t *status) RKL_WARN_UNUSED_NONNULL_ARGS(1,3); 00513 BOOL RKL_ICU_FUNCTION_APPEND(uregex_find) ( uregex *regexp, int32_t location, int32_t *status) RKL_WARN_UNUSED_NONNULL_ARGS(1,3); 00514 BOOL RKL_ICU_FUNCTION_APPEND(uregex_findNext) ( uregex *regexp, int32_t *status) RKL_WARN_UNUSED_NONNULL_ARGS(1,2); 00515 int32_t RKL_ICU_FUNCTION_APPEND(uregex_groupCount) ( uregex *regexp, int32_t *status) RKL_WARN_UNUSED_NONNULL_ARGS(1,2); 00516 uregex *RKL_ICU_FUNCTION_APPEND(uregex_open) (const UniChar *pattern, int32_t patternLength, RKLRegexOptions flags, UParseError *parseError, int32_t *status) RKL_WARN_UNUSED_NONNULL_ARGS(1,4,5); 00517 void RKL_ICU_FUNCTION_APPEND(uregex_reset) ( uregex *regexp, int32_t newIndex, int32_t *status) RKL_NONNULL_ARGS(1,3); 00518 void RKL_ICU_FUNCTION_APPEND(uregex_setText) ( uregex *regexp, const UniChar *text, int32_t textLength, int32_t *status) RKL_NONNULL_ARGS(1,2,4); 00519 int32_t RKL_ICU_FUNCTION_APPEND(uregex_start) ( uregex *regexp, int32_t groupNum, int32_t *status) RKL_WARN_UNUSED_NONNULL_ARGS(1,3); 00520 uregex *RKL_ICU_FUNCTION_APPEND(uregex_clone) (const uregex *regexp, int32_t *status) RKL_WARN_UNUSED_NONNULL_ARGS(1,2); 00521 00523 #pragma mark - 00524 #pragma mark RegexKitLite internal, private function prototypes 00525 00526 // Functions used for managing the 4-way set associative LRU cache and regex string hash lookaside cache. 00527 RKL_STATIC_INLINE NSUInteger rkl_leastRecentlyUsedWayInSet ( NSUInteger cacheSetsCount, const RKLLRUCacheSet_t cacheSetsArray[cacheSetsCount], NSUInteger set) RKL_WARN_UNUSED_NONNULL_ARGS(2); 00528 RKL_STATIC_INLINE void rkl_accessCacheSetWay ( NSUInteger cacheSetsCount, RKLLRUCacheSet_t cacheSetsArray[cacheSetsCount], NSUInteger set, NSUInteger way) RKL_NONNULL_ARGS(2); 00529 RKL_STATIC_INLINE NSUInteger rkl_regexLookasideCacheIndexForPointerAndOptions (const void *ptr, RKLRegexOptions options) RKL_WARN_UNUSED_NONNULL_ARGS(1); 00530 RKL_STATIC_INLINE void rkl_setRegexLookasideCacheToCachedRegexForPointer (const RKLCachedRegex *cachedRegex, const void *ptr) RKL_NONNULL_ARGS(1,2); 00531 RKL_STATIC_INLINE RKLCachedRegex *rkl_cachedRegexFromRegexLookasideCacheForString (const void *ptr, RKLRegexOptions options) RKL_WARN_UNUSED_NONNULL_ARGS(1); 00532 RKL_STATIC_INLINE NSUInteger rkl_makeCacheSetHash ( CFHashCode regexHash, RKLRegexOptions options) RKL_WARN_UNUSED; 00533 RKL_STATIC_INLINE NSUInteger rkl_cacheSetForRegexHashAndOptions ( CFHashCode regexHash, RKLRegexOptions options) RKL_WARN_UNUSED; 00534 RKL_STATIC_INLINE NSUInteger rkl_cacheWayForCachedRegex (const RKLCachedRegex *cachedRegex) RKL_WARN_UNUSED_NONNULL_ARGS(1); 00535 RKL_STATIC_INLINE NSUInteger rkl_cacheSetForCachedRegex (const RKLCachedRegex *cachedRegex) RKL_WARN_UNUSED_NONNULL_ARGS(1); 00536 RKL_STATIC_INLINE RKLCachedRegex *rkl_cachedRegexForCacheSetAndWay ( NSUInteger cacheSet, NSUInteger cacheWay) RKL_WARN_UNUSED; 00537 RKL_STATIC_INLINE RKLCachedRegex *rkl_cachedRegexForRegexHashAndOptionsAndWay ( CFHashCode regexHash, RKLRegexOptions options, NSUInteger cacheWay) RKL_WARN_UNUSED; 00538 RKL_STATIC_INLINE void rkl_updateCachesWithCachedRegex ( RKLCachedRegex *cachedRegex, const void *ptr, int hitOrMiss RKL_UNUSED_DTRACE_ARG, int status RKL_UNUSED_DTRACE_ARG) RKL_NONNULL_ARGS(1,2); 00539 RKL_STATIC_INLINE RKLCachedRegex *rkl_leastRecentlyUsedCachedRegexForRegexHashAndOptions ( CFHashCode regexHash, RKLRegexOptions options) RKL_WARN_UNUSED; 00540 00541 static RKLCachedRegex *rkl_getCachedRegex (NSString *regexString, RKLRegexOptions options, NSError **error, id *exception) RKL_WARN_UNUSED_NONNULL_ARGS(1,4); 00542 static NSUInteger rkl_setCachedRegexToString (RKLCachedRegex *cachedRegex, const NSRange *range, int32_t *status, id *exception RKL_UNUSED_ASSERTION_ARG) RKL_WARN_UNUSED_NONNULL_ARGS(1,2,3,4); 00543 static RKLCachedRegex *rkl_getCachedRegexSetToString (NSString *regexString, RKLRegexOptions options, NSString *matchString, NSUInteger *matchLengthPtr, NSRange *matchRange, NSError **error, id *exception, int32_t *status) RKL_WARN_UNUSED_NONNULL_ARGS(1,3,4,5,7,8); 00544 static id rkl_performDictionaryVarArgsOp(id self, SEL _cmd, RKLRegexOp regexOp, NSString *regexString, RKLRegexOptions options, NSInteger capture, id matchString, NSRange *matchRange, NSString *replacementString, NSError **error, void *result, id firstKey, va_list varArgsList) RKL_NONNULL_ARGS(1,2); 00545 static id rkl_performRegexOp (id self, SEL _cmd, RKLRegexOp regexOp, NSString *regexString, RKLRegexOptions options, NSInteger capture, id matchString, NSRange *matchRange, NSString *replacementString, NSError **error, void *result, NSUInteger captureKeysCount, id captureKeys[captureKeysCount], const int captureKeyIndexes[captureKeysCount]) RKL_NONNULL_ARGS(1,2); 00546 static void rkl_handleDelayedAssert (id self, SEL _cmd, id exception) RKL_NONNULL_ARGS(3); 00547 00548 static NSUInteger rkl_search (RKLCachedRegex *cachedRegex, NSRange *searchRange, NSUInteger updateSearchRange, id *exception RKL_UNUSED_ASSERTION_ARG, int32_t *status) RKL_WARN_UNUSED_NONNULL_ARGS(1,2,4,5); 00549 00550 static BOOL rkl_findRanges (RKLCachedRegex *cachedRegex, RKLRegexOp regexOp, RKLFindAll *findAll, id *exception, int32_t *status) RKL_WARN_UNUSED_NONNULL_ARGS(1,3,4,5); 00551 static NSUInteger rkl_growFindRanges (RKLCachedRegex *cachedRegex, NSUInteger lastLocation, RKLFindAll *findAll, id *exception RKL_UNUSED_ASSERTION_ARG) RKL_WARN_UNUSED_NONNULL_ARGS(1,3,4); 00552 static NSArray *rkl_makeArray (RKLCachedRegex *cachedRegex, RKLRegexOp regexOp, RKLFindAll *findAll, id *exception RKL_UNUSED_ASSERTION_ARG) RKL_WARN_UNUSED_NONNULL_ARGS(1,3,4); 00553 static id rkl_makeDictionary (RKLCachedRegex *cachedRegex, RKLRegexOp regexOp, RKLFindAll *findAll, NSUInteger captureKeysCount, id captureKeys[captureKeysCount], const int captureKeyIndexes[captureKeysCount], id *exception RKL_UNUSED_ASSERTION_ARG) RKL_WARN_UNUSED_NONNULL_ARGS(1,3,5,6); 00554 00555 static NSString *rkl_replaceString (RKLCachedRegex *cachedRegex, id searchString, NSUInteger searchU16Length, NSString *replacementString, NSUInteger replacementU16Length, NSInteger *replacedCount, NSUInteger replaceMutable, id *exception, int32_t *status) RKL_WARN_UNUSED_NONNULL_ARGS(1,2,4,8,9); 00556 static int32_t rkl_replaceAll (RKLCachedRegex *cachedRegex, RKL_STRONG_REF const UniChar * RKL_GC_VOLATILE replacementUniChar, int32_t replacementU16Length, UniChar *replacedUniChar, int32_t replacedU16Capacity, NSInteger *replacedCount, int32_t *needU16Capacity, id *exception RKL_UNUSED_ASSERTION_ARG, int32_t *status) RKL_WARN_UNUSED_NONNULL_ARGS(1,2,4,6,7,8,9); 00557 00558 static NSUInteger rkl_isRegexValid (id self, SEL _cmd, NSString *regex, RKLRegexOptions options, NSInteger *captureCountPtr, NSError **error) RKL_NONNULL_ARGS(1,2); 00559 00560 static void rkl_clearStringCache (void); 00561 static void rkl_clearBuffer (RKLBuffer *buffer, NSUInteger freeDynamicBuffer) RKL_NONNULL_ARGS(1); 00562 static void rkl_clearCachedRegex (RKLCachedRegex *cachedRegex) RKL_NONNULL_ARGS(1); 00563 static void rkl_clearCachedRegexSetTo (RKLCachedRegex *cachedRegex) RKL_NONNULL_ARGS(1); 00564 00565 static NSDictionary *rkl_userInfoDictionary (RKLUserInfoOptions userInfoOptions, NSString *regexString, RKLRegexOptions options, const UParseError *parseError, int32_t status, NSString *matchString, NSRange matchRange, NSString *replacementString, NSString *replacedString, NSInteger replacedCount, RKLRegexEnumerationOptions enumerationOptions, ...) RKL_WARN_UNUSED_SENTINEL; 00566 static NSError *rkl_makeNSError (RKLUserInfoOptions userInfoOptions, NSString *regexString, RKLRegexOptions options, const UParseError *parseError, int32_t status, NSString *matchString, NSRange matchRange, NSString *replacementString, NSString *replacedString, NSInteger replacedCount, RKLRegexEnumerationOptions enumerationOptions, NSString *errorDescription) RKL_WARN_UNUSED; 00567 00568 static NSException *rkl_NSExceptionForRegex (NSString *regexString, RKLRegexOptions options, const UParseError *parseError, int32_t status) RKL_WARN_UNUSED_NONNULL_ARGS(1); 00569 static NSDictionary *rkl_makeAssertDictionary (const char *function, const char *file, int line, NSString *format, ...) RKL_WARN_UNUSED_NONNULL_ARGS(1,2,4); 00570 static NSString *rkl_stringFromClassAndMethod (id object, SEL selector, NSString *format, ...) RKL_WARN_UNUSED_NONNULL_ARGS(3); 00571 00572 RKL_STATIC_INLINE int32_t rkl_getRangeForCapture(RKLCachedRegex *cr, int32_t *s, int32_t c, NSRange *r) RKL_WARN_UNUSED_NONNULL_ARGS(1,2,4); 00573 RKL_STATIC_INLINE int32_t rkl_getRangeForCapture(RKLCachedRegex *cr, int32_t *s, int32_t c, NSRange *r) { uregex *re = cr->icu_regex; int32_t start = RKL_ICU_FUNCTION_APPEND(uregex_start)(re, c, s); if(RKL_EXPECTED((*s > U_ZERO_ERROR), 0L) || (start == -1)) { *r = NSNotFoundRange; } else { r->location = (NSUInteger)start; r->length = (NSUInteger)RKL_ICU_FUNCTION_APPEND(uregex_end)(re, c, s) - r->location; r->location += cr->setToRange.location; } return(*s); } 00574 00575 RKL_STATIC_INLINE RKLFindAll rkl_makeFindAll(RKL_STRONG_REF NSRange * RKL_GC_VOLATILE r, NSRange fir, NSInteger c, size_t s, size_t su, RKL_STRONG_REF void ** RKL_GC_VOLATILE rsb, RKL_STRONG_REF void ** RKL_GC_VOLATILE ssb, RKL_STRONG_REF void ** RKL_GC_VOLATILE asb, RKL_STRONG_REF void ** RKL_GC_VOLATILE dsb, RKL_STRONG_REF void ** RKL_GC_VOLATILE ksb, NSInteger f, NSInteger cap, NSInteger fut) RKL_WARN_UNUSED_CONST; 00576 RKL_STATIC_INLINE RKLFindAll rkl_makeFindAll(RKL_STRONG_REF NSRange * RKL_GC_VOLATILE r, NSRange fir, NSInteger c, size_t s, size_t su, RKL_STRONG_REF void ** RKL_GC_VOLATILE rsb, RKL_STRONG_REF void ** RKL_GC_VOLATILE ssb, RKL_STRONG_REF void ** RKL_GC_VOLATILE asb, RKL_STRONG_REF void ** RKL_GC_VOLATILE dsb, RKL_STRONG_REF void ** RKL_GC_VOLATILE ksb, NSInteger f, NSInteger cap, NSInteger fut) { return(((RKLFindAll){ .ranges=r, .findInRange=fir, .remainingRange=fir, .capacity=c, .found=f, .findUpTo=fut, .capture=cap, .addedSplitRanges=0L, .size=s, .stackUsed=su, .rangesScratchBuffer=rsb, .stringsScratchBuffer=ssb, .arraysScratchBuffer=asb, .dictionariesScratchBuffer=dsb, .keysScratchBuffer=ksb})); } 00577 00579 #pragma mark - 00580 #pragma mark RKL_FAST_MUTABLE_CHECK implementation 00581 00582 #ifdef RKL_FAST_MUTABLE_CHECK 00583 // We use a trampoline function pointer to check at run time if the function __CFStringIsMutable is available. 00584 // If it is, the trampoline function pointer is replaced with the address of that function. 00585 // Otherwise, we assume the worst case that every string is mutable. 00586 // This hopefully helps to protect us since we're using an undocumented, non-public API call. 00587 // We will keep on working if it ever does go away, just with a bit less performance due to the overhead of mutable checks. 00588 00589 static BOOL rkl_CFStringIsMutable_first (CFStringRef str); 00590 static BOOL rkl_CFStringIsMutable_yes (CFStringRef str RKL_UNUSED_ARG) { return(YES); } 00591 static BOOL(*rkl_CFStringIsMutable) (CFStringRef str) = rkl_CFStringIsMutable_first; 00592 static BOOL rkl_CFStringIsMutable_first (CFStringRef str) { if((rkl_CFStringIsMutable = (BOOL(*)(CFStringRef))dlsym(RTLD_DEFAULT, "__CFStringIsMutable")) == NULL) { rkl_CFStringIsMutable = rkl_CFStringIsMutable_yes; } return(rkl_CFStringIsMutable(str)); } 00593 #else // RKL_FAST_MUTABLE_CHECK is not defined. Assume that all strings are potentially mutable. 00594 #define rkl_CFStringIsMutable(s) (YES) 00595 #endif // RKL_FAST_MUTABLE_CHECK 00596 00598 #pragma mark - 00599 #pragma mark iPhone / iPod touch low memory notification handler 00600 00601 #if defined(RKL_REGISTER_FOR_IPHONE_LOWMEM_NOTIFICATIONS) && (RKL_REGISTER_FOR_IPHONE_LOWMEM_NOTIFICATIONS == 1) 00602 00603 // The next few lines are specifically for the iPhone to catch low memory conditions. 00604 // The basic idea is that rkl_RegisterForLowMemoryNotifications() is set to be run once by the linker at load time via __attribute((constructor)). 00605 // rkl_RegisterForLowMemoryNotifications() tries to find the iPhone low memory notification symbol. If it can find it, 00606 // it registers with the default NSNotificationCenter to call the RKLLowMemoryWarningObserver class method +lowMemoryWarning:. 00607 // rkl_RegisterForLowMemoryNotifications() uses an atomic compare and swap to guarantee that it initializes exactly once. 00608 // +lowMemoryWarning tries to acquire the cache lock. If it gets the lock, it clears the cache. If it can't, it calls performSelector: 00609 // with a delay of half a second to try again. This will hopefully prevent any deadlocks, such as a RegexKitLite request for 00610 // memory triggering a notification while the lock is held. 00611 00612 static void rkl_RegisterForLowMemoryNotifications(void) RKL_ATTRIBUTES(used); 00613 00614 @interface RKLLowMemoryWarningObserver : NSObject +(void)lowMemoryWarning:(id)notification; @end 00615 @implementation RKLLowMemoryWarningObserver 00616 +(void)lowMemoryWarning:(id)notification { 00617 if(OSSpinLockTry(&rkl_cacheSpinLock)) { rkl_clearStringCache(); OSSpinLockUnlock(&rkl_cacheSpinLock); } 00618 else { [[RKLLowMemoryWarningObserver class] performSelector:@selector(lowMemoryWarning:) withObject:notification afterDelay:(NSTimeInterval)0.1]; } 00619 } 00620 @end 00621 00622 static volatile int rkl_HaveRegisteredForLowMemoryNotifications = 0; 00623 00624 __attribute__((constructor)) static void rkl_RegisterForLowMemoryNotifications(void) { 00625 _Bool didSwap = false; 00626 void **memoryWarningNotification = NULL; 00627 00628 while((rkl_HaveRegisteredForLowMemoryNotifications == 0) && ((didSwap = OSAtomicCompareAndSwapIntBarrier(0, 1, &rkl_HaveRegisteredForLowMemoryNotifications)) == false)) { /* Allows for spurious CAS failures. */ } 00629 if(didSwap == true) { 00630 if((memoryWarningNotification = (void **)dlsym(RTLD_DEFAULT, "UIApplicationDidReceiveMemoryWarningNotification")) != NULL) { 00631 [[NSNotificationCenter defaultCenter] addObserver:[RKLLowMemoryWarningObserver class] selector:@selector(lowMemoryWarning:) name:(NSString *)*memoryWarningNotification object:NULL]; 00632 } 00633 } 00634 } 00635 00636 #endif // defined(RKL_REGISTER_FOR_IPHONE_LOWMEM_NOTIFICATIONS) && (RKL_REGISTER_FOR_IPHONE_LOWMEM_NOTIFICATIONS == 1) 00637 00639 #pragma mark - 00640 #pragma mark DTrace functionality 00641 00642 #ifdef _RKL_DTRACE_ENABLED 00643 00644 // compiledRegexCache(unsigned long eventID, const char *regexUTF8, int options, int captures, int hitMiss, int icuStatusCode, const char *icuErrorMessage, double *hitRate); 00645 // utf16ConversionCache(unsigned long eventID, unsigned int lookupResultFlags, double *hitRate, const void *string, unsigned long NSRange.location, unsigned long NSRange.length, long length); 00646 00647 /* 00648 provider RegexKitLite { 00649 probe compiledRegexCache(unsigned long, const char *, unsigned int, int, int, int, const char *, double *); 00650 probe utf16ConversionCache(unsigned long, unsigned int, double *, const void *, unsigned long, unsigned long, long); 00651 }; 00652 00653 #pragma D attributes Unstable/Unstable/Common provider RegexKitLite provider 00654 #pragma D attributes Private/Private/Common provider RegexKitLite module 00655 #pragma D attributes Private/Private/Common provider RegexKitLite function 00656 #pragma D attributes Unstable/Unstable/Common provider RegexKitLite name 00657 #pragma D attributes Unstable/Unstable/Common provider RegexKitLite args 00658 */ 00659 00660 #define REGEXKITLITE_STABILITY "___dtrace_stability$RegexKitLite$v1$4_4_5_1_1_5_1_1_5_4_4_5_4_4_5" 00661 #define REGEXKITLITE_TYPEDEFS "___dtrace_typedefs$RegexKitLite$v1" 00662 #define REGEXKITLITE_COMPILEDREGEXCACHE(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) { __asm__ volatile(".reference " REGEXKITLITE_TYPEDEFS); __dtrace_probe$RegexKitLite$compiledRegexCache$v1$756e7369676e6564206c6f6e67$63686172202a$756e7369676e656420696e74$696e74$696e74$696e74$63686172202a$646f75626c65202a(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7); __asm__ volatile(".reference " REGEXKITLITE_STABILITY); } 00663 #define REGEXKITLITE_COMPILEDREGEXCACHE_ENABLED() __dtrace_isenabled$RegexKitLite$compiledRegexCache$v1() 00664 #define REGEXKITLITE_CONVERTEDSTRINGU16CACHE(arg0, arg1, arg2, arg3, arg4, arg5, arg6) { __asm__ volatile(".reference " REGEXKITLITE_TYPEDEFS); __dtrace_probe$RegexKitLite$utf16ConversionCache$v1$756e7369676e6564206c6f6e67$756e7369676e656420696e74$646f75626c65202a$766f6964202a$756e7369676e6564206c6f6e67$756e7369676e6564206c6f6e67$6c6f6e67(arg0, arg1, arg2, arg3, arg4, arg5, arg6); __asm__ volatile(".reference " REGEXKITLITE_STABILITY); } 00665 #define REGEXKITLITE_CONVERTEDSTRINGU16CACHE_ENABLED() __dtrace_isenabled$RegexKitLite$utf16ConversionCache$v1() 00666 00667 extern void __dtrace_probe$RegexKitLite$compiledRegexCache$v1$756e7369676e6564206c6f6e67$63686172202a$756e7369676e656420696e74$696e74$696e74$696e74$63686172202a$646f75626c65202a(unsigned long, const char *, unsigned int, int, int, int, const char *, double *); 00668 extern int __dtrace_isenabled$RegexKitLite$compiledRegexCache$v1(void); 00669 extern void __dtrace_probe$RegexKitLite$utf16ConversionCache$v1$756e7369676e6564206c6f6e67$756e7369676e656420696e74$646f75626c65202a$766f6964202a$756e7369676e6564206c6f6e67$756e7369676e6564206c6f6e67$6c6f6e67(unsigned long, unsigned int, double *, const void *, unsigned long, unsigned long, long); 00670 extern int __dtrace_isenabled$RegexKitLite$utf16ConversionCache$v1(void); 00671 00673 00674 enum { 00675 RKLCacheHitLookupFlag = 1 << 0, 00676 RKLConversionRequiredLookupFlag = 1 << 1, 00677 RKLSetTextLookupFlag = 1 << 2, 00678 RKLDynamicBufferLookupFlag = 1 << 3, 00679 RKLErrorLookupFlag = 1 << 4, 00680 RKLEnumerationBufferLookupFlag = 1 << 5, 00681 }; 00682 00683 #define rkl_dtrace_addLookupFlag(a,b) do { a |= (unsigned int)(b); } while(0) 00684 00685 static char rkl_dtrace_regexUTF8[_RKL_REGEX_CACHE_LINES + 1UL][_RKL_DTRACE_REGEXUTF8_SIZE]; 00686 static NSUInteger rkl_dtrace_eventID, rkl_dtrace_compiledCacheLookups, rkl_dtrace_compiledCacheHits, rkl_dtrace_conversionBufferLookups, rkl_dtrace_conversionBufferHits; 00687 00688 #define rkl_dtrace_incrementEventID() do { rkl_dtrace_eventID++; } while(0) 00689 #define rkl_dtrace_incrementAndGetEventID(v) do { rkl_dtrace_eventID++; v = rkl_dtrace_eventID; } while(0) 00690 #define rkl_dtrace_compiledRegexCache(a0, a1, a2, a3, a4, a5) do { int _a3 = (a3); rkl_dtrace_compiledCacheLookups++; if(_a3 == 1) { rkl_dtrace_compiledCacheHits++; } if(RKL_EXPECTED(REGEXKITLITE_COMPILEDREGEXCACHE_ENABLED(), 0L)) { double hitRate = 0.0; if(rkl_dtrace_compiledCacheLookups > 0UL) { hitRate = ((double)rkl_dtrace_compiledCacheHits / (double)rkl_dtrace_compiledCacheLookups) * 100.0; } REGEXKITLITE_COMPILEDREGEXCACHE(rkl_dtrace_eventID, a0, a1, a2, _a3, a4, a5, &hitRate); } } while(0) 00691 #define rkl_dtrace_utf16ConversionCache(a0, a1, a2, a3, a4) do { unsigned int _a0 = (a0); if((_a0 & RKLConversionRequiredLookupFlag) != 0U) { rkl_dtrace_conversionBufferLookups++; if((_a0 & RKLCacheHitLookupFlag) != 0U) { rkl_dtrace_conversionBufferHits++; } } if(RKL_EXPECTED(REGEXKITLITE_CONVERTEDSTRINGU16CACHE_ENABLED(), 0L)) { double hitRate = 0.0; if(rkl_dtrace_conversionBufferLookups > 0UL) { hitRate = ((double)rkl_dtrace_conversionBufferHits / (double)rkl_dtrace_conversionBufferLookups) * 100.0; } REGEXKITLITE_CONVERTEDSTRINGU16CACHE(rkl_dtrace_eventID, _a0, &hitRate, a1, a2, a3, a4); } } while(0) 00692 #define rkl_dtrace_utf16ConversionCacheWithEventID(c0, a0, a1, a2, a3, a4) do { unsigned int _a0 = (a0); if((_a0 & RKLConversionRequiredLookupFlag) != 0U) { rkl_dtrace_conversionBufferLookups++; if((_a0 & RKLCacheHitLookupFlag) != 0U) { rkl_dtrace_conversionBufferHits++; } } if(RKL_EXPECTED(REGEXKITLITE_CONVERTEDSTRINGU16CACHE_ENABLED(), 0L)) { double hitRate = 0.0; if(rkl_dtrace_conversionBufferLookups > 0UL) { hitRate = ((double)rkl_dtrace_conversionBufferHits / (double)rkl_dtrace_conversionBufferLookups) * 100.0; } REGEXKITLITE_CONVERTEDSTRINGU16CACHE(c0, _a0, &hitRate, a1, a2, a3, a4); } } while(0) 00693 00694 00695 // \342\200\246 == UTF8 for HORIZONTAL ELLIPSIS, aka triple dots '...' 00696 #define RKL_UTF8_ELLIPSE "\342\200\246" 00697 00698 // rkl_dtrace_getRegexUTF8 will copy the str argument to utf8Buffer using UTF8 as the string encoding. 00699 // If the utf8 encoding would take up more bytes than the utf8Buffers length, then the unicode character 'HORIZONTAL ELLIPSIS' ('...') is appended to indicate truncation occurred. 00700 static void rkl_dtrace_getRegexUTF8(CFStringRef str, char *utf8Buffer) RKL_NONNULL_ARGS(2); 00701 static void rkl_dtrace_getRegexUTF8(CFStringRef str, char *utf8Buffer) { 00702 if((str == NULL) || (utf8Buffer == NULL)) { return; } 00703 CFIndex maxLength = ((CFIndex)_RKL_DTRACE_REGEXUTF8_SIZE - 2L), maxBytes = (maxLength - (CFIndex)sizeof(RKL_UTF8_ELLIPSE) - 1L), stringU16Length = CFStringGetLength(str), usedBytes = 0L; 00704 CFStringGetBytes(str, CFMakeRange(0L, ((stringU16Length < maxLength) ? stringU16Length : maxLength)), kCFStringEncodingUTF8, (UInt8)'?', (Boolean)0, (UInt8 *)utf8Buffer, maxBytes, &usedBytes); 00705 if(usedBytes == maxBytes) { strncpy(utf8Buffer + usedBytes, RKL_UTF8_ELLIPSE, ((size_t)_RKL_DTRACE_REGEXUTF8_SIZE - (size_t)usedBytes) - 2UL); } else { utf8Buffer[usedBytes] = (char)0; } 00706 } 00707 00708 #else // _RKL_DTRACE_ENABLED 00709 00710 #define rkl_dtrace_incrementEventID() 00711 #define rkl_dtrace_incrementAndGetEventID(v) 00712 #define rkl_dtrace_compiledRegexCache(a0, a1, a2, a3, a4, a5) 00713 #define rkl_dtrace_utf16ConversionCache(a0, a1, a2, a3, a4) 00714 #define rkl_dtrace_utf16ConversionCacheWithEventID(c0, a0, a1, a2, a3, a4) 00715 #define rkl_dtrace_getRegexUTF8(str, buf) 00716 #define rkl_dtrace_addLookupFlag(a,b) 00717 00718 #endif // _RKL_DTRACE_ENABLED 00719 00721 #pragma mark - 00722 #pragma mark RegexKitLite low-level internal functions 00723 #pragma mark - 00724 00725 // The 4-way set associative LRU logic comes from Henry S. Warren Jr.'s Hacker's Delight, "revisions", 7-7 An LRU Algorithm: 00726 // http://www.hackersdelight.org/revisions.pdf 00727 // The functions rkl_leastRecentlyUsedWayInSet() and rkl_accessCacheSetWay() implement the cache functionality and are used 00728 // from a number of different places that need to perform caching (i.e., cached regex, cached UTF16 conversions, etc) 00729 00730 #pragma mark 4-way set associative LRU functions 00731 00732 RKL_STATIC_INLINE NSUInteger rkl_leastRecentlyUsedWayInSet(NSUInteger cacheSetsCount, const RKLLRUCacheSet_t cacheSetsArray[cacheSetsCount], NSUInteger set) { 00733 RKLCAbortAssert((cacheSetsArray != NULL) && ((NSInteger)cacheSetsCount > 0L) && (set < cacheSetsCount) && ((cacheSetsArray == rkl_cachedRegexCacheSets) ? set < _RKL_REGEX_LRU_CACHE_SETS : 1) && (((sizeof(unsigned int) - sizeof(RKLLRUCacheSet_t)) * 8) < (sizeof(unsigned int) * 8))); 00734 unsigned int cacheSet = (((unsigned int)cacheSetsArray[set]) << ((sizeof(unsigned int) - sizeof(RKLLRUCacheSet_t)) * 8)); // __builtin_clz takes an 'unsigned int' argument. The rest is to ensure bit alignment regardless of 32/64/whatever. 00735 NSUInteger leastRecentlyUsed = ((NSUInteger)(3LU - (NSUInteger)((__builtin_clz((~(((cacheSet & 0x77777777U) + 0x77777777U) | cacheSet | 0x77777777U))) ) >> 2))); 00736 RKLCAbortAssert(leastRecentlyUsed < _RKL_LRU_CACHE_SET_WAYS); 00737 return(leastRecentlyUsed); 00738 } 00739 00740 RKL_STATIC_INLINE void rkl_accessCacheSetWay(NSUInteger cacheSetsCount, RKLLRUCacheSet_t cacheSetsArray[cacheSetsCount], NSUInteger cacheSet, NSUInteger cacheWay) { 00741 RKLCAbortAssert((cacheSetsArray != NULL) && ((NSInteger)cacheSetsCount > 0L) && (cacheSet < cacheSetsCount) && (cacheWay < _RKL_LRU_CACHE_SET_WAYS) && ((cacheSetsArray == rkl_cachedRegexCacheSets) ? cacheSet < _RKL_REGEX_LRU_CACHE_SETS : 1)); 00742 cacheSetsArray[cacheSet] = (RKLLRUCacheSet_t)(((cacheSetsArray[cacheSet] & (RKLLRUCacheSet_t)0xFFFFU) | (((RKLLRUCacheSet_t)0xFU) << (cacheWay * 4U))) & (~(((RKLLRUCacheSet_t)0x1111U) << (3U - cacheWay)))); 00743 } 00744 00745 #pragma mark Common, macro'ish compiled regular expression cache logic 00746 00747 // These functions consolidate bits and pieces of code used to maintain, update, and access the 4-way set associative LRU cache and Regex Lookaside Cache. 00748 RKL_STATIC_INLINE NSUInteger rkl_regexLookasideCacheIndexForPointerAndOptions (const void *ptr, RKLRegexOptions options) { return(((((NSUInteger)(ptr)) >> 4) + options + (options >> 4)) & _RKL_REGEX_LOOKASIDE_CACHE_MASK); } 00749 RKL_STATIC_INLINE void rkl_setRegexLookasideCacheToCachedRegexForPointer (const RKLCachedRegex *cachedRegex, const void *ptr) { rkl_regexLookasideCache[rkl_regexLookasideCacheIndexForPointerAndOptions(ptr, cachedRegex->options)] = (cachedRegex - rkl_cachedRegexes); } 00750 RKL_STATIC_INLINE RKLCachedRegex *rkl_cachedRegexFromRegexLookasideCacheForString (const void *ptr, RKLRegexOptions options) { return(&rkl_cachedRegexes[rkl_regexLookasideCache[rkl_regexLookasideCacheIndexForPointerAndOptions(ptr, options)]]); } 00751 RKL_STATIC_INLINE NSUInteger rkl_makeCacheSetHash ( CFHashCode regexHash, RKLRegexOptions options) { return((NSUInteger)regexHash ^ (NSUInteger)options); } 00752 RKL_STATIC_INLINE NSUInteger rkl_cacheSetForRegexHashAndOptions ( CFHashCode regexHash, RKLRegexOptions options) { return((rkl_makeCacheSetHash(regexHash, options) % _RKL_REGEX_LRU_CACHE_SETS)); } 00753 RKL_STATIC_INLINE NSUInteger rkl_cacheWayForCachedRegex (const RKLCachedRegex *cachedRegex) { return((cachedRegex - rkl_cachedRegexes) % _RKL_LRU_CACHE_SET_WAYS); } 00754 RKL_STATIC_INLINE NSUInteger rkl_cacheSetForCachedRegex (const RKLCachedRegex *cachedRegex) { return(rkl_cacheSetForRegexHashAndOptions(cachedRegex->regexHash, cachedRegex->options)); } 00755 RKL_STATIC_INLINE RKLCachedRegex *rkl_cachedRegexForCacheSetAndWay ( NSUInteger cacheSet, NSUInteger cacheWay) { return(&rkl_cachedRegexes[((cacheSet * _RKL_LRU_CACHE_SET_WAYS) + cacheWay)]); } 00756 RKL_STATIC_INLINE RKLCachedRegex *rkl_cachedRegexForRegexHashAndOptionsAndWay ( CFHashCode regexHash, RKLRegexOptions options, NSUInteger cacheWay) { return(rkl_cachedRegexForCacheSetAndWay(rkl_cacheSetForRegexHashAndOptions(regexHash, options), cacheWay)); } 00757 00758 RKL_STATIC_INLINE void rkl_updateCachesWithCachedRegex(RKLCachedRegex *cachedRegex, const void *ptr, int hitOrMiss RKL_UNUSED_DTRACE_ARG, int status RKL_UNUSED_DTRACE_ARG) { 00759 rkl_lastCachedRegex = cachedRegex; 00760 rkl_setRegexLookasideCacheToCachedRegexForPointer(cachedRegex, ptr); 00761 rkl_accessCacheSetWay(_RKL_REGEX_LRU_CACHE_SETS, rkl_cachedRegexCacheSets, rkl_cacheSetForCachedRegex(cachedRegex), rkl_cacheWayForCachedRegex(cachedRegex)); // Set the matching line as the most recently used. 00762 rkl_dtrace_compiledRegexCache(&rkl_dtrace_regexUTF8[(cachedRegex - rkl_cachedRegexes)][0], cachedRegex->options, (int)cachedRegex->captureCount, hitOrMiss, status, NULL); 00763 } 00764 00765 RKL_STATIC_INLINE RKLCachedRegex *rkl_leastRecentlyUsedCachedRegexForRegexHashAndOptions(CFHashCode regexHash, RKLRegexOptions options) { 00766 NSUInteger cacheSet = rkl_cacheSetForRegexHashAndOptions(regexHash, options); 00767 return(rkl_cachedRegexForCacheSetAndWay(cacheSet, rkl_leastRecentlyUsedWayInSet(_RKL_REGEX_LRU_CACHE_SETS, rkl_cachedRegexCacheSets, cacheSet))); 00768 } 00769 00770 #pragma mark Regular expression lookup function 00771 00772 // IMPORTANT! This code is critical path code. Because of this, it has been written for speed, not clarity. 00773 // IMPORTANT! Should only be called with rkl_cacheSpinLock already locked! 00774 // ---------- 00775 00776 static RKLCachedRegex *rkl_getCachedRegex(NSString *regexString, RKLRegexOptions options, NSError **error, id *exception) { 00777 // ---------- vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv 00778 // IMPORTANT! This section of code is called almost every single time that any RegexKitLite functionality is used! It /MUST/ be very fast! 00779 // ---------- vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv 00780 00781 RKLCachedRegex *cachedRegex = NULL; 00782 CFHashCode regexHash = 0UL; 00783 int32_t status = 0; 00784 00785 RKLCDelayedAssert((rkl_cacheSpinLock != (OSSpinLock)0) && (regexString != NULL), exception, exitNow); 00786 00787 // Fast path the common case where this regex is exactly the same one used last time. 00788 // The pointer equality test is valid under these circumstances since the cachedRegex->regexString is an immutable copy. 00789 // If the regexString argument is mutable, this test will fail, and we'll use the the slow path cache check below. 00790 if(RKL_EXPECTED(rkl_lastCachedRegex != NULL, 1L) && RKL_EXPECTED(rkl_lastCachedRegex->regexString == (CFStringRef)regexString, 1L) && RKL_EXPECTED(rkl_lastCachedRegex->options == options, 1L) && RKL_EXPECTED(rkl_lastCachedRegex->icu_regex != NULL, 1L)) { 00791 rkl_dtrace_compiledRegexCache(&rkl_dtrace_regexUTF8[(rkl_lastCachedRegex - rkl_cachedRegexes)][0], rkl_lastCachedRegex->options, (int)rkl_lastCachedRegex->captureCount, 1, 0, NULL); 00792 return(rkl_lastCachedRegex); 00793 } 00794 00795 rkl_lastCachedRegex = NULL; // Make sure that rkl_lastCachedRegex is NULL in case there is some kind of error. 00796 cachedRegex = rkl_cachedRegexFromRegexLookasideCacheForString(regexString, options); // Check the Regex Lookaside Cache to see if we can quickly find the correct Cached Regex for this regexString pointer + options. 00797 if((RKL_EXPECTED(cachedRegex->regexString == (CFStringRef)regexString, 1L) || (RKL_EXPECTED(cachedRegex->regexString != NULL, 1L) && RKL_EXPECTED(CFEqual((CFTypeRef)regexString, (CFTypeRef)cachedRegex->regexString) == YES, 1L))) && RKL_EXPECTED(cachedRegex->options == options, 1L) && RKL_EXPECTED(cachedRegex->icu_regex != NULL, 1L)) { goto foundMatch; } // There was a Regex Lookaside Cache hit, jump to foundMatch: to quickly return the result. A Regex Lookaside Cache hit allows us to bypass calling CFHash(), which is a decent performance win. 00798 else { cachedRegex = NULL; regexHash = CFHash((CFTypeRef)regexString); } // Regex Lookaside Cache miss. We need to call CFHash() to determine the cache set for this regex. 00799 00800 NSInteger cacheWay = 0L; // Check each way of the set that this regex belongs to. 00801 for(cacheWay = ((NSInteger)_RKL_LRU_CACHE_SET_WAYS - 1L); cacheWay > 0L; cacheWay--) { // Checking the ways in reverse (3, 2, 1, 0) finds a match "sooner" on average. 00802 cachedRegex = rkl_cachedRegexForRegexHashAndOptionsAndWay(regexHash, options, (NSUInteger)cacheWay); 00803 // Return the cached entry if it's a match. If regexString is mutable, the pointer equality test will fail, and CFEqual() is used to determine true equality with the immutable cachedRegex copy. CFEqual() performs a slow character by character check. 00804 if(RKL_EXPECTED(cachedRegex->regexHash == regexHash, 0UL) && ((cachedRegex->regexString == (CFStringRef)regexString) || (RKL_EXPECTED(cachedRegex->regexString != NULL, 1L) && RKL_EXPECTED(CFEqual((CFTypeRef)regexString, (CFTypeRef)cachedRegex->regexString) == YES, 1L))) && RKL_EXPECTED(cachedRegex->options == options, 1L) && RKL_EXPECTED(cachedRegex->icu_regex != NULL, 1L)) { 00805 foundMatch: // Control can transfer here (from above) via a Regex Lookaside Cache hit. 00806 rkl_updateCachesWithCachedRegex(cachedRegex, regexString, 1, 0); 00807 return(cachedRegex); 00808 } 00809 } 00810 00811 // ---------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 00812 // IMPORTANT! This section of code is called almost every single time that any RegexKitLite functionality is used! It /MUST/ be very fast! 00813 // ---------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 00814 00815 // Code below this point is not as sensitive to speed since compiling a regular expression is an extremely expensive operation. 00816 // The regex was not found in the cache. Get the cached regex for the least recently used line in the set, then clear the cached regex and create a new ICU regex in its place. 00817 cachedRegex = rkl_leastRecentlyUsedCachedRegexForRegexHashAndOptions(regexHash, options); 00818 rkl_clearCachedRegex(cachedRegex); 00819 00820 if(RKL_EXPECTED((cachedRegex->regexString = CFStringCreateCopy(NULL, (CFStringRef)regexString)) == NULL, 0L)) { goto exitNow; } ; // Get a cheap immutable copy. 00821 rkl_dtrace_getRegexUTF8(cachedRegex->regexString, &rkl_dtrace_regexUTF8[(cachedRegex - rkl_cachedRegexes)][0]); 00822 cachedRegex->regexHash = regexHash; 00823 cachedRegex->options = options; 00824 00825 CFIndex regexStringU16Length = CFStringGetLength(cachedRegex->regexString); // In UTF16 code units. 00826 UParseError parseError = (UParseError){-1, -1, {0}, {0}}; 00827 RKL_STRONG_REF const UniChar * RKL_GC_VOLATILE regexUniChar = NULL; 00828 00829 if(RKL_EXPECTED(regexStringU16Length >= (CFIndex)INT_MAX, 0L)) { *exception = [NSException exceptionWithName:NSRangeException reason:@"Regex string length exceeds INT_MAX" userInfo:NULL]; goto exitNow; } 00830 00831 // Try to quickly obtain regexString in UTF16 format. 00832 if((regexUniChar = CFStringGetCharactersPtr(cachedRegex->regexString)) == NULL) { // We didn't get the UTF16 pointer quickly and need to perform a full conversion in a temp buffer. 00833 RKL_STRONG_REF UniChar * RKL_GC_VOLATILE uniCharBuffer = NULL; 00834 if(((size_t)regexStringU16Length * sizeof(UniChar)) < (size_t)_RKL_STACK_LIMIT) { if(RKL_EXPECTED((uniCharBuffer = (RKL_STRONG_REF UniChar * RKL_GC_VOLATILE)alloca( (size_t)regexStringU16Length * sizeof(UniChar) )) == NULL, 0L)) { goto exitNow; } } // Try to use the stack. 00835 else { if(RKL_EXPECTED((uniCharBuffer = (RKL_STRONG_REF UniChar * RKL_GC_VOLATILE)rkl_realloc(&rkl_scratchBuffer[0], (size_t)regexStringU16Length * sizeof(UniChar), 0UL)) == NULL, 0L)) { goto exitNow; } } // Otherwise use the heap. 00836 CFStringGetCharacters(cachedRegex->regexString, CFMakeRange(0L, regexStringU16Length), uniCharBuffer); // Convert regexString to UTF16. 00837 regexUniChar = uniCharBuffer; 00838 } 00839 00840 // Create the ICU regex. 00841 if(RKL_EXPECTED((cachedRegex->icu_regex = RKL_ICU_FUNCTION_APPEND(uregex_open)(regexUniChar, (int32_t)regexStringU16Length, options, &parseError, &status)) == NULL, 0L)) { goto exitNow; } 00842 if(RKL_EXPECTED(status <= U_ZERO_ERROR, 1L)) { cachedRegex->captureCount = (NSInteger)RKL_ICU_FUNCTION_APPEND(uregex_groupCount)(cachedRegex->icu_regex, &status); } 00843 if(RKL_EXPECTED(status <= U_ZERO_ERROR, 1L)) { rkl_updateCachesWithCachedRegex(cachedRegex, regexString, 0, status); } 00844 00845 exitNow: 00846 if(RKL_EXPECTED(rkl_scratchBuffer[0] != NULL, 0L)) { rkl_scratchBuffer[0] = rkl_free(&rkl_scratchBuffer[0]); } 00847 if(RKL_EXPECTED(status > U_ZERO_ERROR, 0L)) { rkl_clearCachedRegex(cachedRegex); cachedRegex = rkl_lastCachedRegex = NULL; if(error != NULL) { *error = rkl_makeNSError((RKLUserInfoOptions)RKLUserInfoNone, regexString, options, &parseError, status, NULL, NSNotFoundRange, NULL, NULL, 0L, (RKLRegexEnumerationOptions)RKLRegexEnumerationNoOptions, @"There was an error compiling the regular expression."); } } 00848 00849 #ifdef _RKL_DTRACE_ENABLED 00850 if(RKL_EXPECTED(cachedRegex == NULL, 1L)) { char regexUTF8[_RKL_DTRACE_REGEXUTF8_SIZE]; const char *err = NULL; if(status != U_ZERO_ERROR) { err = RKL_ICU_FUNCTION_APPEND(u_errorName)(status); } rkl_dtrace_getRegexUTF8((CFStringRef)regexString, regexUTF8); rkl_dtrace_compiledRegexCache(regexUTF8, options, -1, -1, status, err); } 00851 #endif // _RKL_DTRACE_ENABLED 00852 00853 return(cachedRegex); 00854 } 00855 00856 // IMPORTANT! This code is critical path code. Because of this, it has been written for speed, not clarity. 00857 // IMPORTANT! Should only be called with rkl_cacheSpinLock already locked! 00858 // ---------- 00859 00860 #pragma mark Set a cached regular expression to a NSStrings UTF-16 text 00861 00862 static NSUInteger rkl_setCachedRegexToString(RKLCachedRegex *cachedRegex, const NSRange *range, int32_t *status, id *exception RKL_UNUSED_ASSERTION_ARG) { 00863 // ---------- vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv 00864 // IMPORTANT! This section of code is called almost every single time that any RegexKitLite functionality is used! It /MUST/ be very fast! 00865 // ---------- vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv 00866 00867 RKLCDelayedAssert((cachedRegex != NULL) && (cachedRegex->setToString != NULL) && ((range != NULL) && (NSEqualRanges(*range, NSNotFoundRange) == NO)) && (status != NULL), exception, exitNow); 00868 RKL_STRONG_REF const UniChar * RKL_GC_VOLATILE stringUniChar = NULL; 00869 #ifdef _RKL_DTRACE_ENABLED 00870 unsigned int lookupResultFlags = 0U; 00871 #endif 00872 00873 NSUInteger useFixedBuffer = (cachedRegex->setToLength < (CFIndex)_RKL_FIXED_LENGTH) ? 1UL : 0UL; 00874 RKLBuffer *buffer = NULL; 00875 00876 if(cachedRegex->setToNeedsConversion == 0U) { 00877 RKLCDelayedAssert((cachedRegex->setToUniChar != NULL) && (cachedRegex->buffer == NULL), exception, exitNow); 00878 if(RKL_EXPECTED((stringUniChar = (RKL_STRONG_REF const UniChar * RKL_GC_VOLATILE)CFStringGetCharactersPtr(cachedRegex->setToString)) == NULL, 0L)) { cachedRegex->setToUniChar = NULL; cachedRegex->setToRange = NSNotFoundRange; cachedRegex->setToNeedsConversion = 1U; } 00879 else { if(RKL_EXPECTED(cachedRegex->setToUniChar != stringUniChar, 0L)) { cachedRegex->setToRange = NSNotFoundRange; cachedRegex->setToUniChar = stringUniChar; } goto setRegexText; } 00880 } 00881 00882 buffer = cachedRegex->buffer; 00883 00884 RKLCDelayedAssert((buffer == NULL) ? 1 : (((buffer == &rkl_lruFixedBuffer[0]) || (buffer == &rkl_lruFixedBuffer[1]) || (buffer == &rkl_lruFixedBuffer[2]) || (buffer == &rkl_lruFixedBuffer[3])) || 00885 ((buffer == &rkl_lruDynamicBuffer[0]) || (buffer == &rkl_lruDynamicBuffer[1]) || (buffer == &rkl_lruDynamicBuffer[2]) || (buffer == &rkl_lruDynamicBuffer[3]))), exception, exitNow); 00886 00887 if((buffer != NULL) && RKL_EXPECTED(cachedRegex->setToString == buffer->string, 1L) && RKL_EXPECTED(cachedRegex->setToHash == buffer->hash, 1L) && RKL_EXPECTED(cachedRegex->setToLength == buffer->length, 1L)) { 00888 RKLCDelayedAssert((buffer->uniChar != NULL), exception, exitNow); 00889 rkl_dtrace_addLookupFlag(lookupResultFlags, RKLCacheHitLookupFlag | RKLConversionRequiredLookupFlag | (useFixedBuffer ? 0U : RKLDynamicBufferLookupFlag)); 00890 if(cachedRegex->setToUniChar != buffer->uniChar) { cachedRegex->setToRange = NSNotFoundRange; cachedRegex->setToUniChar = buffer->uniChar; } 00891 goto setRegexText; 00892 } 00893 00894 buffer = NULL; 00895 cachedRegex->buffer = NULL; 00896 00897 NSInteger cacheWay = 0L; 00898 for(cacheWay = ((NSInteger)_RKL_LRU_CACHE_SET_WAYS - 1L); cacheWay > 0L; cacheWay--) { 00899 if(useFixedBuffer) { buffer = &rkl_lruFixedBuffer[cacheWay]; } else { buffer = &rkl_lruDynamicBuffer[cacheWay]; } 00900 if(RKL_EXPECTED(cachedRegex->setToString == buffer->string, 1L) && RKL_EXPECTED(cachedRegex->setToHash == buffer->hash, 1L) && RKL_EXPECTED(cachedRegex->setToLength == buffer->length, 1L)) { 00901 RKLCDelayedAssert((buffer->uniChar != NULL), exception, exitNow); 00902 rkl_dtrace_addLookupFlag(lookupResultFlags, RKLCacheHitLookupFlag | RKLConversionRequiredLookupFlag | (useFixedBuffer ? 0U : RKLDynamicBufferLookupFlag)); 00903 if(cachedRegex->setToUniChar != buffer->uniChar) { cachedRegex->setToRange = NSNotFoundRange; cachedRegex->setToUniChar = buffer->uniChar; } 00904 cachedRegex->buffer = buffer; 00905 goto setRegexText; 00906 } 00907 } 00908 00909 buffer = NULL; 00910 cachedRegex->setToUniChar = NULL; 00911 cachedRegex->setToRange = NSNotFoundRange; 00912 cachedRegex->buffer = NULL; 00913 00914 RKLCDelayedAssert((cachedRegex->setToNeedsConversion == 1U) && (cachedRegex->buffer == NULL), exception, exitNow); 00915 if(RKL_EXPECTED(cachedRegex->setToNeedsConversion == 1U, 1L) && RKL_EXPECTED((cachedRegex->setToUniChar = (RKL_STRONG_REF const UniChar * RKL_GC_VOLATILE)CFStringGetCharactersPtr(cachedRegex->setToString)) != NULL, 0L)) { cachedRegex->setToNeedsConversion = 0U; cachedRegex->setToRange = NSNotFoundRange; goto setRegexText; } 00916 00917 rkl_dtrace_addLookupFlag(lookupResultFlags, RKLConversionRequiredLookupFlag | (useFixedBuffer ? 0U : RKLDynamicBufferLookupFlag)); 00918 00919 if(useFixedBuffer) { buffer = &rkl_lruFixedBuffer [rkl_leastRecentlyUsedWayInSet(1UL, &rkl_lruFixedBufferCacheSet, 0UL)]; } 00920 else { buffer = &rkl_lruDynamicBuffer[rkl_leastRecentlyUsedWayInSet(1UL, &rkl_lruDynamicBufferCacheSet, 0UL)]; } 00921 00922 RKLCDelayedAssert((useFixedBuffer) ? ((buffer == &rkl_lruFixedBuffer[0]) || (buffer == &rkl_lruFixedBuffer[1]) || (buffer == &rkl_lruFixedBuffer[2]) || (buffer == &rkl_lruFixedBuffer[3])) : 00923 ((buffer == &rkl_lruDynamicBuffer[0]) || (buffer == &rkl_lruDynamicBuffer[1]) || (buffer == &rkl_lruDynamicBuffer[2]) || (buffer == &rkl_lruDynamicBuffer[3])), exception, exitNow); 00924 00925 rkl_clearBuffer(buffer, 0UL); 00926 00927 RKLCDelayedAssert((buffer->string == NULL) && (cachedRegex->setToString != NULL), exception, exitNow); 00928 if(RKL_EXPECTED((buffer->string = (CFStringRef)CFRetain((CFTypeRef)cachedRegex->setToString)) == NULL, 0L)) { goto exitNow; } 00929 buffer->hash = cachedRegex->setToHash; 00930 buffer->length = cachedRegex->setToLength; 00931 00932 if(useFixedBuffer == 0UL) { 00933 RKL_STRONG_REF void * RKL_GC_VOLATILE p = (RKL_STRONG_REF void * RKL_GC_VOLATILE)buffer->uniChar; 00934 if(RKL_EXPECTED((buffer->uniChar = (RKL_STRONG_REF UniChar * RKL_GC_VOLATILE)rkl_realloc(&p, ((size_t)buffer->length * sizeof(UniChar)), 0UL)) == NULL, 0L)) { goto exitNow; } // Resize the buffer. 00935 } 00936 00937 RKLCDelayedAssert((buffer->string != NULL) && (buffer->uniChar != NULL), exception, exitNow); 00938 CFStringGetCharacters(buffer->string, CFMakeRange(0L, buffer->length), (UniChar *)buffer->uniChar); // Convert to a UTF16 string. 00939 00940 cachedRegex->setToUniChar = buffer->uniChar; 00941 cachedRegex->setToRange = NSNotFoundRange; 00942 cachedRegex->buffer = buffer; 00943 00944 setRegexText: 00945 if(buffer != NULL) { if(useFixedBuffer == 1UL) { rkl_accessCacheSetWay(1UL, &rkl_lruFixedBufferCacheSet, 0UL, (NSUInteger)(buffer - rkl_lruFixedBuffer)); } else { rkl_accessCacheSetWay(1UL, &rkl_lruDynamicBufferCacheSet, 0UL, (NSUInteger)(buffer - rkl_lruDynamicBuffer)); } } 00946 00947 if(NSEqualRanges(cachedRegex->setToRange, *range) == NO) { 00948 RKLCDelayedAssert((cachedRegex->icu_regex != NULL) && (cachedRegex->setToUniChar != NULL) && (NSMaxRange(*range) <= (NSUInteger)cachedRegex->setToLength) && (cachedRegex->setToRange.length <= INT_MAX), exception, exitNow); 00949 cachedRegex->lastFindRange = cachedRegex->lastMatchRange = NSNotFoundRange; 00950 cachedRegex->setToRange = *range; 00951 RKL_ICU_FUNCTION_APPEND(uregex_setText)(cachedRegex->icu_regex, cachedRegex->setToUniChar + cachedRegex->setToRange.location, (int32_t)cachedRegex->setToRange.length, status); 00952 rkl_dtrace_addLookupFlag(lookupResultFlags, RKLSetTextLookupFlag); 00953 if(RKL_EXPECTED(*status > U_ZERO_ERROR, 0L)) { rkl_dtrace_addLookupFlag(lookupResultFlags, RKLErrorLookupFlag); goto exitNow; } 00954 } 00955 00956 rkl_dtrace_utf16ConversionCache(lookupResultFlags, cachedRegex->setToString, cachedRegex->setToRange.location, cachedRegex->setToRange.length, cachedRegex->setToLength); 00957 00958 return(1UL); 00959 00960 // ---------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 00961 // IMPORTANT! This section of code is called almost every single time that any RegexKitLite functionality is used! It /MUST/ be very fast! 00962 // ---------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 00963 00964 exitNow: 00965 #ifdef _RKL_DTRACE_ENABLED 00966 rkl_dtrace_addLookupFlag(lookupResultFlags, RKLErrorLookupFlag); 00967 if(cachedRegex != NULL) { rkl_dtrace_utf16ConversionCache(lookupResultFlags, cachedRegex->setToString, cachedRegex->setToRange.location, cachedRegex->setToRange.length, cachedRegex->setToLength); } 00968 #endif // _RKL_DTRACE_ENABLED 00969 if(cachedRegex != NULL) { cachedRegex->buffer = NULL; cachedRegex->setToRange = NSNotFoundRange; cachedRegex->lastFindRange = NSNotFoundRange; cachedRegex->lastMatchRange = NSNotFoundRange; } 00970 return(0UL); 00971 } 00972 00973 // IMPORTANT! This code is critical path code. Because of this, it has been written for speed, not clarity. 00974 // IMPORTANT! Should only be called with rkl_cacheSpinLock already locked! 00975 // ---------- 00976 00977 #pragma mark Get a regular expression and set it to a NSStrings UTF-16 text 00978 00979 static RKLCachedRegex *rkl_getCachedRegexSetToString(NSString *regexString, RKLRegexOptions options, NSString *matchString, NSUInteger *matchLengthPtr, NSRange *matchRange, NSError **error, id *exception, int32_t *status) { 00980 // ---------- vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv 00981 // IMPORTANT! This section of code is called almost every single time that any RegexKitLite functionality is used! It /MUST/ be very fast! 00982 // ---------- vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv 00983 00984 RKLCachedRegex *cachedRegex = NULL; 00985 RKLCDelayedAssert((regexString != NULL) && (matchString != NULL) && (exception != NULL) && (status != NULL) && (matchLengthPtr != NULL), exception, exitNow); 00986 00987 if(RKL_EXPECTED((cachedRegex = rkl_getCachedRegex(regexString, options, error, exception)) == NULL, 0L)) { goto exitNow; } 00988 RKLCDelayedAssert(((cachedRegex >= rkl_cachedRegexes) && ((cachedRegex - rkl_cachedRegexes) < (ssize_t)_RKL_REGEX_CACHE_LINES)) && (cachedRegex != NULL) && (cachedRegex->icu_regex != NULL) && (cachedRegex->regexString != NULL) && (cachedRegex->captureCount >= 0L) && (cachedRegex == rkl_lastCachedRegex), exception, exitNow); 00989 00990 // Optimize the case where the string to search (matchString) is immutable and the setToString immutable copy is the same string with its reference count incremented. 00991 NSUInteger isSetTo = ((cachedRegex->setToString == (CFStringRef)matchString)) ? 1UL : 0UL; 00992 CFIndex matchLength = ((cachedRegex->setToIsImmutable == 1U) && (isSetTo == 1UL)) ? cachedRegex->setToLength : CFStringGetLength((CFStringRef)matchString); 00993 00994 *matchLengthPtr = (NSUInteger)matchLength; 00995 if(matchRange->length == NSUIntegerMax) { matchRange->length = (NSUInteger)matchLength; } // For convenience, allow NSUIntegerMax == string length. 00996 00997 if(RKL_EXPECTED((NSUInteger)matchLength < NSMaxRange(*matchRange), 0L)) { goto exitNow; } // The match range is out of bounds for the string. performRegexOp will catch and report the problem. 00998 00999 RKLCDelayedAssert((isSetTo == 1UL) ? (cachedRegex->setToString != NULL) : 1, exception, exitNow); 01000 01001 if(((cachedRegex->setToIsImmutable == 1U) ? isSetTo : (NSUInteger)((isSetTo == 1UL) && (cachedRegex->setToLength == matchLength) && (cachedRegex->setToHash == CFHash((CFTypeRef)matchString)))) == 0UL) { 01002 if(cachedRegex->setToString != NULL) { rkl_clearCachedRegexSetTo(cachedRegex); } 01003 01004 cachedRegex->setToString = (CFStringRef)CFRetain((CFTypeRef)matchString); 01005 RKLCDelayedAssert(cachedRegex->setToString != NULL, exception, exitNow); 01006 cachedRegex->setToUniChar = CFStringGetCharactersPtr(cachedRegex->setToString); 01007 cachedRegex->setToNeedsConversion = (cachedRegex->setToUniChar == NULL) ? 1U : 0U; 01008 cachedRegex->setToIsImmutable = (rkl_CFStringIsMutable(cachedRegex->setToString) == YES) ? 0U : 1U; // If RKL_FAST_MUTABLE_CHECK is not defined then setToIsImmutable will always be set to '0', or in other words mutable.. 01009 cachedRegex->setToHash = CFHash((CFTypeRef)cachedRegex->setToString); 01010 cachedRegex->setToRange = NSNotFoundRange; 01011 cachedRegex->setToLength = matchLength; 01012 01013 } 01014 01015 if(RKL_EXPECTED(rkl_setCachedRegexToString(cachedRegex, matchRange, status, exception) == 0UL, 0L)) { cachedRegex = NULL; if(*exception == NULL) { *exception = (id)RKLCAssertDictionary(@"Failed to set up UTF16 buffer."); } goto exitNow; } 01016 01017 exitNow: 01018 return(cachedRegex); 01019 // ---------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 01020 // IMPORTANT! This section of code is called almost every single time that any RegexKitLite functionality is used! It /MUST/ be very fast! 01021 // ---------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 01022 } 01023 01024 #pragma mark GCC cleanup __attribute__ functions that ensure the global rkl_cacheSpinLock is properly unlocked 01025 #ifdef RKL_HAVE_CLEANUP 01026 01027 // rkl_cleanup_cacheSpinLockStatus takes advantage of GCC's 'cleanup' variable attribute. When an 'auto' variable with the 'cleanup' attribute goes out of scope, 01028 // GCC arranges to have the designated function called. In this case, we make sure that if rkl_cacheSpinLock was locked that it was also unlocked. 01029 // If rkl_cacheSpinLock was locked, but the rkl_cacheSpinLockStatus unlocked flag was not set, we force rkl_cacheSpinLock unlocked with a call to OSSpinLockUnlock. 01030 // This is not a panacea for preventing mutex usage errors. Old style ObjC exceptions will bypass the cleanup call, but newer C++ style ObjC exceptions should cause the cleanup function to be called during the stack unwind. 01031 01032 // We do not depend on this cleanup function being called. It is used only as an extra safety net. It is probably a bug in RegexKitLite if it is ever invoked and forced to take some kind of protective action. 01033 01034 volatile NSUInteger rkl_debugCacheSpinLockCount = 0UL; 01035 01036 void rkl_debugCacheSpinLock (void) RKL_ATTRIBUTES(used, noinline, visibility("default")); 01037 static void rkl_cleanup_cacheSpinLockStatus (volatile NSUInteger *rkl_cacheSpinLockStatusPtr) RKL_ATTRIBUTES(used); 01038 01039 void rkl_debugCacheSpinLock(void) { 01040 rkl_debugCacheSpinLockCount++; // This is here primarily to prevent the optimizer from optimizing away the function. 01041 } 01042 01043 static void rkl_cleanup_cacheSpinLockStatus(volatile NSUInteger *rkl_cacheSpinLockStatusPtr) { 01044 static NSUInteger didPrintForcedUnlockWarning = 0UL, didPrintNotLockedWarning = 0UL; 01045 NSUInteger rkl_cacheSpinLockStatus = *rkl_cacheSpinLockStatusPtr; 01046 01047 if(RKL_EXPECTED((rkl_cacheSpinLockStatus & RKLUnlockedCacheSpinLock) == 0UL, 0L) && RKL_EXPECTED((rkl_cacheSpinLockStatus & RKLLockedCacheSpinLock) != 0UL, 1L)) { 01048 if(rkl_cacheSpinLock != (OSSpinLock)0) { 01049 if(didPrintForcedUnlockWarning == 0UL) { didPrintForcedUnlockWarning = 1UL; NSLog(@"[RegexKitLite] Unusual condition detected: Recorded that rkl_cacheSpinLock was locked, but for some reason it was not unlocked. Forcibly unlocking rkl_cacheSpinLock. Set a breakpoint at rkl_debugCacheSpinLock to debug. This warning is only printed once."); } 01050 rkl_debugCacheSpinLock(); // Since this is an unusual condition, offer an attempt to catch it before we unlock. 01051 OSSpinLockUnlock(&rkl_cacheSpinLock); 01052 } else { 01053 if(didPrintNotLockedWarning == 0UL) { didPrintNotLockedWarning = 1UL; NSLog(@"[RegexKitLite] Unusual condition detected: Recorded that rkl_cacheSpinLock was locked, but for some reason it was not unlocked, yet rkl_cacheSpinLock is currently not locked? Set a breakpoint at rkl_debugCacheSpinLock to debug. This warning is only printed once."); } 01054 rkl_debugCacheSpinLock(); 01055 } 01056 } 01057 } 01058 01059 #endif // RKL_HAVE_CLEANUP 01060 01061 // rkl_performDictionaryVarArgsOp is a front end to rkl_performRegexOp which converts a ', ...' varargs key/captures list and converts it in to a form that rkl_performRegexOp can use. 01062 // All error checking of arguments is handled by rkl_performRegexOp. 01063 01064 #pragma mark Front end function that handles varargs and calls rkl_performRegexOp with the marshaled results 01065 01066 static id rkl_performDictionaryVarArgsOp(id self, SEL _cmd, RKLRegexOp regexOp, NSString *regexString, RKLRegexOptions options, NSInteger capture, id matchString, NSRange *matchRange, NSString *replacementString, NSError **error, void *result, id firstKey, va_list varArgsList) { 01067 id captureKeys[64]; 01068 int captureKeyIndexes[64]; 01069 NSUInteger captureKeysCount = 0UL; 01070 01071 if(varArgsList != NULL) { 01072 while(captureKeysCount < 62UL) { 01073 id thisCaptureKey = (captureKeysCount == 0) ? firstKey : va_arg(varArgsList, id); 01074 if(RKL_EXPECTED(thisCaptureKey == NULL, 0L)) { break; } 01075 int thisCaptureKeyIndex = va_arg(varArgsList, int); 01076 captureKeys[captureKeysCount] = thisCaptureKey; 01077 captureKeyIndexes[captureKeysCount] = thisCaptureKeyIndex; 01078 captureKeysCount++; 01079 } 01080 } 01081 01082 return(rkl_performRegexOp(self, _cmd, regexOp, regexString, options, capture, matchString, matchRange, replacementString, error, result, captureKeysCount, captureKeys, captureKeyIndexes)); 01083 } 01084 01085 // IMPORTANT! This code is critical path code. Because of this, it has been written for speed, not clarity. 01086 // ---------- 01087 01088 #pragma mark Primary internal function that Objective-C methods call to perform regular expression operations 01089 01090 static id rkl_performRegexOp(id self, SEL _cmd, RKLRegexOp regexOp, NSString *regexString, RKLRegexOptions options, NSInteger capture, id matchString, NSRange *matchRange, NSString *replacementString, NSError **error, void *result, NSUInteger captureKeysCount, id captureKeys[captureKeysCount], const int captureKeyIndexes[captureKeysCount]) { 01091 volatile NSUInteger RKL_CLEANUP(rkl_cleanup_cacheSpinLockStatus) rkl_cacheSpinLockStatus = 0UL; 01092 01093 NSUInteger replaceMutable = 0UL; 01094 RKLRegexOp maskedRegexOp = (regexOp & RKLMaskOp); 01095 BOOL dictionaryOp = ((maskedRegexOp == RKLDictionaryOfCapturesOp) || (maskedRegexOp == RKLArrayOfDictionariesOfCapturesOp)) ? YES : NO; 01096 01097 if((error != NULL) && (*error != NULL)) { *error = NULL; } 01098 01099 if(RKL_EXPECTED(regexString == NULL, 0L)) { RKL_RAISE_EXCEPTION(NSInvalidArgumentException, @"The regular expression argument is NULL."); } 01100 if(RKL_EXPECTED(matchString == NULL, 0L)) { RKL_RAISE_EXCEPTION(NSInternalInconsistencyException, @"The match string argument is NULL."); } 01101 if(RKL_EXPECTED(matchRange == NULL, 0L)) { RKL_RAISE_EXCEPTION(NSInternalInconsistencyException, @"The match range argument is NULL."); } 01102 if((maskedRegexOp == RKLReplaceOp) && RKL_EXPECTED(replacementString == NULL, 0L)) { RKL_RAISE_EXCEPTION(NSInvalidArgumentException, @"The replacement string argument is NULL."); } 01103 if((dictionaryOp == YES) && RKL_EXPECTED(captureKeys == NULL, 0L)) { RKL_RAISE_EXCEPTION(NSInvalidArgumentException, @"The keys argument is NULL."); } 01104 if((dictionaryOp == YES) && RKL_EXPECTED(captureKeyIndexes == NULL, 0L)) { RKL_RAISE_EXCEPTION(NSInvalidArgumentException, @"The captures argument is NULL."); } 01105 01106 id resultObject = NULL, exception = NULL; 01107 int32_t status = U_ZERO_ERROR; 01108 RKLCachedRegex *cachedRegex = NULL; 01109 NSUInteger stringU16Length = 0UL, tmpIdx = 0UL; 01110 NSRange stackRanges[2048]; 01111 RKLFindAll findAll; 01112 01113 // IMPORTANT! Once we have obtained the lock, code MUST exit via 'goto exitNow;' to unlock the lock! NO EXCEPTIONS! 01114 // ---------- 01115 OSSpinLockLock(&rkl_cacheSpinLock); // Grab the lock and get cache entry. 01116 rkl_cacheSpinLockStatus |= RKLLockedCacheSpinLock; 01117 rkl_dtrace_incrementEventID(); 01118 01119 if(RKL_EXPECTED((cachedRegex = rkl_getCachedRegexSetToString(regexString, options, matchString, &stringU16Length, matchRange, error, &exception, &status)) == NULL, 0L)) { stringU16Length = (NSUInteger)CFStringGetLength((CFStringRef)matchString); } 01120 if(RKL_EXPECTED(matchRange->length == NSUIntegerMax, 0L)) { matchRange->length = stringU16Length; } // For convenience. 01121 if(RKL_EXPECTED(stringU16Length < NSMaxRange(*matchRange), 0L) && RKL_EXPECTED(exception == NULL, 1L)) { exception = (id)RKL_EXCEPTION(NSRangeException, @"Range or index out of bounds."); goto exitNow; } 01122 if(RKL_EXPECTED(stringU16Length >= (NSUInteger)INT_MAX, 0L) && RKL_EXPECTED(exception == NULL, 1L)) { exception = (id)RKL_EXCEPTION(NSRangeException, @"String length exceeds INT_MAX."); goto exitNow; } 01123 if(((maskedRegexOp == RKLRangeOp) || (maskedRegexOp == RKLArrayOfStringsOp)) && RKL_EXPECTED(cachedRegex != NULL, 1L) && (RKL_EXPECTED(capture < 0L, 0L) || RKL_EXPECTED(capture > cachedRegex->captureCount, 0L)) && RKL_EXPECTED(exception == NULL, 1L)) { exception = (id)RKL_EXCEPTION(NSInvalidArgumentException, @"The capture argument is not valid."); goto exitNow; } 01124 01125 if((dictionaryOp == YES) && RKL_EXPECTED(cachedRegex != NULL, 1L) && RKL_EXPECTED(exception == NULL, 1L)) { 01126 for(tmpIdx = 0UL; tmpIdx < captureKeysCount; tmpIdx++) { 01127 if(RKL_EXPECTED(captureKeys[tmpIdx] == NULL, 0L)) { exception = (id)RKL_EXCEPTION(NSInvalidArgumentException, @"The capture key (key %lu of %lu) is NULL.", (unsigned long)(tmpIdx + 1UL), (unsigned long)captureKeysCount); break; } 01128 if((RKL_EXPECTED(captureKeyIndexes[tmpIdx] < 0, 0L) || RKL_EXPECTED(captureKeyIndexes[tmpIdx] > cachedRegex->captureCount, 0L))) { exception = (id)RKL_EXCEPTION(NSInvalidArgumentException, @"The capture argument %d (capture %lu of %lu) for key '%@' is not valid.", captureKeyIndexes[tmpIdx], (unsigned long)(tmpIdx + 1UL), (unsigned long)captureKeysCount, captureKeys[tmpIdx]); break; } 01129 } 01130 } 01131 01132 if(RKL_EXPECTED(cachedRegex == NULL, 0L) || RKL_EXPECTED(status > U_ZERO_ERROR, 0L) || RKL_EXPECTED(exception != NULL, 0L)) { goto exitNow; } 01133 01134 RKLCDelayedAssert(((cachedRegex >= rkl_cachedRegexes) && ((cachedRegex - rkl_cachedRegexes) < (ssize_t)_RKL_REGEX_CACHE_LINES)) && (cachedRegex != NULL) && (cachedRegex->icu_regex != NULL) && (cachedRegex->regexString != NULL) && (cachedRegex->captureCount >= 0L) && (cachedRegex->setToString != NULL) && (cachedRegex->setToLength >= 0L) && (cachedRegex->setToUniChar != NULL) && ((CFIndex)NSMaxRange(cachedRegex->setToRange) <= cachedRegex->setToLength), &exception, exitNow); 01135 RKLCDelayedAssert((cachedRegex->setToNeedsConversion == 0U) ? ((cachedRegex->setToNeedsConversion == 0U) && (cachedRegex->setToUniChar == CFStringGetCharactersPtr(cachedRegex->setToString))) : ((cachedRegex->buffer != NULL) && (cachedRegex->setToHash == cachedRegex->buffer->hash) && (cachedRegex->setToLength == cachedRegex->buffer->length) && (cachedRegex->setToUniChar == cachedRegex->buffer->uniChar)), &exception, exitNow); 01136 01137 switch(maskedRegexOp) { 01138 case RKLRangeOp: 01139 if((RKL_EXPECTED(rkl_search(cachedRegex, matchRange, 0UL, &exception, &status) == NO, 0L)) || (RKL_EXPECTED(status > U_ZERO_ERROR, 0L))) { *(NSRange *)result = NSNotFoundRange; goto exitNow; } 01140 if(RKL_EXPECTED(capture == 0L, 1L)) { *(NSRange *)result = cachedRegex->lastMatchRange; } else { if(RKL_EXPECTED(rkl_getRangeForCapture(cachedRegex, &status, (int32_t)capture, (NSRange *)result) > U_ZERO_ERROR, 0L)) { goto exitNow; } } 01141 break; 01142 01143 case RKLSplitOp: // Fall-thru... 01144 case RKLArrayOfStringsOp: // Fall-thru... 01145 case RKLCapturesArrayOp: // Fall-thru... 01146 case RKLArrayOfCapturesOp: // Fall-thru... 01147 case RKLDictionaryOfCapturesOp: // Fall-thru... 01148 case RKLArrayOfDictionariesOfCapturesOp: 01149 findAll = rkl_makeFindAll(stackRanges, *matchRange, 2048L, (2048UL * sizeof(NSRange)), 0UL, &rkl_scratchBuffer[0], &rkl_scratchBuffer[1], &rkl_scratchBuffer[2], &rkl_scratchBuffer[3], &rkl_scratchBuffer[4], 0L, capture, (((maskedRegexOp == RKLCapturesArrayOp) || (maskedRegexOp == RKLDictionaryOfCapturesOp)) ? 1L : NSIntegerMax)); 01150 01151 if(RKL_EXPECTED(rkl_findRanges(cachedRegex, regexOp, &findAll, &exception, &status) == NO, 1L)) { 01152 if(RKL_EXPECTED(findAll.found == 0L, 0L)) { resultObject = (maskedRegexOp == RKLDictionaryOfCapturesOp) ? [NSDictionary dictionary] : [NSArray array]; } 01153 else { 01154 if(dictionaryOp == YES) { resultObject = rkl_makeDictionary (cachedRegex, regexOp, &findAll, captureKeysCount, captureKeys, captureKeyIndexes, &exception); } 01155 else { resultObject = rkl_makeArray (cachedRegex, regexOp, &findAll, &exception); } 01156 } 01157 } 01158 01159 for(tmpIdx = 0UL; tmpIdx < _RKL_SCRATCH_BUFFERS; tmpIdx++) { if(RKL_EXPECTED(rkl_scratchBuffer[tmpIdx] != NULL, 0L)) { rkl_scratchBuffer[tmpIdx] = rkl_free(&rkl_scratchBuffer[tmpIdx]); } } 01160 01161 break; 01162 01163 case RKLReplaceOp: resultObject = rkl_replaceString(cachedRegex, matchString, stringU16Length, replacementString, (NSUInteger)CFStringGetLength((CFStringRef)replacementString), (NSInteger *)result, (replaceMutable = (((regexOp & RKLReplaceMutable) != 0) ? 1UL : 0UL)), &exception, &status); break; 01164 01165 default: exception = RKLCAssertDictionary(@"Unknown regexOp code."); break; 01166 } 01167 01168 exitNow: 01169 OSSpinLockUnlock(&rkl_cacheSpinLock); 01170 rkl_cacheSpinLockStatus |= RKLUnlockedCacheSpinLock; // Warning about rkl_cacheSpinLockStatus never being read can be safely ignored. 01171 01172 if(RKL_EXPECTED(status > U_ZERO_ERROR, 0L) && RKL_EXPECTED(exception == NULL, 0L)) { exception = rkl_NSExceptionForRegex(regexString, options, NULL, status); } // If we had a problem, prepare an exception to be thrown. 01173 if(RKL_EXPECTED(exception != NULL, 0L)) { rkl_handleDelayedAssert(self, _cmd, exception); } // If there is an exception, throw it at this point. 01174 // If we're working on a mutable string and there were successful matches/replacements, then we still have work to do. 01175 // This is done outside the cache lock and with the objc replaceCharactersInRange:withString: method because Core Foundation 01176 // does not assert that the string we are attempting to update is actually a mutable string, whereas Foundation ensures 01177 // the object receiving the message is a mutable string and throws an exception if we're attempting to modify an immutable string. 01178 if(RKL_EXPECTED(replaceMutable == 1UL, 0L) && RKL_EXPECTED(*((NSInteger *)result) > 0L, 1L) && RKL_EXPECTED(status == U_ZERO_ERROR, 1L) && RKL_EXPECTED(resultObject != NULL, 1L)) { [matchString replaceCharactersInRange:*matchRange withString:resultObject]; } 01179 // If status < U_ZERO_ERROR, consider it an error, even though status < U_ZERO_ERROR is a 'warning' in ICU nomenclature. 01180 // status > U_ZERO_ERROR are an exception and handled above. 01181 // http://sourceforge.net/tracker/?func=detail&atid=990188&aid=2890810&group_id=204582 01182 if(RKL_EXPECTED(status < U_ZERO_ERROR, 0L) && RKL_EXPECTED(resultObject == NULL, 0L) && (error != NULL)) { 01183 NSString *replacedString = NULL; 01184 NSInteger replacedCount = 0L; 01185 RKLUserInfoOptions userInfoOptions = RKLUserInfoNone; 01186 if((maskedRegexOp == RKLReplaceOp) && (result != NULL)) { userInfoOptions |= RKLUserInfoReplacedCount; replacedString = resultObject; replacedCount = *((NSInteger *)result); } 01187 if(matchRange != NULL) { userInfoOptions |= RKLUserInfoSubjectRange; } 01188 *error = rkl_makeNSError(userInfoOptions, regexString, options, NULL, status, matchString, (matchRange != NULL) ? *matchRange : NSNotFoundRange, replacementString, replacedString, replacedCount, (RKLRegexEnumerationOptions)RKLRegexEnumerationNoOptions, @"The ICU library returned an unexpected error."); 01189 } 01190 return(resultObject); 01191 } 01192 01193 static void rkl_handleDelayedAssert(id self, SEL _cmd, id exception) { 01194 if(RKL_EXPECTED(exception != NULL, 1L)) { 01195 if([exception isKindOfClass:[NSException class]]) { [[NSException exceptionWithName:[exception name] reason:rkl_stringFromClassAndMethod(self, _cmd, [exception reason]) userInfo:[exception userInfo]] raise]; } 01196 else { 01197 id functionString = [exception objectForKey:@"function"], fileString = [exception objectForKey:@"file"], descriptionString = [exception objectForKey:@"description"], lineNumber = [exception objectForKey:@"line"]; 01198 RKLCHardAbortAssert((functionString != NULL) && (fileString != NULL) && (descriptionString != NULL) && (lineNumber != NULL)); 01199 [[NSAssertionHandler currentHandler] handleFailureInFunction:functionString file:fileString lineNumber:(NSInteger)[lineNumber longValue] description:descriptionString]; 01200 } 01201 } 01202 } 01203 01204 // IMPORTANT! This code is critical path code. Because of this, it has been written for speed, not clarity. 01205 // IMPORTANT! Should only be called from rkl_performRegexOp() or rkl_findRanges(). 01206 // ---------- 01207 01208 #pragma mark Primary means of performing a search with a regular expression 01209 01210 static NSUInteger rkl_search(RKLCachedRegex *cachedRegex, NSRange *searchRange, NSUInteger updateSearchRange, id *exception RKL_UNUSED_ASSERTION_ARG, int32_t *status) { 01211 NSUInteger foundMatch = 0UL; 01212 01213 if((NSEqualRanges(*searchRange, cachedRegex->lastFindRange) == YES) && ((cachedRegex->lastMatchRange.length > 0UL) || (cachedRegex->lastMatchRange.location == (NSUInteger)NSNotFound))) { foundMatch = ((cachedRegex->lastMatchRange.location == (NSUInteger)NSNotFound) ? 0UL : 1UL);} 01214 else { // Only perform an expensive 'find' operation iff the current find range is different than the last find range. 01215 NSUInteger findLocation = (searchRange->location - cachedRegex->setToRange.location); 01216 RKLCDelayedAssert(((searchRange->location >= cachedRegex->setToRange.location)) && (NSRangeInsideRange(*searchRange, cachedRegex->setToRange) == YES) && (findLocation < INT_MAX) && (findLocation <= cachedRegex->setToRange.length), exception, exitNow); 01217 01218 RKL_PREFETCH_UNICHAR(cachedRegex->setToUniChar, searchRange->location); // Spool up the CPU caches. 01219 01220 // Using uregex_findNext can be a slight performance win. 01221 NSUInteger useFindNext = (RKL_EXPECTED(searchRange->location == (NSMaxRange(cachedRegex->lastMatchRange) + ((RKL_EXPECTED(cachedRegex->lastMatchRange.length == 0UL, 0L) && RKL_EXPECTED(cachedRegex->lastMatchRange.location < NSMaxRange(cachedRegex->setToRange), 0L)) ? 1UL : 0UL)), 1L) ? 1UL : 0UL); 01222 01223 cachedRegex->lastFindRange = *searchRange; 01224 if(RKL_EXPECTED(useFindNext == 0UL, 0L)) { if(RKL_EXPECTED((RKL_ICU_FUNCTION_APPEND(uregex_find) (cachedRegex->icu_regex, (int32_t)findLocation, status) == NO), 0L) || RKL_EXPECTED(*status > U_ZERO_ERROR, 0L)) { goto finishedFind; } } 01225 else { if(RKL_EXPECTED((RKL_ICU_FUNCTION_APPEND(uregex_findNext)(cachedRegex->icu_regex, status) == NO), 0L) || RKL_EXPECTED(*status > U_ZERO_ERROR, 0L)) { goto finishedFind; } } 01226 foundMatch = 1UL; 01227 01228 if(RKL_EXPECTED(rkl_getRangeForCapture(cachedRegex, status, 0, &cachedRegex->lastMatchRange) > U_ZERO_ERROR, 0L)) { goto finishedFind; } 01229 RKLCDelayedAssert(NSRangeInsideRange(cachedRegex->lastMatchRange, *searchRange) == YES, exception, exitNow); 01230 } 01231 01232 finishedFind: 01233 if(RKL_EXPECTED(*status > U_ZERO_ERROR, 0L)) { foundMatch = 0UL; cachedRegex->lastFindRange = NSNotFoundRange; } 01234 01235 if(RKL_EXPECTED(foundMatch == 0UL, 0L)) { cachedRegex->lastFindRange = NSNotFoundRange; cachedRegex->lastMatchRange = NSNotFoundRange; if(RKL_EXPECTED(updateSearchRange == 1UL, 1L)) { *searchRange = NSMakeRange(NSMaxRange(*searchRange), 0UL); } } 01236 else { 01237 RKLCDelayedAssert(NSRangeInsideRange(cachedRegex->lastMatchRange, *searchRange) == YES, exception, exitNow); 01238 if(RKL_EXPECTED(updateSearchRange == 1UL, 1L)) { 01239 NSUInteger nextLocation = (NSMaxRange(cachedRegex->lastMatchRange) + ((RKL_EXPECTED(cachedRegex->lastMatchRange.length == 0UL, 0L) && RKL_EXPECTED(cachedRegex->lastMatchRange.location < NSMaxRange(cachedRegex->setToRange), 1L)) ? 1UL : 0UL)), locationDiff = nextLocation - searchRange->location; 01240 RKLCDelayedAssert((((locationDiff > 0UL) || ((locationDiff == 0UL) && (cachedRegex->lastMatchRange.location == NSMaxRange(cachedRegex->setToRange)))) && (locationDiff <= searchRange->length)), exception, exitNow); 01241 searchRange->location = nextLocation; 01242 searchRange->length -= locationDiff; 01243 } 01244 } 01245 01246 #ifndef NS_BLOCK_ASSERTIONS 01247 exitNow: 01248 #endif 01249 return(foundMatch); 01250 } 01251 01252 // IMPORTANT! This code is critical path code. Because of this, it has been written for speed, not clarity. 01253 // IMPORTANT! Should only be called from rkl_performRegexOp() or rkl_performEnumerationUsingBlock(). 01254 // ---------- 01255 01256 #pragma mark Used to perform multiple searches at once and return the NSRange results in bulk 01257 01258 static BOOL rkl_findRanges(RKLCachedRegex *cachedRegex, RKLRegexOp regexOp, RKLFindAll *findAll, id *exception, int32_t *status) { 01259 BOOL returnWithError = YES; 01260 RKLCDelayedAssert((((cachedRegex != NULL) && (cachedRegex->icu_regex != NULL) && (cachedRegex->setToUniChar != NULL) && (cachedRegex->captureCount >= 0L) && (cachedRegex->setToRange.location != (NSUInteger)NSNotFound)) && (status != NULL) && ((findAll != NULL) && (findAll->found == 0L) && (findAll->addedSplitRanges == 0L) && ((findAll->capacity >= 0L) && (((findAll->capacity > 0L) || (findAll->size > 0UL)) ? ((findAll->ranges != NULL) && (findAll->capacity > 0L) && (findAll->size > 0UL)) : 1)) && ((findAll->capture >= 0L) && (findAll->capture <= cachedRegex->captureCount)))), exception, exitNow); 01261 01262 NSInteger captureCount = cachedRegex->captureCount, findAllRangeIndexOfLastNonZeroLength = 0L; 01263 NSUInteger lastLocation = findAll->findInRange.location; 01264 RKLRegexOp maskedRegexOp = (regexOp & RKLMaskOp); 01265 NSRange searchRange = findAll->findInRange; 01266 01267 for(findAll->found = 0L; (findAll->found < findAll->findUpTo) && ((findAll->found < findAll->capacity) || (findAll->found == 0L)); findAll->found++) { 01268 NSInteger loopCapture, shouldBreak = 0L; 01269 01270 if(RKL_EXPECTED(findAll->found >= ((findAll->capacity - ((captureCount + 2L) * 4L)) - 4L), 0L)) { if(RKL_EXPECTED(rkl_growFindRanges(cachedRegex, lastLocation, findAll, exception) == 0UL, 0L)) { goto exitNow; } } 01271 01272 RKLCDelayedAssert((searchRange.location != (NSUInteger)NSNotFound) && (NSRangeInsideRange(searchRange, cachedRegex->setToRange) == YES) && (NSRangeInsideRange(findAll->findInRange, cachedRegex->setToRange) == YES), exception, exitNow); 01273 01274 // This fixes a 'bug' that is also present in ICU's uregex_split(). 'Bug', in this case, means that the results of a split operation can differ from those that perl's split() creates for the same input. 01275 // "I|at|ice I eat rice" split using the regex "\b\s*" demonstrates the problem. ICU bug http://bugs.icu-project.org/trac/ticket/6826 01276 // ICU : "", "I", "|", "at", "|", "ice", "", "I", "", "eat", "", "rice" <- Results that RegexKitLite used to produce. 01277 // PERL: "I", "|", "at", "|", "ice", "I", "eat", "rice" <- Results that RegexKitLite now produces. 01278 do { if((rkl_search(cachedRegex, &searchRange, 1UL, exception, status) == NO) || (RKL_EXPECTED(*status > U_ZERO_ERROR, 0L))) { shouldBreak = 1L; } findAll->remainingRange = searchRange; } 01279 while(RKL_EXPECTED((cachedRegex->lastMatchRange.location - lastLocation) == 0UL, 0L) && RKL_EXPECTED(cachedRegex->lastMatchRange.length == 0UL, 0L) && (maskedRegexOp == RKLSplitOp) && RKL_EXPECTED(shouldBreak == 0L, 1L)); 01280 if(RKL_EXPECTED(shouldBreak == 1L, 0L)) { break; } 01281 01282 RKLCDelayedAssert((searchRange.location != (NSUInteger)NSNotFound) && (NSRangeInsideRange(searchRange, cachedRegex->setToRange) == YES) && (NSRangeInsideRange(findAll->findInRange, cachedRegex->setToRange) == YES) && (NSRangeInsideRange(searchRange, findAll->findInRange) == YES), exception, exitNow); 01283 RKLCDelayedAssert((NSRangeInsideRange(cachedRegex->lastFindRange, cachedRegex->setToRange) == YES) && (NSRangeInsideRange(cachedRegex->lastMatchRange, cachedRegex->setToRange) == YES) && (NSRangeInsideRange(cachedRegex->lastMatchRange, findAll->findInRange) == YES), exception, exitNow); 01284 RKLCDelayedAssert((findAll->ranges != NULL) && (findAll->found >= 0L) && (findAll->capacity >= 0L) && ((findAll->found + (captureCount + 3L) + 1L) < (findAll->capacity - 2L)), exception, exitNow); 01285 01286 NSInteger findAllRangesIndexForCapture0 = findAll->found; 01287 switch(maskedRegexOp) { 01288 case RKLArrayOfStringsOp: 01289 if(findAll->capture == 0L) { findAll->ranges[findAll->found] = cachedRegex->lastMatchRange; } else { if(RKL_EXPECTED(rkl_getRangeForCapture(cachedRegex, status, (int32_t)findAll->capture, &findAll->ranges[findAll->found]) > U_ZERO_ERROR, 0L)) { goto exitNow; } } 01290 break; 01291 01292 case RKLSplitOp: // Fall-thru... 01293 case RKLCapturesArrayOp: // Fall-thru... 01294 case RKLDictionaryOfCapturesOp: // Fall-thru... 01295 case RKLArrayOfDictionariesOfCapturesOp: // Fall-thru... 01296 case RKLArrayOfCapturesOp: 01297 findAll->ranges[findAll->found] = ((maskedRegexOp == RKLSplitOp) ? NSMakeRange(lastLocation, cachedRegex->lastMatchRange.location - lastLocation) : cachedRegex->lastMatchRange); 01298 01299 for(loopCapture = 1L; loopCapture <= captureCount; loopCapture++) { 01300 RKLCDelayedAssert((findAll->found >= 0L) && (findAll->found < (findAll->capacity - 2L)) && (loopCapture < INT_MAX), exception, exitNow); 01301 if(RKL_EXPECTED(rkl_getRangeForCapture(cachedRegex, status, (int32_t)loopCapture, &findAll->ranges[++findAll->found]) > U_ZERO_ERROR, 0L)) { goto exitNow; } 01302 } 01303 break; 01304 01305 default: if(*exception == NULL) { *exception = RKLCAssertDictionary(@"Unknown regexOp."); } goto exitNow; break; 01306 } 01307 01308 if(findAll->ranges[findAllRangesIndexForCapture0].length > 0UL) { findAllRangeIndexOfLastNonZeroLength = findAll->found + 1UL; } 01309 lastLocation = NSMaxRange(cachedRegex->lastMatchRange); 01310 } 01311 01312 if(RKL_EXPECTED(*status > U_ZERO_ERROR, 0L)) { goto exitNow; } 01313 01314 RKLCDelayedAssert((findAll->ranges != NULL) && (findAll->found >= 0L) && (findAll->found < (findAll->capacity - 2L)), exception, exitNow); 01315 if(maskedRegexOp == RKLSplitOp) { 01316 if(lastLocation != NSMaxRange(findAll->findInRange)) { findAll->addedSplitRanges++; findAll->ranges[findAll->found++] = NSMakeRange(lastLocation, NSMaxRange(findAll->findInRange) - lastLocation); findAllRangeIndexOfLastNonZeroLength = findAll->found; } 01317 findAll->found = findAllRangeIndexOfLastNonZeroLength; 01318 } 01319 01320 RKLCDelayedAssert((findAll->ranges != NULL) && (findAll->found >= 0L) && (findAll->found < (findAll->capacity - 2L)), exception, exitNow); 01321 returnWithError = NO; 01322 01323 exitNow: 01324 return(returnWithError); 01325 } 01326 01327 // IMPORTANT! This code is critical path code. Because of this, it has been written for speed, not clarity. 01328 // IMPORTANT! Should only be called from rkl_findRanges(). 01329 // ---------- 01330 01331 static NSUInteger rkl_growFindRanges(RKLCachedRegex *cachedRegex, NSUInteger lastLocation, RKLFindAll *findAll, id *exception RKL_UNUSED_ASSERTION_ARG) { 01332 NSUInteger didGrowRanges = 0UL; 01333 RKLCDelayedAssert((((cachedRegex != NULL) && (cachedRegex->captureCount >= 0L)) && ((findAll != NULL) && (findAll->capacity >= 0L) && (findAll->rangesScratchBuffer != NULL) && (findAll->found >= 0L) && (((findAll->capacity > 0L) || (findAll->size > 0UL) || (findAll->ranges != NULL)) ? ((findAll->capacity > 0L) && (findAll->size > 0UL) && (findAll->ranges != NULL) && (((size_t)findAll->capacity * sizeof(NSRange)) == findAll->size)) : 1))), exception, exitNow); 01334 01335 // Attempt to guesstimate the required capacity based on: the total length needed to search / (length we've searched so far / ranges found so far). 01336 NSInteger newCapacity = (findAll->capacity + (findAll->capacity / 2L)), estimate = (NSInteger)((float)cachedRegex->setToLength / (((float)lastLocation + 1.0f) / ((float)findAll->found + 1.0f))); 01337 newCapacity = (((newCapacity + ((estimate > newCapacity) ? estimate : newCapacity)) / 2L) + ((cachedRegex->captureCount + 2L) * 4L) + 4L); 01338 01339 NSUInteger needToCopy = ((*findAll->rangesScratchBuffer != findAll->ranges) && (findAll->ranges != NULL)) ? 1UL : 0UL; // If findAll->ranges is set to a stack allocation then we need to manually copy the data from the stack to the new heap allocation. 01340 size_t newSize = ((size_t)newCapacity * sizeof(NSRange)); 01341 RKL_STRONG_REF NSRange * RKL_GC_VOLATILE newRanges = NULL; 01342 01343 if(RKL_EXPECTED((newRanges = (RKL_STRONG_REF NSRange * RKL_GC_VOLATILE)rkl_realloc((RKL_STRONG_REF void ** RKL_GC_VOLATILE)findAll->rangesScratchBuffer, newSize, 0UL)) == NULL, 0L)) { findAll->capacity = 0L; findAll->size = 0UL; findAll->ranges = NULL; *findAll->rangesScratchBuffer = rkl_free((RKL_STRONG_REF void ** RKL_GC_VOLATILE)findAll->rangesScratchBuffer); goto exitNow; } else { didGrowRanges = 1UL; } 01344 if(needToCopy == 1UL) { memcpy(newRanges, findAll->ranges, findAll->size); } // If necessary, copy the existing data to the new heap allocation. 01345 01346 findAll->capacity = newCapacity; 01347 findAll->size = newSize; 01348 findAll->ranges = newRanges; 01349 01350 exitNow: 01351 return(didGrowRanges); 01352 } 01353 01354 // IMPORTANT! This code is critical path code. Because of this, it has been written for speed, not clarity. 01355 // IMPORTANT! Should only be called from rkl_performRegexOp(). 01356 // ---------- 01357 01358 #pragma mark Convert bulk results from rkl_findRanges in to various NSArray types 01359 01360 static NSArray *rkl_makeArray(RKLCachedRegex *cachedRegex, RKLRegexOp regexOp, RKLFindAll *findAll, id *exception RKL_UNUSED_ASSERTION_ARG) { 01361 NSUInteger createdStringsCount = 0UL, createdArraysCount = 0UL, transferredStringsCount = 0UL; 01362 id * RKL_GC_VOLATILE matchedStrings = NULL, * RKL_GC_VOLATILE subcaptureArrays = NULL, emptyString = @""; 01363 NSArray * RKL_GC_VOLATILE resultArray = NULL; 01364 01365 RKLCDelayedAssert((cachedRegex != NULL) && ((findAll != NULL) && (findAll->found >= 0L) && (findAll->stringsScratchBuffer != NULL) && (findAll->arraysScratchBuffer != NULL)), exception, exitNow); 01366 01367 size_t matchedStringsSize = ((size_t)findAll->found * sizeof(id)); 01368 CFStringRef setToString = cachedRegex->setToString; 01369 01370 if((findAll->stackUsed + matchedStringsSize) < (size_t)_RKL_STACK_LIMIT) { if(RKL_EXPECTED((matchedStrings = (id * RKL_GC_VOLATILE)alloca(matchedStringsSize)) == NULL, 0L)) { goto exitNow; } findAll->stackUsed += matchedStringsSize; } 01371 else { if(RKL_EXPECTED((matchedStrings = (id * RKL_GC_VOLATILE)rkl_realloc(findAll->stringsScratchBuffer, matchedStringsSize, (NSUInteger)RKLScannedOption)) == NULL, 0L)) { goto exitNow; } } 01372 01373 { // This sub-block (and its local variables) is here for the benefit of the optimizer. 01374 NSUInteger found = (NSUInteger)findAll->found; 01375 const NSRange *rangePtr = findAll->ranges; 01376 id *matchedStringsPtr = matchedStrings; 01377 01378 for(createdStringsCount = 0UL; createdStringsCount < found; createdStringsCount++) { 01379 NSRange range = *rangePtr++; 01380 if(RKL_EXPECTED(((*matchedStringsPtr++ = RKL_EXPECTED(range.length == 0UL, 0L) ? emptyString : rkl_CreateStringWithSubstring((id)setToString, range)) == NULL), 0L)) { goto exitNow; } 01381 } 01382 } 01383 01384 NSUInteger arrayCount = createdStringsCount; 01385 id * RKL_GC_VOLATILE arrayObjects = matchedStrings; 01386 01387 if((regexOp & RKLSubcapturesArray) != 0UL) { 01388 RKLCDelayedAssert(((createdStringsCount % ((NSUInteger)cachedRegex->captureCount + 1UL)) == 0UL) && (createdArraysCount == 0UL), exception, exitNow); 01389 01390 NSUInteger captureCount = ((NSUInteger)cachedRegex->captureCount + 1UL); 01391 NSUInteger subcaptureArraysCount = (createdStringsCount / captureCount); 01392 size_t subcaptureArraysSize = ((size_t)subcaptureArraysCount * sizeof(id)); 01393 01394 if((findAll->stackUsed + subcaptureArraysSize) < (size_t)_RKL_STACK_LIMIT) { if(RKL_EXPECTED((subcaptureArrays = (id * RKL_GC_VOLATILE)alloca(subcaptureArraysSize)) == NULL, 0L)) { goto exitNow; } findAll->stackUsed += subcaptureArraysSize; } 01395 else { if(RKL_EXPECTED((subcaptureArrays = (id * RKL_GC_VOLATILE)rkl_realloc(findAll->arraysScratchBuffer, subcaptureArraysSize, (NSUInteger)RKLScannedOption)) == NULL, 0L)) { goto exitNow; } } 01396 01397 { // This sub-block (and its local variables) is here for the benefit of the optimizer. 01398 id *subcaptureArraysPtr = subcaptureArrays; 01399 id *matchedStringsPtr = matchedStrings; 01400 01401 for(createdArraysCount = 0UL; createdArraysCount < subcaptureArraysCount; createdArraysCount++) { 01402 if(RKL_EXPECTED((*subcaptureArraysPtr++ = rkl_CreateArrayWithObjects((void **)matchedStringsPtr, captureCount)) == NULL, 0L)) { goto exitNow; } 01403 matchedStringsPtr += captureCount; 01404 transferredStringsCount += captureCount; 01405 } 01406 } 01407 01408 RKLCDelayedAssert((transferredStringsCount == createdStringsCount), exception, exitNow); 01409 arrayCount = createdArraysCount; 01410 arrayObjects = subcaptureArrays; 01411 } 01412 01413 RKLCDelayedAssert((arrayObjects != NULL), exception, exitNow); 01414 resultArray = rkl_CreateAutoreleasedArray((void **)arrayObjects, (NSUInteger)arrayCount); 01415 01416 exitNow: 01417 if(RKL_EXPECTED(resultArray == NULL, 0L) && (rkl_collectingEnabled() == NO)) { // If we did not create an array then we need to make sure that we release any objects we created. 01418 NSUInteger x; 01419 if(matchedStrings != NULL) { for(x = transferredStringsCount; x < createdStringsCount; x++) { if((matchedStrings[x] != NULL) && (matchedStrings[x] != emptyString)) { matchedStrings[x] = rkl_ReleaseObject(matchedStrings[x]); } } } 01420 if(subcaptureArrays != NULL) { for(x = 0UL; x < createdArraysCount; x++) { if(subcaptureArrays[x] != NULL) { subcaptureArrays[x] = rkl_ReleaseObject(subcaptureArrays[x]); } } } 01421 } 01422 01423 return(resultArray); 01424 } 01425 01426 // IMPORTANT! This code is critical path code. Because of this, it has been written for speed, not clarity. 01427 // IMPORTANT! Should only be called from rkl_performRegexOp(). 01428 // ---------- 01429 01430 #pragma mark Convert bulk results from rkl_findRanges in to various NSDictionary types 01431 01432 static id rkl_makeDictionary(RKLCachedRegex *cachedRegex, RKLRegexOp regexOp, RKLFindAll *findAll, NSUInteger captureKeysCount, id captureKeys[captureKeysCount], const int captureKeyIndexes[captureKeysCount], id *exception RKL_UNUSED_ASSERTION_ARG) { 01433 NSUInteger matchedStringIndex = 0UL, createdStringsCount = 0UL, createdDictionariesCount = 0UL, matchedDictionariesCount = (findAll->found / (cachedRegex->captureCount + 1UL)), transferredDictionariesCount = 0UL; 01434 id * RKL_GC_VOLATILE matchedStrings = NULL, * RKL_GC_VOLATILE matchedKeys = NULL, emptyString = @""; 01435 id RKL_GC_VOLATILE returnObject = NULL; 01436 NSDictionary ** RKL_GC_VOLATILE matchedDictionaries = NULL; 01437 01438 RKLCDelayedAssert((cachedRegex != NULL) && ((findAll != NULL) && (findAll->found >= 0L) && (findAll->stringsScratchBuffer != NULL) && (findAll->dictionariesScratchBuffer != NULL) && (findAll->keysScratchBuffer != NULL) && (captureKeyIndexes != NULL)), exception, exitNow); 01439 01440 CFStringRef setToString = cachedRegex->setToString; 01441 01442 size_t matchedStringsSize = ((size_t)captureKeysCount * sizeof(void *)); 01443 if((findAll->stackUsed + matchedStringsSize) < (size_t)_RKL_STACK_LIMIT) { if(RKL_EXPECTED((matchedStrings = (id * RKL_GC_VOLATILE)alloca(matchedStringsSize)) == NULL, 0L)) { goto exitNow; } findAll->stackUsed += matchedStringsSize; } 01444 else { if(RKL_EXPECTED((matchedStrings = (id * RKL_GC_VOLATILE)rkl_realloc(findAll->stringsScratchBuffer, matchedStringsSize, (NSUInteger)RKLScannedOption)) == NULL, 0L)) { goto exitNow; } } 01445 01446 size_t matchedKeysSize = ((size_t)captureKeysCount * sizeof(void *)); 01447 if((findAll->stackUsed + matchedKeysSize) < (size_t)_RKL_STACK_LIMIT) { if(RKL_EXPECTED((matchedKeys = (id * RKL_GC_VOLATILE)alloca(matchedKeysSize)) == NULL, 0L)) { goto exitNow; } findAll->stackUsed += matchedKeysSize; } 01448 else { if(RKL_EXPECTED((matchedKeys = (id * RKL_GC_VOLATILE)rkl_realloc(findAll->keysScratchBuffer, matchedKeysSize, (NSUInteger)RKLScannedOption)) == NULL, 0L)) { goto exitNow; } } 01449 01450 size_t matchedDictionariesSize = ((size_t)matchedDictionariesCount * sizeof(NSDictionary *)); 01451 if((findAll->stackUsed + matchedDictionariesSize) < (size_t)_RKL_STACK_LIMIT) { if(RKL_EXPECTED((matchedDictionaries = (NSDictionary ** RKL_GC_VOLATILE)alloca(matchedDictionariesSize)) == NULL, 0L)) { goto exitNow; } findAll->stackUsed += matchedDictionariesSize; } 01452 else { if(RKL_EXPECTED((matchedDictionaries = (NSDictionary ** RKL_GC_VOLATILE)rkl_realloc(findAll->dictionariesScratchBuffer, matchedDictionariesSize, (NSUInteger)RKLScannedOption)) == NULL, 0L)) { goto exitNow; } } 01453 01454 { // This sub-block (and its local variables) is here for the benefit of the optimizer. 01455 NSUInteger captureCount = cachedRegex->captureCount; 01456 NSDictionary **matchedDictionariesPtr = matchedDictionaries; 01457 01458 for(createdDictionariesCount = 0UL; createdDictionariesCount < matchedDictionariesCount; createdDictionariesCount++) { 01459 RKLCDelayedAssert(((createdDictionariesCount * captureCount) < (NSUInteger)findAll->found), exception, exitNow); 01460 RKL_STRONG_REF const NSRange * RKL_GC_VOLATILE rangePtr = &findAll->ranges[(createdDictionariesCount * (captureCount + 1UL))]; 01461 for(matchedStringIndex = 0UL; matchedStringIndex < captureKeysCount; matchedStringIndex++) { 01462 NSRange range = rangePtr[captureKeyIndexes[matchedStringIndex]]; 01463 if(RKL_EXPECTED(range.location != NSNotFound, 0L)) { 01464 if(RKL_EXPECTED(((matchedStrings[createdStringsCount] = RKL_EXPECTED(range.length == 0UL, 0L) ? emptyString : rkl_CreateStringWithSubstring((id)setToString, range)) == NULL), 0L)) { goto exitNow; } 01465 matchedKeys[createdStringsCount] = captureKeys[createdStringsCount]; 01466 createdStringsCount++; 01467 } 01468 } 01469 RKLCDelayedAssert((matchedStringIndex <= captureCount), exception, exitNow); 01470 if(RKL_EXPECTED(((*matchedDictionariesPtr++ = (NSDictionary * RKL_GC_VOLATILE)CFDictionaryCreate(NULL, (const void **)matchedKeys, (const void **)matchedStrings, (CFIndex)createdStringsCount, &rkl_transferOwnershipDictionaryKeyCallBacks, &rkl_transferOwnershipDictionaryValueCallBacks)) == NULL), 0L)) { goto exitNow; } 01471 createdStringsCount = 0UL; 01472 } 01473 } 01474 01475 if(createdDictionariesCount > 0UL) { 01476 if((regexOp & RKLMaskOp) == RKLArrayOfDictionariesOfCapturesOp) { 01477 RKLCDelayedAssert((matchedDictionaries != NULL) && (createdDictionariesCount > 0UL), exception, exitNow); 01478 if((returnObject = rkl_CreateAutoreleasedArray((void **)matchedDictionaries, createdDictionariesCount)) == NULL) { goto exitNow; } 01479 transferredDictionariesCount = createdDictionariesCount; 01480 } else { 01481 RKLCDelayedAssert((matchedDictionaries != NULL) && (createdDictionariesCount == 1UL), exception, exitNow); 01482 if((returnObject = rkl_CFAutorelease(matchedDictionaries[0])) == NULL) { goto exitNow; } 01483 transferredDictionariesCount = 1UL; 01484 } 01485 } 01486 01487 exitNow: 01488 RKLCDelayedAssert((createdDictionariesCount <= transferredDictionariesCount) && ((transferredDictionariesCount > 0UL) ? (createdStringsCount == 0UL) : 1), exception, exitNow2); 01489 #ifndef NS_BLOCK_ASSERTIONS 01490 exitNow2: 01491 #endif 01492 01493 if(rkl_collectingEnabled() == NO) { // Release any objects, if necessary. 01494 NSUInteger x; 01495 if(matchedStrings != NULL) { for(x = 0UL; x < createdStringsCount; x++) { if((matchedStrings[x] != NULL) && (matchedStrings[x] != emptyString)) { matchedStrings[x] = rkl_ReleaseObject(matchedStrings[x]); } } } 01496 if(matchedDictionaries != NULL) { for(x = transferredDictionariesCount; x < createdDictionariesCount; x++) { if((matchedDictionaries[x] != NULL)) { matchedDictionaries[x] = rkl_ReleaseObject(matchedDictionaries[x]); } } } 01497 } 01498 01499 return(returnObject); 01500 } 01501 01502 // IMPORTANT! This code is critical path code. Because of this, it has been written for speed, not clarity. 01503 // IMPORTANT! Should only be called from rkl_performRegexOp(). 01504 // ---------- 01505 01506 #pragma mark Perform "search and replace" operations on strings using ICUs uregex_*replace* functions 01507 01508 static NSString *rkl_replaceString(RKLCachedRegex *cachedRegex, id searchString, NSUInteger searchU16Length, NSString *replacementString, NSUInteger replacementU16Length, NSInteger *replacedCountPtr, NSUInteger replaceMutable, id *exception, int32_t *status) { 01509 RKL_STRONG_REF UniChar * RKL_GC_VOLATILE tempUniCharBuffer = NULL; 01510 RKL_STRONG_REF const UniChar * RKL_GC_VOLATILE replacementUniChar = NULL; 01511 uint64_t searchU16Length64 = (uint64_t)searchU16Length, replacementU16Length64 = (uint64_t)replacementU16Length; 01512 int32_t resultU16Length = 0, tempUniCharBufferU16Capacity = 0, needU16Capacity = 0; 01513 id RKL_GC_VOLATILE resultObject = NULL; 01514 NSInteger replacedCount = -1L; 01515 01516 if((RKL_EXPECTED(replacementU16Length64 >= (uint64_t)INT_MAX, 0L) || RKL_EXPECTED(((searchU16Length64 / 2ULL) + (replacementU16Length64 * 2ULL)) >= (uint64_t)INT_MAX, 0L))) { *exception = [NSException exceptionWithName:NSRangeException reason:@"Replacement string length exceeds INT_MAX." userInfo:NULL]; goto exitNow; } 01517 01518 RKLCDelayedAssert((searchU16Length64 < (uint64_t)INT_MAX) && (replacementU16Length64 < (uint64_t)INT_MAX) && (((searchU16Length64 / 2ULL) + (replacementU16Length64 * 2ULL)) < (uint64_t)INT_MAX), exception, exitNow); 01519 01520 // Zero order approximation of the buffer sizes for holding the replaced string or split strings and split strings pointer offsets. As UTF16 code units. 01521 tempUniCharBufferU16Capacity = (int32_t)(16UL + (searchU16Length + (searchU16Length / 2UL)) + (replacementU16Length * 2UL)); 01522 RKLCDelayedAssert((tempUniCharBufferU16Capacity < INT_MAX) && (tempUniCharBufferU16Capacity > 0), exception, exitNow); 01523 01524 // Buffer sizes converted from native units to bytes. 01525 size_t stackSize = 0UL, replacementSize = ((size_t)replacementU16Length * sizeof(UniChar)), tempUniCharBufferSize = ((size_t)tempUniCharBufferU16Capacity * sizeof(UniChar)); 01526 01527 // For the various buffers we require, we first try to allocate from the stack if we're not over the RKL_STACK_LIMIT. If we are, switch to using the heap for the buffer. 01528 if((stackSize + tempUniCharBufferSize) < (size_t)_RKL_STACK_LIMIT) { if(RKL_EXPECTED((tempUniCharBuffer = (RKL_STRONG_REF UniChar * RKL_GC_VOLATILE)alloca(tempUniCharBufferSize)) == NULL, 0L)) { goto exitNow; } stackSize += tempUniCharBufferSize; } 01529 else { if(RKL_EXPECTED((tempUniCharBuffer = (RKL_STRONG_REF UniChar * RKL_GC_VOLATILE)rkl_realloc(&rkl_scratchBuffer[0], tempUniCharBufferSize, 0UL)) == NULL, 0L)) { goto exitNow; } } 01530 01531 // Try to get the pointer to the replacement strings UTF16 data. If we can't, allocate some buffer space, then covert to UTF16. 01532 if((replacementUniChar = CFStringGetCharactersPtr((CFStringRef)replacementString)) == NULL) { 01533 RKL_STRONG_REF UniChar * RKL_GC_VOLATILE uniCharBuffer = NULL; 01534 if((stackSize + replacementSize) < (size_t)_RKL_STACK_LIMIT) { if(RKL_EXPECTED((uniCharBuffer = (RKL_STRONG_REF UniChar * RKL_GC_VOLATILE)alloca(replacementSize)) == NULL, 0L)) { goto exitNow; } stackSize += replacementSize; } 01535 else { if(RKL_EXPECTED((uniCharBuffer = (RKL_STRONG_REF UniChar * RKL_GC_VOLATILE)rkl_realloc(&rkl_scratchBuffer[1], replacementSize, 0UL)) == NULL, 0L)) { goto exitNow; } } 01536 CFStringGetCharacters((CFStringRef)replacementString, CFMakeRange(0L, replacementU16Length), uniCharBuffer); // Convert to a UTF16 string. 01537 replacementUniChar = uniCharBuffer; 01538 } 01539 01540 resultU16Length = rkl_replaceAll(cachedRegex, replacementUniChar, (int32_t)replacementU16Length, tempUniCharBuffer, tempUniCharBufferU16Capacity, &replacedCount, &needU16Capacity, exception, status); 01541 RKLCDelayedAssert((resultU16Length <= tempUniCharBufferU16Capacity) && (needU16Capacity >= resultU16Length) && (needU16Capacity >= 0), exception, exitNow); 01542 if(RKL_EXPECTED((needU16Capacity + 4) >= INT_MAX, 0L)) { *exception = [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Replaced string length exceeds INT_MAX." userInfo:NULL]; goto exitNow; } 01543 01544 if(RKL_EXPECTED(*status == U_BUFFER_OVERFLOW_ERROR, 0L)) { // Our buffer guess(es) were too small. Resize the buffers and try again. 01545 // rkl_replaceAll will turn a status of U_STRING_NOT_TERMINATED_WARNING in to a U_BUFFER_OVERFLOW_ERROR. 01546 // As an extra precaution, we pad out the amount needed by an extra four characters "just in case". 01547 // http://lists.apple.com/archives/Cocoa-dev/2010/Jan/msg01011.html 01548 needU16Capacity += 4; 01549 tempUniCharBufferSize = ((size_t)(tempUniCharBufferU16Capacity = needU16Capacity + 4) * sizeof(UniChar)); // Use needU16Capacity. Bug 2890810. 01550 if((stackSize + tempUniCharBufferSize) < (size_t)_RKL_STACK_LIMIT) { if(RKL_EXPECTED((tempUniCharBuffer = (RKL_STRONG_REF UniChar * RKL_GC_VOLATILE)alloca(tempUniCharBufferSize)) == NULL, 0L)) { goto exitNow; } stackSize += tempUniCharBufferSize; } // Warning about stackSize can be safely ignored. 01551 else { if(RKL_EXPECTED((tempUniCharBuffer = (RKL_STRONG_REF UniChar * RKL_GC_VOLATILE)rkl_realloc(&rkl_scratchBuffer[0], tempUniCharBufferSize, 0UL)) == NULL, 0L)) { goto exitNow; } } 01552 01553 *status = U_ZERO_ERROR; // Make sure the status var is cleared and try again. 01554 resultU16Length = rkl_replaceAll(cachedRegex, replacementUniChar, (int32_t)replacementU16Length, tempUniCharBuffer, tempUniCharBufferU16Capacity, &replacedCount, &needU16Capacity, exception, status); 01555 RKLCDelayedAssert((resultU16Length <= tempUniCharBufferU16Capacity) && (needU16Capacity >= resultU16Length) && (needU16Capacity >= 0), exception, exitNow); 01556 } 01557 01558 // If status != U_ZERO_ERROR, consider it an error, even though status < U_ZERO_ERROR is a 'warning' in ICU nomenclature. 01559 // http://sourceforge.net/tracker/?func=detail&atid=990188&aid=2890810&group_id=204582 01560 if(RKL_EXPECTED(*status != U_ZERO_ERROR, 0L)) { goto exitNow; } // Something went wrong. 01561 01562 RKLCDelayedAssert((replacedCount >= 0L), exception, exitNow); 01563 if(RKL_EXPECTED(resultU16Length == 0, 0L)) { resultObject = @""; } // Optimize the case where the replaced text length == 0 with a @"" string. 01564 else if(RKL_EXPECTED((NSUInteger)resultU16Length == searchU16Length, 0L) && RKL_EXPECTED(replacedCount == 0L, 1L)) { // Optimize the case where the replacement == original by creating a copy. Very fast if self is immutable. 01565 if(replaceMutable == 0UL) { resultObject = rkl_CFAutorelease(CFStringCreateCopy(NULL, (CFStringRef)searchString)); } // .. but only if this is not replacing a mutable self. Warning about potential leak can be safely ignored. 01566 } else { resultObject = rkl_CFAutorelease(CFStringCreateWithCharacters(NULL, tempUniCharBuffer, (CFIndex)resultU16Length)); } // otherwise, create a new string. Warning about potential leak can be safely ignored. 01567 01568 // If replaceMutable == 1UL, we don't do the replacement here. We wait until after we return and unlock the cache lock. 01569 // This is because we may be trying to mutate an immutable string object. 01570 if((replaceMutable == 1UL) && RKL_EXPECTED(replacedCount > 0L, 1L)) { // We're working on a mutable string and there were successful matches with replaced text, so there's work to do. 01571 if(cachedRegex->buffer != NULL) { rkl_clearBuffer(cachedRegex->buffer, 0UL); cachedRegex->buffer = NULL; } 01572 NSUInteger idx = 0UL; 01573 for(idx = 0UL; idx < _RKL_LRU_CACHE_SET_WAYS; idx++) { 01574 RKLBuffer *buffer = ((NSUInteger)cachedRegex->setToLength < _RKL_FIXED_LENGTH) ? &rkl_lruFixedBuffer[idx] : &rkl_lruDynamicBuffer[idx]; 01575 if(RKL_EXPECTED(cachedRegex->setToString == buffer->string, 0L) && (cachedRegex->setToLength == buffer->length) && (cachedRegex->setToHash == buffer->hash)) { rkl_clearBuffer(buffer, 0UL); } 01576 } 01577 rkl_clearCachedRegexSetTo(cachedRegex); // Flush any cached information about this string since it will mutate. 01578 } 01579 01580 exitNow: 01581 if(RKL_EXPECTED(status == NULL, 0L) || RKL_EXPECTED(*status != U_ZERO_ERROR, 0L) || RKL_EXPECTED(exception == NULL, 0L) || RKL_EXPECTED(*exception != NULL, 0L)) { replacedCount = -1L; } 01582 if(rkl_scratchBuffer[0] != NULL) { rkl_scratchBuffer[0] = rkl_free(&rkl_scratchBuffer[0]); } 01583 if(rkl_scratchBuffer[1] != NULL) { rkl_scratchBuffer[1] = rkl_free(&rkl_scratchBuffer[1]); } 01584 if(replacedCountPtr != NULL) { *replacedCountPtr = replacedCount; } 01585 return(resultObject); 01586 } // The two warnings about potential leaks can be safely ignored. 01587 01588 // IMPORTANT! Should only be called from rkl_replaceString(). 01589 // ---------- 01590 // Modified version of the ICU libraries uregex_replaceAll() that keeps count of the number of replacements made. 01591 01592 static int32_t rkl_replaceAll(RKLCachedRegex *cachedRegex, RKL_STRONG_REF const UniChar * RKL_GC_VOLATILE replacementUniChar, int32_t replacementU16Length, UniChar *replacedUniChar, int32_t replacedU16Capacity, NSInteger *replacedCount, int32_t *needU16Capacity, id *exception RKL_UNUSED_ASSERTION_ARG, int32_t *status) { 01593 int32_t u16Length = 0, initialReplacedU16Capacity = replacedU16Capacity; 01594 NSUInteger bufferOverflowed = 0UL; 01595 NSInteger replaced = -1L; 01596 RKLCDelayedAssert((cachedRegex != NULL) && (replacementUniChar != NULL) && (replacedUniChar != NULL) && (replacedCount != NULL) && (needU16Capacity != NULL) && (status != NULL) && (replacementU16Length >= 0) && (replacedU16Capacity >= 0), exception, exitNow); 01597 01598 cachedRegex->lastFindRange = cachedRegex->lastMatchRange = NSNotFoundRange; // Clear the cached find information for this regex so a subsequent find works correctly. 01599 RKL_ICU_FUNCTION_APPEND(uregex_reset)(cachedRegex->icu_regex, 0, status); 01600 01601 // Work around for ICU uregex_reset() bug, see http://bugs.icu-project.org/trac/ticket/6545 01602 // http://sourceforge.net/tracker/index.php?func=detail&aid=2105213&group_id=204582&atid=990188 01603 if(RKL_EXPECTED(cachedRegex->setToRange.length == 0UL, 0L) && (*status == U_INDEX_OUTOFBOUNDS_ERROR)) { *status = U_ZERO_ERROR; } 01604 replaced = 0L; 01605 // This loop originally came from ICU source/i18n/uregex.cpp, uregex_replaceAll. 01606 // There is a bug in that code which causes the size of the buffer required for the replaced text to not be calculated correctly. 01607 // This contains a work around using the variable bufferOverflowed. 01608 // ICU bug: http://bugs.icu-project.org/trac/ticket/6656 01609 // http://sourceforge.net/tracker/index.php?func=detail&aid=2408447&group_id=204582&atid=990188 01610 while(RKL_ICU_FUNCTION_APPEND(uregex_findNext)(cachedRegex->icu_regex, status)) { 01611 replaced++; 01612 u16Length += RKL_ICU_FUNCTION_APPEND(uregex_appendReplacement)(cachedRegex->icu_regex, replacementUniChar, replacementU16Length, &replacedUniChar, &replacedU16Capacity, status); 01613 if(RKL_EXPECTED(*status == U_BUFFER_OVERFLOW_ERROR, 0L)) { bufferOverflowed = 1UL; *status = U_ZERO_ERROR; } 01614 } 01615 if(RKL_EXPECTED(*status == U_BUFFER_OVERFLOW_ERROR, 0L)) { bufferOverflowed = 1UL; *status = U_ZERO_ERROR; } 01616 if(RKL_EXPECTED(*status <= U_ZERO_ERROR, 1L)) { u16Length += RKL_ICU_FUNCTION_APPEND(uregex_appendTail)(cachedRegex->icu_regex, &replacedUniChar, &replacedU16Capacity, status); } 01617 01618 // Try to work around a status of U_STRING_NOT_TERMINATED_WARNING. For now, we treat it as a "Buffer Overflow" error. 01619 // As an extra precaution, in rkl_replaceString, we pad out the amount needed by an extra four characters "just in case". 01620 // http://lists.apple.com/archives/Cocoa-dev/2010/Jan/msg01011.html 01621 if(RKL_EXPECTED(*status == U_STRING_NOT_TERMINATED_WARNING, 0L)) { *status = U_BUFFER_OVERFLOW_ERROR; } 01622 01623 // Check for status <= U_ZERO_ERROR (a 'warning' in ICU nomenclature) rather than just status == U_ZERO_ERROR. 01624 // Under just the right circumstances, status might be equal to U_STRING_NOT_TERMINATED_WARNING. When this occurred, 01625 // rkl_replaceString would never get the U_BUFFER_OVERFLOW_ERROR status, and thus never grow the buffer to the size needed. 01626 // http://sourceforge.net/tracker/?func=detail&atid=990188&aid=2890810&group_id=204582 01627 if(RKL_EXPECTED(bufferOverflowed == 1UL, 0L) && RKL_EXPECTED(*status <= U_ZERO_ERROR, 1L)) { *status = U_BUFFER_OVERFLOW_ERROR; } 01628 01629 #ifndef NS_BLOCK_ASSERTIONS 01630 exitNow: 01631 #endif // NS_BLOCK_ASSERTIONS 01632 if(RKL_EXPECTED(replacedCount != NULL, 1L)) { *replacedCount = replaced; } 01633 if(RKL_EXPECTED(needU16Capacity != NULL, 1L)) { *needU16Capacity = u16Length; } // Use needU16Capacity to return the number of characters that are needed for the completely replaced string. Bug 2890810. 01634 return(initialReplacedU16Capacity - replacedU16Capacity); // Return the number of characters of replacedUniChar that were used. 01635 } 01636 01637 #pragma mark Internal function used to check if a regular expression is valid. 01638 01639 static NSUInteger rkl_isRegexValid(id self, SEL _cmd, NSString *regex, RKLRegexOptions options, NSInteger *captureCountPtr, NSError **error) { 01640 volatile NSUInteger RKL_CLEANUP(rkl_cleanup_cacheSpinLockStatus) rkl_cacheSpinLockStatus = 0UL; 01641 01642 RKLCachedRegex *cachedRegex = NULL; 01643 NSUInteger gotCachedRegex = 0UL; 01644 NSInteger captureCount = -1L; 01645 id exception = NULL; 01646 01647 if((error != NULL) && (*error != NULL)) { *error = NULL; } 01648 if(RKL_EXPECTED(regex == NULL, 0L)) { RKL_RAISE_EXCEPTION(NSInvalidArgumentException, @"The regular expression argument is NULL."); } 01649 01650 OSSpinLockLock(&rkl_cacheSpinLock); 01651 rkl_cacheSpinLockStatus |= RKLLockedCacheSpinLock; 01652 rkl_dtrace_incrementEventID(); 01653 if(RKL_EXPECTED((cachedRegex = rkl_getCachedRegex(regex, options, error, &exception)) != NULL, 1L)) { gotCachedRegex = 1UL; captureCount = cachedRegex->captureCount; } 01654 cachedRegex = NULL; 01655 OSSpinLockUnlock(&rkl_cacheSpinLock); 01656 rkl_cacheSpinLockStatus |= RKLUnlockedCacheSpinLock; // Warning about rkl_cacheSpinLockStatus never being read can be safely ignored. 01657 01658 if(captureCountPtr != NULL) { *captureCountPtr = captureCount; } 01659 if(RKL_EXPECTED(exception != NULL, 0L)) { rkl_handleDelayedAssert(self, _cmd, exception); } 01660 return(gotCachedRegex); 01661 } 01662 01663 #pragma mark Functions used for clearing and releasing resources for various internal data structures 01664 01665 static void rkl_clearStringCache(void) { 01666 RKLCAbortAssert(rkl_cacheSpinLock != (OSSpinLock)0); 01667 rkl_lastCachedRegex = NULL; 01668 NSUInteger x = 0UL; 01669 for(x = 0UL; x < _RKL_SCRATCH_BUFFERS; x++) { if(rkl_scratchBuffer[x] != NULL) { rkl_scratchBuffer[x] = rkl_free(&rkl_scratchBuffer[x]); } } 01670 for(x = 0UL; x < _RKL_REGEX_CACHE_LINES; x++) { rkl_clearCachedRegex(&rkl_cachedRegexes[x]); } 01671 for(x = 0UL; x < _RKL_LRU_CACHE_SET_WAYS; x++) { rkl_clearBuffer(&rkl_lruFixedBuffer[x], 0UL); rkl_clearBuffer(&rkl_lruDynamicBuffer[x], 1UL); } 01672 } 01673 01674 static void rkl_clearBuffer(RKLBuffer *buffer, NSUInteger freeDynamicBuffer) { 01675 RKLCAbortAssert(buffer != NULL); 01676 if(RKL_EXPECTED(buffer == NULL, 0L)) { return; } 01677 if(RKL_EXPECTED(freeDynamicBuffer == 1UL, 0L) && RKL_EXPECTED(buffer->uniChar != NULL, 1L)) { RKL_STRONG_REF void * RKL_GC_VOLATILE p = (RKL_STRONG_REF void * RKL_GC_VOLATILE)buffer->uniChar; buffer->uniChar = (RKL_STRONG_REF UniChar * RKL_GC_VOLATILE)rkl_free(&p); } 01678 if(RKL_EXPECTED(buffer->string != NULL, 1L)) { CFRelease((CFTypeRef)buffer->string); buffer->string = NULL; } 01679 buffer->length = 0L; 01680 buffer->hash = 0UL; 01681 } 01682 01683 static void rkl_clearCachedRegex(RKLCachedRegex *cachedRegex) { 01684 RKLCAbortAssert(cachedRegex != NULL); 01685 if(RKL_EXPECTED(cachedRegex == NULL, 0L)) { return; } 01686 rkl_clearCachedRegexSetTo(cachedRegex); 01687 if(rkl_lastCachedRegex == cachedRegex) { rkl_lastCachedRegex = NULL; } 01688 if(cachedRegex->icu_regex != NULL) { RKL_ICU_FUNCTION_APPEND(uregex_close)(cachedRegex->icu_regex); cachedRegex->icu_regex = NULL; cachedRegex->captureCount = -1L; } 01689 if(cachedRegex->regexString != NULL) { CFRelease((CFTypeRef)cachedRegex->regexString); cachedRegex->regexString = NULL; cachedRegex->options = 0U; cachedRegex->regexHash = 0UL; } 01690 } 01691 01692 static void rkl_clearCachedRegexSetTo(RKLCachedRegex *cachedRegex) { 01693 RKLCAbortAssert(cachedRegex != NULL); 01694 if(RKL_EXPECTED(cachedRegex == NULL, 0L)) { return; } 01695 if(RKL_EXPECTED(cachedRegex->icu_regex != NULL, 1L)) { int32_t status = 0; RKL_ICU_FUNCTION_APPEND(uregex_setText)(cachedRegex->icu_regex, &rkl_emptyUniCharString[0], 0, &status); } 01696 if(RKL_EXPECTED(cachedRegex->setToString != NULL, 1L)) { CFRelease((CFTypeRef)cachedRegex->setToString); cachedRegex->setToString = NULL; } 01697 cachedRegex->lastFindRange = cachedRegex->lastMatchRange = cachedRegex->setToRange = NSNotFoundRange; 01698 cachedRegex->setToIsImmutable = cachedRegex->setToNeedsConversion = 0U; 01699 cachedRegex->setToUniChar = NULL; 01700 cachedRegex->setToHash = 0UL; 01701 cachedRegex->setToLength = 0L; 01702 cachedRegex->buffer = NULL; 01703 } 01704 01705 #pragma mark Internal functions used to implement NSException and NSError functionality and userInfo NSDictionaries 01706 01707 // Helps to keep things tidy. 01708 #define addKeyAndObject(objs, keys, i, k, o) ({id _o=(o), _k=(k); if((_o != NULL) && (_k != NULL)) { objs[i] = _o; keys[i] = _k; i++; } }) 01709 01710 static NSDictionary *rkl_userInfoDictionary(RKLUserInfoOptions userInfoOptions, NSString *regexString, RKLRegexOptions options, const UParseError *parseError, int32_t status, NSString *matchString, NSRange matchRange, NSString *replacementString, NSString *replacedString, NSInteger replacedCount, RKLRegexEnumerationOptions enumerationOptions, ...) { 01711 va_list varArgsList; 01712 va_start(varArgsList, enumerationOptions); 01713 if(regexString == NULL) { regexString = @"<NULL regex>"; } 01714 01715 id objects[64], keys[64]; 01716 NSUInteger count = 0UL; 01717 01718 NSString * RKL_GC_VOLATILE errorNameString = [NSString stringWithUTF8String:RKL_ICU_FUNCTION_APPEND(u_errorName)(status)]; 01719 01720 addKeyAndObject(objects, keys, count, RKLICURegexRegexErrorKey, regexString); 01721 addKeyAndObject(objects, keys, count, RKLICURegexRegexOptionsErrorKey, [NSNumber numberWithUnsignedInt:options]); 01722 addKeyAndObject(objects, keys, count, RKLICURegexErrorCodeErrorKey, [NSNumber numberWithInt:status]); 01723 addKeyAndObject(objects, keys, count, RKLICURegexErrorNameErrorKey, errorNameString); 01724 01725 if(matchString != NULL) { addKeyAndObject(objects, keys, count, RKLICURegexSubjectStringErrorKey, matchString); } 01726 if((userInfoOptions & RKLUserInfoSubjectRange) != 0UL) { addKeyAndObject(objects, keys, count, RKLICURegexSubjectRangeErrorKey, [NSValue valueWithRange:matchRange]); } 01727 if(replacementString != NULL) { addKeyAndObject(objects, keys, count, RKLICURegexReplacementStringErrorKey, replacementString); } 01728 if(replacedString != NULL) { addKeyAndObject(objects, keys, count, RKLICURegexReplacedStringErrorKey, replacedString); } 01729 if((userInfoOptions & RKLUserInfoReplacedCount) != 0UL) { addKeyAndObject(objects, keys, count, RKLICURegexReplacedCountErrorKey, [NSNumber numberWithInteger:replacedCount]); } 01730 if((userInfoOptions & RKLUserInfoRegexEnumerationOptions) != 0UL) { addKeyAndObject(objects, keys, count, RKLICURegexEnumerationOptionsErrorKey, [NSNumber numberWithUnsignedInteger:enumerationOptions]); } 01731 01732 if((parseError != NULL) && (parseError->line != -1)) { 01733 NSString *preContextString = [NSString stringWithCharacters:&parseError->preContext[0] length:(NSUInteger)RKL_ICU_FUNCTION_APPEND(u_strlen)(&parseError->preContext[0])]; 01734 NSString *postContextString = [NSString stringWithCharacters:&parseError->postContext[0] length:(NSUInteger)RKL_ICU_FUNCTION_APPEND(u_strlen)(&parseError->postContext[0])]; 01735 01736 addKeyAndObject(objects, keys, count, RKLICURegexLineErrorKey, [NSNumber numberWithInt:parseError->line]); 01737 addKeyAndObject(objects, keys, count, RKLICURegexOffsetErrorKey, [NSNumber numberWithInt:parseError->offset]); 01738 addKeyAndObject(objects, keys, count, RKLICURegexPreContextErrorKey, preContextString); 01739 addKeyAndObject(objects, keys, count, RKLICURegexPostContextErrorKey, postContextString); 01740 addKeyAndObject(objects, keys, count, @"NSLocalizedFailureReason", ([NSString stringWithFormat:@"The error %@ occurred at line %d, column %d: %@<<HERE>>%@", errorNameString, parseError->line, parseError->offset, preContextString, postContextString])); 01741 } else { 01742 addKeyAndObject(objects, keys, count, @"NSLocalizedFailureReason", ([NSString stringWithFormat:@"The error %@ occurred.", errorNameString])); 01743 } 01744 01745 while(count < 62UL) { id obj = va_arg(varArgsList, id), key = va_arg(varArgsList, id); if((obj != NULL) && (key != NULL)) { addKeyAndObject(objects, keys, count, key, obj); } else { break; } } 01746 va_end(varArgsList); 01747 01748 return([NSDictionary dictionaryWithObjects:&objects[0] forKeys:&keys[0] count:count]); 01749 } 01750 01751 static NSError *rkl_makeNSError(RKLUserInfoOptions userInfoOptions, NSString *regexString, RKLRegexOptions options, const UParseError *parseError, int32_t status, NSString *matchString, NSRange matchRange, NSString *replacementString, NSString *replacedString, NSInteger replacedCount, RKLRegexEnumerationOptions enumerationOptions, NSString *errorDescription) { 01752 if(errorDescription == NULL) { errorDescription = (status == U_ZERO_ERROR) ? @"No description of this error is available." : [NSString stringWithFormat:@"ICU regular expression error #%d, %s.", status, RKL_ICU_FUNCTION_APPEND(u_errorName)(status)]; } 01753 return([NSError errorWithDomain:RKLICURegexErrorDomain code:(NSInteger)status userInfo:rkl_userInfoDictionary(userInfoOptions, regexString, options, parseError, status, matchString, matchRange, replacementString, replacedString, replacedCount, enumerationOptions, errorDescription, @"NSLocalizedDescription", NULL)]); 01754 } 01755 01756 static NSException *rkl_NSExceptionForRegex(NSString *regexString, RKLRegexOptions options, const UParseError *parseError, int32_t status) { 01757 return([NSException exceptionWithName:RKLICURegexException reason:[NSString stringWithFormat:@"ICU regular expression error #%d, %s.", status, RKL_ICU_FUNCTION_APPEND(u_errorName)(status)] userInfo:rkl_userInfoDictionary((RKLUserInfoOptions)RKLUserInfoNone, regexString, options, parseError, status, NULL, NSNotFoundRange, NULL, NULL, 0L, (RKLRegexEnumerationOptions)RKLRegexEnumerationNoOptions, NULL)]); 01758 } 01759 01760 static NSDictionary *rkl_makeAssertDictionary(const char *function, const char *file, int line, NSString *format, ...) { 01761 va_list varArgsList; 01762 va_start(varArgsList, format); 01763 NSString * RKL_GC_VOLATILE formatString = [[[NSString alloc] initWithFormat:format arguments:varArgsList] autorelease]; 01764 va_end(varArgsList); 01765 NSString * RKL_GC_VOLATILE functionString = [NSString stringWithUTF8String:function], *fileString = [NSString stringWithUTF8String:file]; 01766 return([NSDictionary dictionaryWithObjectsAndKeys:formatString, @"description", functionString, @"function", fileString, @"file", [NSNumber numberWithInt:line], @"line", NSInternalInconsistencyException, @"exceptionName", NULL]); 01767 } 01768 01769 static NSString *rkl_stringFromClassAndMethod(id object, SEL selector, NSString *format, ...) { 01770 va_list varArgsList; 01771 va_start(varArgsList, format); 01772 NSString * RKL_GC_VOLATILE formatString = [[[NSString alloc] initWithFormat:format arguments:varArgsList] autorelease]; 01773 va_end(varArgsList); 01774 Class objectsClass = (object == NULL) ? NULL : [object class]; 01775 return([NSString stringWithFormat:@"*** %c[%@ %@]: %@", (object == objectsClass) ? '+' : '-', (objectsClass == NULL) ? @"<NULL>" : NSStringFromClass(objectsClass), (selector == NULL) ? @":NULL:" : NSStringFromSelector(selector), formatString]); 01776 } 01777 01778 #ifdef _RKL_BLOCKS_ENABLED 01779 01781 #pragma mark - 01782 #pragma mark Objective-C ^Blocks Support 01783 #pragma mark - 01784 01785 01786 // Prototypes 01787 01788 static id rkl_performEnumerationUsingBlock(id self, SEL _cmd, 01789 RKLRegexOp regexOp, NSString *regexString, RKLRegexOptions options, 01790 id matchString, NSRange matchRange, 01791 RKLBlockEnumerationOp blockEnumerationOp, RKLRegexEnumerationOptions enumerationOptions, 01792 NSInteger *replacedCountPtr, NSUInteger *errorFreePtr, 01793 NSError **error, 01794 void (^stringsAndRangesBlock)(NSInteger capturedCount, NSString * const capturedStrings[capturedCount], const NSRange capturedStringRanges[capturedCount], volatile BOOL * const stop), 01795 NSString *(^replaceStringsAndRangesBlock)(NSInteger capturedCount, NSString * const capturedStrings[capturedCount], const NSRange capturedStringRanges[capturedCount], volatile BOOL * const stop) 01796 ) RKL_NONNULL_ARGS(1,2,4,6); 01797 01798 // This is an object meant for internal use only. It wraps and abstracts various functionality to simplify ^Blocks support. 01799 01800 @interface RKLBlockEnumerationHelper : NSObject { 01801 @public 01802 RKLCachedRegex cachedRegex; 01803 RKLBuffer buffer; 01804 RKL_STRONG_REF void * RKL_GC_VOLATILE scratchBuffer[_RKL_SCRATCH_BUFFERS]; 01805 NSUInteger needToFreeBufferUniChar:1; 01806 } 01807 - (id)initWithRegex:(NSString *)initRegexString options:(RKLRegexOptions)initOptions string:(NSString *)initString range:(NSRange)initRange error:(NSError **)initError; 01808 @end 01809 01810 @implementation RKLBlockEnumerationHelper 01811 01812 - (id)initWithRegex:(NSString *)initRegexString options:(RKLRegexOptions)initOptions string:(NSString *)initString range:(NSRange)initRange error:(NSError **)initError 01813 { 01814 volatile NSUInteger RKL_CLEANUP(rkl_cleanup_cacheSpinLockStatus) rkl_cacheSpinLockStatus = 0UL; 01815 01816 int32_t status = U_ZERO_ERROR; 01817 id exception = NULL; 01818 RKLCachedRegex *retrievedCachedRegex = NULL; 01819 01820 #ifdef _RKL_DTRACE_ENABLED 01821 NSUInteger thisDTraceEventID = 0UL; 01822 unsigned int lookupResultFlags = 0U; 01823 #endif 01824 01825 if(RKL_EXPECTED((self = [super init]) == NULL, 0L)) { goto errorExit; } 01826 01827 RKLCDelayedAssert((initRegexString != NULL) && (initString != NULL), &exception, errorExit); 01828 01829 // IMPORTANT! Once we have obtained the lock, code MUST exit via 'goto exitNow;' to unlock the lock! NO EXCEPTIONS! 01830 // ---------- 01831 OSSpinLockLock(&rkl_cacheSpinLock); // Grab the lock and get cache entry. 01832 rkl_cacheSpinLockStatus |= RKLLockedCacheSpinLock; 01833 rkl_dtrace_incrementAndGetEventID(thisDTraceEventID); 01834 01835 if(RKL_EXPECTED((retrievedCachedRegex = rkl_getCachedRegex(initRegexString, initOptions, initError, &exception)) == NULL, 0L)) { goto exitNow; } 01836 RKLCDelayedAssert(((retrievedCachedRegex >= rkl_cachedRegexes) && ((retrievedCachedRegex - &rkl_cachedRegexes[0]) < (ssize_t)_RKL_REGEX_CACHE_LINES)) && (retrievedCachedRegex != NULL) && (retrievedCachedRegex->icu_regex != NULL) && (retrievedCachedRegex->regexString != NULL) && (retrievedCachedRegex->captureCount >= 0L) && (retrievedCachedRegex == rkl_lastCachedRegex), &exception, exitNow); 01837 01838 if(RKL_EXPECTED(retrievedCachedRegex == NULL, 0L) || RKL_EXPECTED(status > U_ZERO_ERROR, 0L) || RKL_EXPECTED(exception != NULL, 0L)) { goto exitNow; } 01839 01840 if(RKL_EXPECTED((cachedRegex.icu_regex = RKL_ICU_FUNCTION_APPEND(uregex_clone)(retrievedCachedRegex->icu_regex, &status)) == NULL, 0L) || RKL_EXPECTED(status != U_ZERO_ERROR, 0L)) { goto exitNow; } 01841 if(RKL_EXPECTED((cachedRegex.regexString = (CFStringRef)CFRetain((CFTypeRef)retrievedCachedRegex->regexString)) == NULL, 0L)) { goto exitNow; } 01842 cachedRegex.options = initOptions; 01843 cachedRegex.captureCount = retrievedCachedRegex->captureCount; 01844 cachedRegex.regexHash = retrievedCachedRegex->regexHash; 01845 01846 RKLCDelayedAssert((cachedRegex.icu_regex != NULL) && (cachedRegex.regexString != NULL) && (cachedRegex.captureCount >= 0L), &exception, exitNow); 01847 01848 exitNow: 01849 if((rkl_cacheSpinLockStatus & RKLLockedCacheSpinLock) != 0UL) { // In case we arrive at exitNow: without obtaining the rkl_cacheSpinLock. 01850 OSSpinLockUnlock(&rkl_cacheSpinLock); 01851 rkl_cacheSpinLockStatus |= RKLUnlockedCacheSpinLock; // Warning about rkl_cacheSpinLockStatus never being read can be safely ignored. 01852 } 01853 01854 if(RKL_EXPECTED(self == NULL, 0L) || RKL_EXPECTED(retrievedCachedRegex == NULL, 0L) || RKL_EXPECTED(cachedRegex.icu_regex == NULL, 0L) || RKL_EXPECTED(status != U_ZERO_ERROR, 0L) || RKL_EXPECTED(exception != NULL, 0L)) { goto errorExit; } 01855 retrievedCachedRegex = NULL; // Since we no longer hold the lock, ensure that nothing accesses the retrieved cache regex after this point. 01856 01857 rkl_dtrace_addLookupFlag(lookupResultFlags, RKLEnumerationBufferLookupFlag); 01858 01859 if(RKL_EXPECTED((buffer.string = CFStringCreateCopy(NULL, (CFStringRef)initString)) == NULL, 0L)) { goto errorExit; } 01860 buffer.hash = CFHash((CFTypeRef)buffer.string); 01861 buffer.length = CFStringGetLength(buffer.string); 01862 01863 if((buffer.uniChar = (UniChar *)CFStringGetCharactersPtr(buffer.string)) == NULL) { 01864 rkl_dtrace_addLookupFlag(lookupResultFlags, RKLConversionRequiredLookupFlag); 01865 if(RKL_EXPECTED((buffer.uniChar = (RKL_STRONG_REF UniChar * RKL_GC_VOLATILE)rkl_realloc((RKL_STRONG_REF void ** RKL_GC_VOLATILE)&buffer.uniChar, ((size_t)buffer.length * sizeof(UniChar)), 0UL)) == NULL, 0L)) { goto errorExit; } // Resize the buffer. 01866 needToFreeBufferUniChar = rkl_collectingEnabled() ? 0U : 1U; 01867 CFStringGetCharacters(buffer.string, CFMakeRange(0L, buffer.length), (UniChar *)buffer.uniChar); // Convert to a UTF16 string. 01868 } 01869 01870 if(RKL_EXPECTED((cachedRegex.setToString = (CFStringRef)CFRetain((CFTypeRef)buffer.string)) == NULL, 0L)) { goto errorExit; } 01871 cachedRegex.setToHash = buffer.hash; 01872 cachedRegex.setToLength = buffer.length; 01873 cachedRegex.setToUniChar = buffer.uniChar; 01874 cachedRegex.buffer = &buffer; 01875 01876 RKLCDelayedAssert((cachedRegex.icu_regex != NULL) && (cachedRegex.setToUniChar != NULL) && (cachedRegex.setToLength < INT_MAX) && (NSMaxRange(initRange) <= (NSUInteger)cachedRegex.setToLength) && (NSMaxRange(initRange) < INT_MAX), &exception, errorExit); 01877 cachedRegex.lastFindRange = cachedRegex.lastMatchRange = NSNotFoundRange; 01878 cachedRegex.setToRange = initRange; 01879 RKL_ICU_FUNCTION_APPEND(uregex_setText)(cachedRegex.icu_regex, cachedRegex.setToUniChar + cachedRegex.setToRange.location, (int32_t)cachedRegex.setToRange.length, &status); 01880 if(RKL_EXPECTED(status > U_ZERO_ERROR, 0L)) { goto errorExit; } 01881 01882 rkl_dtrace_addLookupFlag(lookupResultFlags, RKLSetTextLookupFlag); 01883 rkl_dtrace_utf16ConversionCacheWithEventID(thisDTraceEventID, lookupResultFlags, initString, cachedRegex.setToRange.location, cachedRegex.setToRange.length, cachedRegex.setToLength); 01884 01885 return(self); 01886 01887 errorExit: 01888 if(RKL_EXPECTED(self != NULL, 1L)) { [self autorelease]; } 01889 if(RKL_EXPECTED(status > U_ZERO_ERROR, 0L) && RKL_EXPECTED(exception == NULL, 0L)) { exception = rkl_NSExceptionForRegex(initRegexString, initOptions, NULL, status); } // If we had a problem, prepare an exception to be thrown. 01890 if(RKL_EXPECTED(status < U_ZERO_ERROR, 0L) && (initError != NULL)) { *initError = rkl_makeNSError((RKLUserInfoOptions)RKLUserInfoNone, initRegexString, initOptions, NULL, status, initString, initRange, NULL, NULL, 0L, (RKLRegexEnumerationOptions)RKLRegexEnumerationNoOptions, @"The ICU library returned an unexpected error."); } 01891 if(RKL_EXPECTED(exception != NULL, 0L)) { rkl_handleDelayedAssert(self, _cmd, exception); } 01892 01893 return(NULL); 01894 } 01895 01896 #ifdef __OBJC_GC__ 01897 - (void)finalize 01898 { 01899 rkl_clearCachedRegex(&cachedRegex); 01900 rkl_clearBuffer(&buffer, (needToFreeBufferUniChar != 0U) ? 1LU : 0LU); 01901 NSUInteger tmpIdx = 0UL; // The rkl_free() below is "probably" a no-op when GC is on, but better to be safe than sorry... 01902 for(tmpIdx = 0UL; tmpIdx < _RKL_SCRATCH_BUFFERS; tmpIdx++) { if(RKL_EXPECTED(scratchBuffer[tmpIdx] != NULL, 0L)) { scratchBuffer[tmpIdx] = rkl_free(&scratchBuffer[tmpIdx]); } } 01903 [super finalize]; 01904 } 01905 #endif // __OBJC_GC__ 01906 01907 - (void)dealloc 01908 { 01909 rkl_clearCachedRegex(&cachedRegex); 01910 rkl_clearBuffer(&buffer, (needToFreeBufferUniChar != 0U) ? 1LU : 0LU); 01911 NSUInteger tmpIdx = 0UL; 01912 for(tmpIdx = 0UL; tmpIdx < _RKL_SCRATCH_BUFFERS; tmpIdx++) { if(RKL_EXPECTED(scratchBuffer[tmpIdx] != NULL, 0L)) { scratchBuffer[tmpIdx] = rkl_free(&scratchBuffer[tmpIdx]); } } 01913 [super dealloc]; 01914 } 01915 01916 @end 01917 01918 // IMPORTANT! This code is critical path code. Because of this, it has been written for speed, not clarity. 01919 // ---------- 01920 // 01921 // Return value: BOOL. Per "Error Handling Programming Guide" wrt/ NSError, return NO on error / failure, and set *error to an NSError object. 01922 // 01923 // rkl_performEnumerationUsingBlock reference counted / manual memory management notes: 01924 // 01925 // When running using reference counting, rkl_performEnumerationUsingBlock() creates a CFMutableArray called autoreleaseArray, which is -autoreleased. 01926 // autoreleaseArray uses the rkl_transferOwnershipArrayCallBacks CFArray callbacks which do not perform a -retain/CFRetain() when objects are added, but do perform a -release/CFRelease() when an object is removed. 01927 // 01928 // A special class, RKLBlockEnumerationHelper, is used to manage the details of creating a private instantiation of the ICU regex (via uregex_clone()) and setting up the details of the UTF-16 buffer required by the ICU regex engine. 01929 // The instantiated RKLBlockEnumerationHelper is not autoreleased, but added to autoreleaseArray. When rkl_performEnumerationUsingBlock() exits, it calls CFArrayRemoveAllValues(autoreleaseArray), which empties the array. 01930 // This has the effect of immediately -releasing the instantiated RKLBlockEnumerationHelper object, and all the memory used to hold the ICU regex and UTF-16 conversion buffer. 01931 // This means the memory is reclaimed immediately and we do not have to wait until the autorelease pool pops. 01932 // 01933 // If we are performing a "string replacement" operation, we create a temporary NSMutableString named mutableReplacementString to hold the replaced strings results. mutableReplacementString is also added to autoreleaseArray so that it 01934 // can be properly released on an error. 01935 // 01936 // Temporary strings that are created during the enumeration of matches are added to autoreleaseArray. 01937 // The strings are added by doing a CFArrayReplaceValues(), which simultaneously releases the previous iterations temporary strings while adding the current iterations temporary strings to the array. 01938 // 01939 // autoreleaseArray always has a reference to any "live" and in use objects. If anything "Goes Wrong", at any point, for any reason (ie, exception is thrown), autoreleaseArray is in the current NSAutoreleasePool 01940 // and will automatically be released when that pool pops. This ensures that we don't leak anything even when things go seriously sideways. This also allows us to keep the total amount of memory in use 01941 // down to a minimum, which can be substantial if the user is enumerating a large string, for example a regex of '\w+' on a 500K+ text file. 01942 // 01943 // The only 'caveat' is that the user needs to -retain any strings that they want to use past the point at which their ^block returns. Logically, it is as if the following takes place: 01944 // 01945 // for(eachMatchOfRegexInStringToSearch) { 01946 // NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 01947 // callUsersBlock(capturedCount, capturedStrings, capturedStringRanges, stop); 01948 // [pool release]; 01949 // } 01950 // 01951 // But in reality, no NSAutoreleasePool is created, it's all slight of hand done via the CFMutableArray autoreleaseArray. 01952 // 01953 // rkl_performEnumerationUsingBlock garbage collected / automatic memory management notes: 01954 // 01955 // When RegexKitLite is built with -fobjc-gc or -fobjc-gc-only, and (in the case of -fobjc-gc) RegexKitLite determines that GC is active at execution time, then rkl_performEnumerationUsingBlock essentially 01956 // skips all of the above reference counted autoreleaseArray stuff. 01957 // 01958 // rkl_performEnumerationUsingBlock and RKLRegexEnumerationReleaseStringReturnedByReplacementBlock notes 01959 // 01960 // Under reference counting, this enumeration option allows the user to return a non-autoreleased string, and then have RegexKitLite send the object a -release message once it's done with it. 01961 // The primary reason to do this is to immediately reclaim the memory used by the string holding the replacement text. 01962 // Just in case the user returns one of the strings we passed via capturedStrings[], we check to see if the string return by the block is any of the strings we created and passed via capturedStrings[]. 01963 // If it is one of our strings, we do not send the string a -release since that would over release it. It is assumed that the user will /NOT/ add a -retain to our strings in this case. 01964 // Under GC, RKLRegexEnumerationReleaseStringReturnedByReplacementBlock is ignored and no -release messages are sent. 01965 // 01966 01967 #pragma mark Primary internal function that Objective-C ^Blocks related methods call to perform regular expression operations 01968 01969 static id rkl_performEnumerationUsingBlock(id self, SEL _cmd, 01970 RKLRegexOp regexOp, NSString *regexString, RKLRegexOptions options, 01971 id matchString, NSRange matchRange, 01972 RKLBlockEnumerationOp blockEnumerationOp, RKLRegexEnumerationOptions enumerationOptions, 01973 NSInteger *replacedCountPtr, NSUInteger *errorFreePtr, 01974 NSError **error, 01975 void (^stringsAndRangesBlock)(NSInteger capturedCount, NSString * const capturedStrings[capturedCount], const NSRange capturedStringRanges[capturedCount], volatile BOOL * const stop), 01976 NSString *(^replaceStringsAndRangesBlock)(NSInteger capturedCount, NSString * const capturedStrings[capturedCount], const NSRange capturedStringRanges[capturedCount], volatile BOOL * const stop)) { 01977 NSMutableArray * RKL_GC_VOLATILE autoreleaseArray = NULL; 01978 RKLBlockEnumerationHelper * RKL_GC_VOLATILE blockEnumerationHelper = NULL; 01979 NSMutableString * RKL_GC_VOLATILE mutableReplacementString = NULL; 01980 RKL_STRONG_REF UniChar * RKL_GC_VOLATILE blockEnumerationHelperUniChar = NULL; 01981 NSUInteger errorFree = NO; 01982 id exception = NULL, returnObject = NULL; 01983 CFRange autoreleaseReplaceRange = CFMakeRange(0L, 0L); 01984 int32_t status = U_ZERO_ERROR; 01985 RKLRegexOp maskedRegexOp = (regexOp & RKLMaskOp); 01986 volatile BOOL shouldStop = NO; 01987 NSInteger replacedCount = -1L; 01988 NSRange lastMatchedRange = NSNotFoundRange; 01989 NSUInteger stringU16Length = 0UL; 01990 01991 BOOL performStringReplacement = (blockEnumerationOp == RKLBlockEnumerationReplaceOp) ? YES : NO; 01992 01993 if((error != NULL) && (*error != NULL)) { *error = NULL; } 01994 01995 if(RKL_EXPECTED(regexString == NULL, 0L)) { exception = (id)RKL_EXCEPTION(NSInvalidArgumentException, @"The regular expression argument is NULL."); goto exitNow; } 01996 if(RKL_EXPECTED(matchString == NULL, 0L)) { exception = (id)RKL_EXCEPTION(NSInternalInconsistencyException, @"The match string argument is NULL."); goto exitNow; } 01997 01998 if((((enumerationOptions & RKLRegexEnumerationCapturedStringsNotRequired) != 0UL) && ((enumerationOptions & RKLRegexEnumerationFastCapturedStringsXXX) != 0UL)) || 01999 (((enumerationOptions & RKLRegexEnumerationReleaseStringReturnedByReplacementBlock) != 0UL) && (blockEnumerationOp != RKLBlockEnumerationReplaceOp)) || 02000 ((enumerationOptions & (~((RKLRegexEnumerationOptions)(RKLRegexEnumerationCapturedStringsNotRequired | RKLRegexEnumerationReleaseStringReturnedByReplacementBlock | RKLRegexEnumerationFastCapturedStringsXXX)))) != 0UL)) { 02001 exception = (id)RKL_EXCEPTION(NSInvalidArgumentException, @"The RKLRegexEnumerationOptions argument is not valid."); 02002 goto exitNow; 02003 } 02004 02005 stringU16Length = (NSUInteger)CFStringGetLength((CFStringRef)matchString); 02006 02007 if(RKL_EXPECTED(matchRange.length == NSUIntegerMax, 1L)) { matchRange.length = stringU16Length; } // For convenience. 02008 if(RKL_EXPECTED(stringU16Length < NSMaxRange(matchRange), 0L)) { exception = (id)RKL_EXCEPTION(NSRangeException, @"Range or index out of bounds."); goto exitNow; } 02009 if(RKL_EXPECTED(stringU16Length >= (NSUInteger)INT_MAX, 0L)) { exception = (id)RKL_EXCEPTION(NSRangeException, @"String length exceeds INT_MAX."); goto exitNow; } 02010 02011 RKLCDelayedAssert((self != NULL) && (_cmd != NULL) && ((blockEnumerationOp == RKLBlockEnumerationMatchOp) ? (((regexOp == RKLCapturesArrayOp) || (regexOp == RKLSplitOp)) && (stringsAndRangesBlock != NULL) && (replaceStringsAndRangesBlock == NULL)) : 1) && ((blockEnumerationOp == RKLBlockEnumerationReplaceOp) ? ((regexOp == RKLCapturesArrayOp) && (stringsAndRangesBlock == NULL) && (replaceStringsAndRangesBlock != NULL)) : 1) , &exception, exitNow); 02012 02013 if((rkl_collectingEnabled() == NO) && RKL_EXPECTED((autoreleaseArray = rkl_CFAutorelease(CFArrayCreateMutable(NULL, 0L, &rkl_transferOwnershipArrayCallBacks))) == NULL, 0L)) { goto exitNow; } // Warning about potential leak of Core Foundation object can be safely ignored. 02014 if(RKL_EXPECTED((blockEnumerationHelper = [[RKLBlockEnumerationHelper alloc] initWithRegex:regexString options:options string:matchString range:matchRange error:error]) == NULL, 0L)) { goto exitNow; } // Warning about potential leak of blockEnumerationHelper can be safely ignored. 02015 if(autoreleaseArray != NULL) { CFArrayAppendValue((CFMutableArrayRef)autoreleaseArray, blockEnumerationHelper); autoreleaseReplaceRange.location++; } // We do not autorelease blockEnumerationHelper, but instead add it to autoreleaseArray. 02016 02017 if(performStringReplacement == YES) { 02018 if(RKL_EXPECTED((mutableReplacementString = [[NSMutableString alloc] init]) == NULL, 0L)) { goto exitNow; } // Warning about potential leak of mutableReplacementString can be safely ignored. 02019 if(autoreleaseArray != NULL) { CFArrayAppendValue((CFMutableArrayRef)autoreleaseArray, mutableReplacementString); autoreleaseReplaceRange.location++; } // We do not autorelease mutableReplacementString, but instead add it to autoreleaseArray. 02020 } 02021 02022 // RKLBlockEnumerationHelper creates an immutable copy of the string to match (matchString) which we reference via blockEnumerationHelperString. We use blockEnumerationHelperString when creating the captured strings from a match. 02023 // This protects us against the user mutating matchString while we are in the middle of enumerating matches. 02024 NSString * RKL_GC_VOLATILE blockEnumerationHelperString = (NSString *)blockEnumerationHelper->buffer.string, ** RKL_GC_VOLATILE capturedStrings = NULL, *emptyString = @""; 02025 CFMutableStringRef * RKL_GC_VOLATILE fastCapturedStrings = NULL; 02026 NSInteger captureCountBlockArgument = (blockEnumerationHelper->cachedRegex.captureCount + 1L); 02027 size_t capturedStringsCapacity = ((size_t)captureCountBlockArgument + 4UL); 02028 size_t capturedRangesCapacity = (((size_t)captureCountBlockArgument + 4UL) * 5UL); 02029 NSRange *capturedRanges = NULL; 02030 02031 lastMatchedRange = NSMakeRange(matchRange.location, 0UL); 02032 blockEnumerationHelperUniChar = blockEnumerationHelper->buffer.uniChar; 02033 02034 RKLCDelayedAssert((blockEnumerationHelperString != NULL) && (blockEnumerationHelperUniChar != NULL) && (captureCountBlockArgument > 0L) && (capturedStringsCapacity > 0UL) && (capturedRangesCapacity > 0UL), &exception, exitNow); 02035 02036 if((capturedStrings = (NSString ** RKL_GC_VOLATILE)alloca(sizeof(NSString *) * capturedStringsCapacity)) == NULL) { goto exitNow; } // Space to hold the captured strings from a match. 02037 if((capturedRanges = (NSRange *) alloca(sizeof(NSRange) * capturedRangesCapacity)) == NULL) { goto exitNow; } // Space to hold the NSRanges of the captured strings from a match. 02038 02039 #ifdef NS_BLOCK_ASSERTIONS 02040 { // Initialize the padded capturedStrings and capturedRanges to values that should tickle a fault if they are ever used. 02041 size_t idx = 0UL; 02042 for(idx = captureCountBlockArgument; idx < capturedStringsCapacity; idx++) { capturedStrings[idx] = (NSString *)RKLIllegalPointer; } 02043 for(idx = captureCountBlockArgument; idx < capturedRangesCapacity; idx++) { capturedRanges[idx] = RKLIllegalRange; } 02044 } 02045 #else 02046 { // Initialize all of the capturedStrings and capturedRanges to values that should tickle a fault if they are ever used. 02047 size_t idx = 0UL; 02048 for(idx = 0UL; idx < capturedStringsCapacity; idx++) { capturedStrings[idx] = (NSString *)RKLIllegalPointer; } 02049 for(idx = 0UL; idx < capturedRangesCapacity; idx++) { capturedRanges[idx] = RKLIllegalRange; } 02050 } 02051 #endif 02052 02053 if((enumerationOptions & RKLRegexEnumerationFastCapturedStringsXXX) != 0UL) { 02054 RKLCDelayedAssert(((enumerationOptions & RKLRegexEnumerationCapturedStringsNotRequired) == 0UL), &exception, exitNow); 02055 size_t idx = 0UL; 02056 if((fastCapturedStrings = (CFMutableStringRef * RKL_GC_VOLATILE)alloca(sizeof(NSString *) * capturedStringsCapacity)) == NULL) { goto exitNow; } // Space to hold the "fast" captured strings from a match. 02057 02058 for(idx = 0UL; idx < (size_t)captureCountBlockArgument; idx++) { 02059 if((fastCapturedStrings[idx] = CFStringCreateMutableWithExternalCharactersNoCopy(NULL, NULL, 0L, 0L, kCFAllocatorNull)) == NULL) { goto exitNow; } 02060 if(autoreleaseArray != NULL) { CFArrayAppendValue((CFMutableArrayRef)autoreleaseArray, fastCapturedStrings[idx]); autoreleaseReplaceRange.location++; } // We do not autorelease mutableReplacementString, but instead add it to autoreleaseArray. 02061 capturedStrings[idx] = (NSString *)fastCapturedStrings[idx]; 02062 } 02063 } 02064 02065 RKLFindAll findAll = rkl_makeFindAll(capturedRanges, matchRange, (NSInteger)capturedRangesCapacity, (capturedRangesCapacity * sizeof(NSRange)), 0UL, &blockEnumerationHelper->scratchBuffer[0], &blockEnumerationHelper->scratchBuffer[1], &blockEnumerationHelper->scratchBuffer[2], &blockEnumerationHelper->scratchBuffer[3], &blockEnumerationHelper->scratchBuffer[4], 0L, 0L, 1L); 02066 02067 NSString ** RKL_GC_VOLATILE capturedStringsBlockArgument = NULL; // capturedStringsBlockArgument is what we pass to the 'capturedStrings[]' argument of the users ^block. Will pass NULL if the user doesn't want the captured strings created automatically. 02068 if((enumerationOptions & RKLRegexEnumerationCapturedStringsNotRequired) == 0UL) { capturedStringsBlockArgument = capturedStrings; } // If the user wants the captured strings automatically created, set to capturedStrings. 02069 02070 replacedCount = 0L; 02071 while(RKL_EXPECTED(rkl_findRanges(&blockEnumerationHelper->cachedRegex, regexOp, &findAll, &exception, &status) == NO, 1L) && RKL_EXPECTED(findAll.found > 0L, 1L) && RKL_EXPECTED(exception == NULL, 1L) && RKL_EXPECTED(status == U_ZERO_ERROR, 1L)) { 02072 if(performStringReplacement == YES) { 02073 NSUInteger lastMatchedMaxLocation = (lastMatchedRange.location + lastMatchedRange.length); 02074 NSRange previousUnmatchedRange = NSMakeRange(lastMatchedMaxLocation, findAll.ranges[0].location - lastMatchedMaxLocation); 02075 RKLCDelayedAssert((NSMaxRange(previousUnmatchedRange) <= stringU16Length) && (NSRangeInsideRange(previousUnmatchedRange, matchRange) == YES), &exception, exitNow); 02076 if(RKL_EXPECTED(previousUnmatchedRange.length > 0UL, 1L)) { CFStringAppendCharacters((CFMutableStringRef)mutableReplacementString, blockEnumerationHelperUniChar + previousUnmatchedRange.location, (CFIndex)previousUnmatchedRange.length); } 02077 } 02078 02079 findAll.found -= findAll.addedSplitRanges; 02080 02081 NSInteger passCaptureCountBlockArgument = ((findAll.found == 0L) && (findAll.addedSplitRanges == 1L) && (maskedRegexOp == RKLSplitOp)) ? 1L : findAll.found, capturedStringsIdx = passCaptureCountBlockArgument; 02082 RKLCDelayedHardAssert(passCaptureCountBlockArgument <= captureCountBlockArgument, &exception, exitNow); 02083 if(capturedStringsBlockArgument != NULL) { // Only create the captured strings if the user has requested them. 02084 BOOL hadError = NO; // Loop over all the strings rkl_findRanges found. If rkl_CreateStringWithSubstring() returns NULL due to an error, set returnBool to NO, and break out of the for() loop. 02085 02086 for(capturedStringsIdx = 0L; capturedStringsIdx < passCaptureCountBlockArgument; capturedStringsIdx++) { 02087 RKLCDelayedHardAssert(capturedStringsIdx < captureCountBlockArgument, &exception, exitNow); 02088 if((enumerationOptions & RKLRegexEnumerationFastCapturedStringsXXX) != 0UL) { 02089 // Analyzer report of "Dereference of null pointer" can be safely ignored for the next line. Bug filed: http://llvm.org/bugs/show_bug.cgi?id=6150 02090 CFStringSetExternalCharactersNoCopy(fastCapturedStrings[capturedStringsIdx], &blockEnumerationHelperUniChar[findAll.ranges[capturedStringsIdx].location], (CFIndex)findAll.ranges[capturedStringsIdx].length, (CFIndex)findAll.ranges[capturedStringsIdx].length); 02091 } else { 02092 if((capturedStrings[capturedStringsIdx] = (findAll.ranges[capturedStringsIdx].length == 0UL) ? emptyString : rkl_CreateStringWithSubstring(blockEnumerationHelperString, findAll.ranges[capturedStringsIdx])) == NULL) { hadError = YES; break; } 02093 } 02094 } 02095 if(((enumerationOptions & RKLRegexEnumerationFastCapturedStringsXXX) == 0UL) && RKL_EXPECTED(autoreleaseArray != NULL, 1L)) { CFArrayReplaceValues((CFMutableArrayRef)autoreleaseArray, autoreleaseReplaceRange, (const void **)capturedStrings, capturedStringsIdx); autoreleaseReplaceRange.length = capturedStringsIdx; } // Add to autoreleaseArray all the strings the for() loop created. 02096 if(RKL_EXPECTED(hadError == YES, 0L)) { goto exitNow; } // hadError == YES will be set if rkl_CreateStringWithSubstring() returned NULL. 02097 } 02098 // For safety, set any capturedRanges and capturedStrings up to captureCountBlockArgument + 1 to values that indicate that they are not valid. 02099 // These values are chosen such that they should tickle any misuse by users. 02100 // capturedStringsIdx is initialized to passCaptureCountBlockArgument, but if capturedStringsBlockArgument != NULL, it is reset to 0 by the loop that creates strings. 02101 // If the loop that creates strings has an error, execution should transfer to exitNow and this will never get run. 02102 // Again, this is for safety for users that do not check the passed block argument 'captureCount' and instead depend on something like [regex captureCount]. 02103 for(; capturedStringsIdx < captureCountBlockArgument + 1L; capturedStringsIdx++) { RKLCDelayedAssert((capturedStringsIdx < (NSInteger)capturedStringsCapacity) && (capturedStringsIdx < (NSInteger)capturedRangesCapacity), &exception, exitNow); capturedRanges[capturedStringsIdx] = RKLIllegalRange; capturedStrings[capturedStringsIdx] = (NSString *)RKLIllegalPointer; } 02104 02105 RKLCDelayedAssert((passCaptureCountBlockArgument > 0L) && (NSMaxRange(capturedRanges[0]) <= stringU16Length) && (capturedRanges[0].location < NSIntegerMax) && (capturedRanges[0].length < NSIntegerMax), &exception, exitNow); 02106 02107 switch(blockEnumerationOp) { 02108 case RKLBlockEnumerationMatchOp: stringsAndRangesBlock(passCaptureCountBlockArgument, capturedStringsBlockArgument, capturedRanges, &shouldStop); break; 02109 02110 case RKLBlockEnumerationReplaceOp: { 02111 NSString *blockReturnedReplacementString = replaceStringsAndRangesBlock(passCaptureCountBlockArgument, capturedStringsBlockArgument, capturedRanges, &shouldStop); 02112 02113 if(RKL_EXPECTED(blockReturnedReplacementString != NULL, 1L)) { 02114 CFStringAppend((CFMutableStringRef)mutableReplacementString, (CFStringRef)blockReturnedReplacementString); 02115 BOOL shouldRelease = (((enumerationOptions & RKLRegexEnumerationReleaseStringReturnedByReplacementBlock) != 0UL) && (capturedStringsBlockArgument != NULL) && (rkl_collectingEnabled() == NO)) ? YES : NO; 02116 if(shouldRelease == YES) { NSInteger idx = 0L; for(idx = 0L; idx < passCaptureCountBlockArgument; idx++) { if(capturedStrings[idx] == blockReturnedReplacementString) { shouldRelease = NO; break; } } } 02117 if(shouldRelease == YES) { [blockReturnedReplacementString release]; } 02118 } 02119 } 02120 break; 02121 02122 default: exception = RKLCAssertDictionary(@"Unknown blockEnumerationOp code."); goto exitNow; break; 02123 } 02124 02125 replacedCount++; 02126 findAll.addedSplitRanges = 0L; // rkl_findRanges() expects findAll.addedSplitRanges to be 0 on entry. 02127 findAll.found = 0L; // rkl_findRanges() expects findAll.found to be 0 on entry. 02128 findAll.findInRange = findAll.remainingRange; // Ask rkl_findRanges() to search the part of the string after the current match. 02129 lastMatchedRange = findAll.ranges[0]; 02130 02131 if(RKL_EXPECTED(shouldStop != NO, 0L)) { break; } 02132 } 02133 errorFree = YES; 02134 02135 exitNow: 02136 if(RKL_EXPECTED(errorFree == NO, 0L)) { replacedCount = -1L; } 02137 if((blockEnumerationOp == RKLBlockEnumerationReplaceOp) && RKL_EXPECTED(errorFree == YES, 1L)) { 02138 RKLCDelayedAssert(replacedCount >= 0L, &exception, exitNow2); 02139 if(RKL_EXPECTED(replacedCount == 0UL, 0L)) { 02140 RKLCDelayedAssert((blockEnumerationHelper != NULL) && (blockEnumerationHelper->buffer.string != NULL), &exception, exitNow2); 02141 returnObject = rkl_CreateStringWithSubstring((id)blockEnumerationHelper->buffer.string, matchRange); 02142 if(rkl_collectingEnabled() == NO) { returnObject = rkl_CFAutorelease(returnObject); } 02143 } 02144 else { 02145 NSUInteger lastMatchedMaxLocation = (lastMatchedRange.location + lastMatchedRange.length); 02146 NSRange previousUnmatchedRange = NSMakeRange(lastMatchedMaxLocation, NSMaxRange(matchRange) - lastMatchedMaxLocation); 02147 RKLCDelayedAssert((NSMaxRange(previousUnmatchedRange) <= stringU16Length) && (NSRangeInsideRange(previousUnmatchedRange, matchRange) == YES), &exception, exitNow2); 02148 02149 if(RKL_EXPECTED(previousUnmatchedRange.length > 0UL, 1L)) { CFStringAppendCharacters((CFMutableStringRef)mutableReplacementString, blockEnumerationHelperUniChar + previousUnmatchedRange.location, (CFIndex)previousUnmatchedRange.length); } 02150 returnObject = rkl_CFAutorelease(CFStringCreateCopy(NULL, (CFStringRef)mutableReplacementString)); // Warning about potential leak of Core Foundation object can be safely ignored. 02151 } 02152 } 02153 02154 #ifndef NS_BLOCK_ASSERTIONS 02155 exitNow2: 02156 #endif // NS_BLOCK_ASSERTIONS 02157 if(RKL_EXPECTED(autoreleaseArray != NULL, 1L)) { CFArrayRemoveAllValues((CFMutableArrayRef)autoreleaseArray); } // Causes blockEnumerationHelper to be released immediately, freeing all of its resources (such as a large UTF-16 conversion buffer). 02158 if(RKL_EXPECTED(exception != NULL, 0L)) { rkl_handleDelayedAssert(self, _cmd, exception); } // If there is an exception, throw it at this point. 02159 if(((errorFree == NO) || ((errorFree == YES) && (returnObject == NULL))) && (error != NULL) && (*error == NULL)) { 02160 RKLUserInfoOptions userInfoOptions = (RKLUserInfoSubjectRange | RKLUserInfoRegexEnumerationOptions); 02161 NSString *replacedString = NULL; 02162 if(blockEnumerationOp == RKLBlockEnumerationReplaceOp) { userInfoOptions |= RKLUserInfoReplacedCount; if(RKL_EXPECTED(errorFree == YES, 1L)) { replacedString = returnObject; } } 02163 *error = rkl_makeNSError(userInfoOptions, regexString, options, NULL, status, (blockEnumerationHelper != NULL) ? (blockEnumerationHelper->buffer.string != NULL) ? (NSString *)blockEnumerationHelper->buffer.string : matchString : matchString, matchRange, NULL, replacedString, replacedCount, enumerationOptions, @"An unexpected error occurred."); 02164 } 02165 if(replacedCountPtr != NULL) { *replacedCountPtr = replacedCount; } 02166 if(errorFreePtr != NULL) { *errorFreePtr = errorFree; } 02167 return(returnObject); 02168 } // The two warnings about potential leaks can be safely ignored. 02169 02170 #endif // _RKL_BLOCKS_ENABLED 02171 02173 #pragma mark - 02174 #pragma mark Objective-C Public Interface 02175 #pragma mark - 02176 02177 02178 @implementation NSString (RegexKitLiteAdditions) 02179 02180 #pragma mark +clearStringCache 02181 02182 + (void)RKL_METHOD_PREPEND(clearStringCache) 02183 { 02184 volatile NSUInteger RKL_CLEANUP(rkl_cleanup_cacheSpinLockStatus) rkl_cacheSpinLockStatus = 0UL; 02185 OSSpinLockLock(&rkl_cacheSpinLock); 02186 rkl_cacheSpinLockStatus |= RKLLockedCacheSpinLock; 02187 rkl_clearStringCache(); 02188 OSSpinLockUnlock(&rkl_cacheSpinLock); 02189 rkl_cacheSpinLockStatus |= RKLUnlockedCacheSpinLock; // Warning about rkl_cacheSpinLockStatus never being read can be safely ignored. 02190 } 02191 02192 #pragma mark +captureCountForRegex: 02193 02194 + (NSInteger)RKL_METHOD_PREPEND(captureCountForRegex):(NSString *)regex 02195 { 02196 NSInteger captureCount = -1L; 02197 rkl_isRegexValid(self, _cmd, regex, RKLNoOptions, &captureCount, NULL); 02198 return(captureCount); 02199 } 02200 02201 + (NSInteger)RKL_METHOD_PREPEND(captureCountForRegex):(NSString *)regex options:(RKLRegexOptions)options error:(NSError **)error 02202 { 02203 NSInteger captureCount = -1L; 02204 rkl_isRegexValid(self, _cmd, regex, options, &captureCount, error); 02205 return(captureCount); 02206 } 02207 02208 #pragma mark -captureCount: 02209 02210 - (NSInteger)RKL_METHOD_PREPEND(captureCount) 02211 { 02212 NSInteger captureCount = -1L; 02213 rkl_isRegexValid(self, _cmd, self, RKLNoOptions, &captureCount, NULL); 02214 return(captureCount); 02215 } 02216 02217 - (NSInteger)RKL_METHOD_PREPEND(captureCountWithOptions):(RKLRegexOptions)options error:(NSError **)error 02218 { 02219 NSInteger captureCount = -1L; 02220 rkl_isRegexValid(self, _cmd, self, options, &captureCount, error); 02221 return(captureCount); 02222 } 02223 02224 #pragma mark -componentsSeparatedByRegex: 02225 02226 - (NSArray *)RKL_METHOD_PREPEND(componentsSeparatedByRegex):(NSString *)regex 02227 { 02228 NSRange range = NSMaxiumRange; 02229 return(rkl_performRegexOp(self, _cmd, (RKLRegexOp)RKLSplitOp, regex, RKLNoOptions, 0L, self, &range, NULL, NULL, NULL, 0UL, NULL, NULL)); 02230 } 02231 02232 - (NSArray *)RKL_METHOD_PREPEND(componentsSeparatedByRegex):(NSString *)regex range:(NSRange)range 02233 { 02234 return(rkl_performRegexOp(self, _cmd, (RKLRegexOp)RKLSplitOp, regex, RKLNoOptions, 0L, self, &range, NULL, NULL, NULL, 0UL, NULL, NULL)); 02235 } 02236 02237 - (NSArray *)RKL_METHOD_PREPEND(componentsSeparatedByRegex):(NSString *)regex options:(RKLRegexOptions)options range:(NSRange)range error:(NSError **)error 02238 { 02239 return(rkl_performRegexOp(self, _cmd, (RKLRegexOp)RKLSplitOp, regex, options, 0L, self, &range, NULL, error, NULL, 0UL, NULL, NULL)); 02240 } 02241 02242 #pragma mark -isMatchedByRegex: 02243 02244 - (BOOL)RKL_METHOD_PREPEND(isMatchedByRegex):(NSString *)regex 02245 { 02246 NSRange result = NSNotFoundRange, range = NSMaxiumRange; 02247 rkl_performRegexOp(self, _cmd, (RKLRegexOp)RKLRangeOp, regex, RKLNoOptions, 0L, self, &range, NULL, NULL, &result, 0UL, NULL, NULL); 02248 return((result.location == (NSUInteger)NSNotFound) ? NO : YES); 02249 } 02250 02251 - (BOOL)RKL_METHOD_PREPEND(isMatchedByRegex):(NSString *)regex inRange:(NSRange)range 02252 { 02253 NSRange result = NSNotFoundRange; 02254 rkl_performRegexOp(self, _cmd, (RKLRegexOp)RKLRangeOp, regex, RKLNoOptions, 0L, self, &range, NULL, NULL, &result, 0UL, NULL, NULL); 02255 return((result.location == (NSUInteger)NSNotFound) ? NO : YES); 02256 } 02257 02258 - (BOOL)RKL_METHOD_PREPEND(isMatchedByRegex):(NSString *)regex options:(RKLRegexOptions)options inRange:(NSRange)range error:(NSError **)error 02259 { 02260 NSRange result = NSNotFoundRange; 02261 rkl_performRegexOp(self, _cmd, (RKLRegexOp)RKLRangeOp, regex, options, 0L, self, &range, NULL, error, &result, 0UL, NULL, NULL); 02262 return((result.location == (NSUInteger)NSNotFound) ? NO : YES); 02263 } 02264 02265 #pragma mark -isRegexValid 02266 02267 - (BOOL)RKL_METHOD_PREPEND(isRegexValid) 02268 { 02269 return(rkl_isRegexValid(self, _cmd, self, RKLNoOptions, NULL, NULL) == 1UL ? YES : NO); 02270 } 02271 02272 - (BOOL)RKL_METHOD_PREPEND(isRegexValidWithOptions):(RKLRegexOptions)options error:(NSError **)error 02273 { 02274 return(rkl_isRegexValid(self, _cmd, self, options, NULL, error) == 1UL ? YES : NO); 02275 } 02276 02277 #pragma mark -flushCachedRegexData 02278 02279 - (void)RKL_METHOD_PREPEND(flushCachedRegexData) 02280 { 02281 volatile NSUInteger RKL_CLEANUP(rkl_cleanup_cacheSpinLockStatus) rkl_cacheSpinLockStatus = 0UL; 02282 02283 CFIndex selfLength = CFStringGetLength((CFStringRef)self); 02284 CFHashCode selfHash = CFHash((CFTypeRef)self); 02285 02286 OSSpinLockLock(&rkl_cacheSpinLock); 02287 rkl_cacheSpinLockStatus |= RKLLockedCacheSpinLock; 02288 rkl_dtrace_incrementEventID(); 02289 02290 NSUInteger idx; 02291 for(idx = 0UL; idx < _RKL_REGEX_CACHE_LINES; idx++) { 02292 RKLCachedRegex *cachedRegex = &rkl_cachedRegexes[idx]; 02293 if((cachedRegex->setToString != NULL) && ( (cachedRegex->setToString == (CFStringRef)self) || ((cachedRegex->setToLength == selfLength) && (cachedRegex->setToHash == selfHash)) ) ) { rkl_clearCachedRegexSetTo(cachedRegex); } 02294 } 02295 for(idx = 0UL; idx < _RKL_LRU_CACHE_SET_WAYS; idx++) { RKLBuffer *buffer = &rkl_lruFixedBuffer[idx]; if((buffer->string != NULL) && ((buffer->string == (CFStringRef)self) || ((buffer->length == selfLength) && (buffer->hash == selfHash)))) { rkl_clearBuffer(buffer, 0UL); } } 02296 for(idx = 0UL; idx < _RKL_LRU_CACHE_SET_WAYS; idx++) { RKLBuffer *buffer = &rkl_lruDynamicBuffer[idx]; if((buffer->string != NULL) && ((buffer->string == (CFStringRef)self) || ((buffer->length == selfLength) && (buffer->hash == selfHash)))) { rkl_clearBuffer(buffer, 0UL); } } 02297 02298 OSSpinLockUnlock(&rkl_cacheSpinLock); 02299 rkl_cacheSpinLockStatus |= RKLUnlockedCacheSpinLock; // Warning about rkl_cacheSpinLockStatus never being read can be safely ignored. 02300 } 02301 02302 #pragma mark -rangeOfRegex: 02303 02304 - (NSRange)RKL_METHOD_PREPEND(rangeOfRegex):(NSString *)regex 02305 { 02306 NSRange result = NSNotFoundRange, range = NSMaxiumRange; 02307 rkl_performRegexOp(self, _cmd, (RKLRegexOp)RKLRangeOp, regex, RKLNoOptions, 0L, self, &range, NULL, NULL, &result, 0UL, NULL, NULL); 02308 return(result); 02309 } 02310 02311 - (NSRange)RKL_METHOD_PREPEND(rangeOfRegex):(NSString *)regex capture:(NSInteger)capture 02312 { 02313 NSRange result = NSNotFoundRange, range = NSMaxiumRange; 02314 rkl_performRegexOp(self, _cmd, (RKLRegexOp)RKLRangeOp, regex, RKLNoOptions, capture, self, &range, NULL, NULL, &result, 0UL, NULL, NULL); 02315 return(result); 02316 } 02317 02318 - (NSRange)RKL_METHOD_PREPEND(rangeOfRegex):(NSString *)regex inRange:(NSRange)range 02319 { 02320 NSRange result = NSNotFoundRange; 02321 rkl_performRegexOp(self, _cmd, (RKLRegexOp)RKLRangeOp, regex, RKLNoOptions, 0L, self, &range, NULL, NULL, &result, 0UL, NULL, NULL); 02322 return(result); 02323 } 02324 02325 - (NSRange)RKL_METHOD_PREPEND(rangeOfRegex):(NSString *)regex options:(RKLRegexOptions)options inRange:(NSRange)range capture:(NSInteger)capture error:(NSError **)error 02326 { 02327 NSRange result = NSNotFoundRange; 02328 rkl_performRegexOp(self, _cmd, (RKLRegexOp)RKLRangeOp, regex, options, capture, self, &range, NULL, error, &result, 0UL, NULL, NULL); 02329 return(result); 02330 } 02331 02332 #pragma mark -stringByMatching: 02333 02334 - (NSString *)RKL_METHOD_PREPEND(stringByMatching):(NSString *)regex 02335 { 02336 NSRange matchedRange = NSNotFoundRange, range = NSMaxiumRange; 02337 rkl_performRegexOp(self, _cmd, (RKLRegexOp)RKLRangeOp, regex, RKLNoOptions, 0L, self, &range, NULL, NULL, &matchedRange, 0UL, NULL, NULL); 02338 return((matchedRange.location == (NSUInteger)NSNotFound) ? NULL : rkl_CFAutorelease(CFStringCreateWithSubstring(NULL, (CFStringRef)self, CFMakeRange(matchedRange.location, matchedRange.length)))); // Warning about potential leak can be safely ignored. 02339 } // Warning about potential leak can be safely ignored. 02340 02341 - (NSString *)RKL_METHOD_PREPEND(stringByMatching):(NSString *)regex capture:(NSInteger)capture 02342 { 02343 NSRange matchedRange = NSNotFoundRange, range = NSMaxiumRange; 02344 rkl_performRegexOp(self, _cmd, (RKLRegexOp)RKLRangeOp, regex, RKLNoOptions, capture, self, &range, NULL, NULL, &matchedRange, 0UL, NULL, NULL); 02345 return((matchedRange.location == (NSUInteger)NSNotFound) ? NULL : rkl_CFAutorelease(CFStringCreateWithSubstring(NULL, (CFStringRef)self, CFMakeRange(matchedRange.location, matchedRange.length)))); // Warning about potential leak can be safely ignored. 02346 } // Warning about potential leak can be safely ignored. 02347 02348 - (NSString *)RKL_METHOD_PREPEND(stringByMatching):(NSString *)regex inRange:(NSRange)range 02349 { 02350 NSRange matchedRange = NSNotFoundRange; 02351 rkl_performRegexOp(self, _cmd, (RKLRegexOp)RKLRangeOp, regex, RKLNoOptions, 0L, self, &range, NULL, NULL, &matchedRange, 0UL, NULL, NULL); 02352 return((matchedRange.location == (NSUInteger)NSNotFound) ? NULL : rkl_CFAutorelease(CFStringCreateWithSubstring(NULL, (CFStringRef)self, CFMakeRange(matchedRange.location, matchedRange.length)))); // Warning about potential leak can be safely ignored. 02353 } // Warning about potential leak can be safely ignored. 02354 02355 - (NSString *)RKL_METHOD_PREPEND(stringByMatching):(NSString *)regex options:(RKLRegexOptions)options inRange:(NSRange)range capture:(NSInteger)capture error:(NSError **)error 02356 { 02357 NSRange matchedRange = NSNotFoundRange; 02358 rkl_performRegexOp(self, _cmd, (RKLRegexOp)RKLRangeOp, regex, options, capture, self, &range, NULL, error, &matchedRange, 0UL, NULL, NULL); 02359 return((matchedRange.location == (NSUInteger)NSNotFound) ? NULL : rkl_CFAutorelease(CFStringCreateWithSubstring(NULL, (CFStringRef)self, CFMakeRange(matchedRange.location, matchedRange.length)))); // Warning about potential leak can be safely ignored. 02360 } // Warning about potential leak can be safely ignored. 02361 02362 #pragma mark -stringByReplacingOccurrencesOfRegex: 02363 02364 - (NSString *)RKL_METHOD_PREPEND(stringByReplacingOccurrencesOfRegex):(NSString *)regex withString:(NSString *)replacement 02365 { 02366 NSRange searchRange = NSMaxiumRange; 02367 return(rkl_performRegexOp(self, _cmd, (RKLRegexOp)RKLReplaceOp, regex, RKLNoOptions, 0L, self, &searchRange, replacement, NULL, NULL, 0UL, NULL, NULL)); 02368 } 02369 02370 - (NSString *)RKL_METHOD_PREPEND(stringByReplacingOccurrencesOfRegex):(NSString *)regex withString:(NSString *)replacement range:(NSRange)searchRange 02371 { 02372 return(rkl_performRegexOp(self, _cmd, (RKLRegexOp)RKLReplaceOp, regex, RKLNoOptions, 0L, self, &searchRange, replacement, NULL, NULL, 0UL, NULL, NULL)); 02373 } 02374 02375 - (NSString *)RKL_METHOD_PREPEND(stringByReplacingOccurrencesOfRegex):(NSString *)regex withString:(NSString *)replacement options:(RKLRegexOptions)options range:(NSRange)searchRange error:(NSError **)error 02376 { 02377 return(rkl_performRegexOp(self, _cmd, (RKLRegexOp)RKLReplaceOp, regex, options, 0L, self, &searchRange, replacement, error, NULL, 0UL, NULL, NULL)); 02378 } 02379 02380 #pragma mark -componentsMatchedByRegex: 02381 02382 - (NSArray *)RKL_METHOD_PREPEND(componentsMatchedByRegex):(NSString *)regex 02383 { 02384 NSRange searchRange = NSMaxiumRange; 02385 return(rkl_performRegexOp(self, _cmd, (RKLRegexOp)RKLArrayOfStringsOp, regex, RKLNoOptions, 0L, self, &searchRange, NULL, NULL, NULL, 0UL, NULL, NULL)); 02386 } 02387 02388 - (NSArray *)RKL_METHOD_PREPEND(componentsMatchedByRegex):(NSString *)regex capture:(NSInteger)capture 02389 { 02390 NSRange searchRange = NSMaxiumRange; 02391 return(rkl_performRegexOp(self, _cmd, (RKLRegexOp)RKLArrayOfStringsOp, regex, RKLNoOptions, capture, self, &searchRange, NULL, NULL, NULL, 0UL, NULL, NULL)); 02392 } 02393 02394 - (NSArray *)RKL_METHOD_PREPEND(componentsMatchedByRegex):(NSString *)regex range:(NSRange)range 02395 { 02396 return(rkl_performRegexOp(self, _cmd, (RKLRegexOp)RKLArrayOfStringsOp, regex, RKLNoOptions, 0L, self, &range, NULL, NULL, NULL, 0UL, NULL, NULL)); 02397 } 02398 02399 - (NSArray *)RKL_METHOD_PREPEND(componentsMatchedByRegex):(NSString *)regex options:(RKLRegexOptions)options range:(NSRange)range capture:(NSInteger)capture error:(NSError **)error 02400 { 02401 return(rkl_performRegexOp(self, _cmd, (RKLRegexOp)RKLArrayOfStringsOp, regex, options, capture, self, &range, NULL, error, NULL, 0UL, NULL, NULL)); 02402 } 02403 02404 #pragma mark -captureComponentsMatchedByRegex: 02405 02406 - (NSArray *)RKL_METHOD_PREPEND(captureComponentsMatchedByRegex):(NSString *)regex 02407 { 02408 NSRange searchRange = NSMaxiumRange; 02409 return(rkl_performRegexOp(self, _cmd, (RKLRegexOp)RKLCapturesArrayOp, regex, RKLNoOptions, 0L, self, &searchRange, NULL, NULL, NULL, 0UL, NULL, NULL)); 02410 } 02411 02412 - (NSArray *)RKL_METHOD_PREPEND(captureComponentsMatchedByRegex):(NSString *)regex range:(NSRange)range 02413 { 02414 return(rkl_performRegexOp(self, _cmd, (RKLRegexOp)RKLCapturesArrayOp, regex, RKLNoOptions, 0L, self, &range, NULL, NULL, NULL, 0UL, NULL, NULL)); 02415 } 02416 02417 - (NSArray *)RKL_METHOD_PREPEND(captureComponentsMatchedByRegex):(NSString *)regex options:(RKLRegexOptions)options range:(NSRange)range error:(NSError **)error 02418 { 02419 return(rkl_performRegexOp(self, _cmd, (RKLRegexOp)RKLCapturesArrayOp, regex, options, 0L, self, &range, NULL, error, NULL, 0UL, NULL, NULL)); 02420 } 02421 02422 #pragma mark -arrayOfCaptureComponentsMatchedByRegex: 02423 02424 - (NSArray *)RKL_METHOD_PREPEND(arrayOfCaptureComponentsMatchedByRegex):(NSString *)regex 02425 { 02426 NSRange searchRange = NSMaxiumRange; 02427 return(rkl_performRegexOp(self, _cmd, (RKLRegexOp)(RKLArrayOfCapturesOp | RKLSubcapturesArray), regex, RKLNoOptions, 0L, self, &searchRange, NULL, NULL, NULL, 0UL, NULL, NULL)); 02428 } 02429 02430 - (NSArray *)RKL_METHOD_PREPEND(arrayOfCaptureComponentsMatchedByRegex):(NSString *)regex range:(NSRange)range 02431 { 02432 return(rkl_performRegexOp(self, _cmd, (RKLRegexOp)(RKLArrayOfCapturesOp | RKLSubcapturesArray), regex, RKLNoOptions, 0L, self, &range, NULL, NULL, NULL, 0UL, NULL, NULL)); 02433 } 02434 02435 - (NSArray *)RKL_METHOD_PREPEND(arrayOfCaptureComponentsMatchedByRegex):(NSString *)regex options:(RKLRegexOptions)options range:(NSRange)range error:(NSError **)error 02436 { 02437 return(rkl_performRegexOp(self, _cmd, (RKLRegexOp)(RKLArrayOfCapturesOp | RKLSubcapturesArray), regex, options, 0L, self, &range, NULL, error, NULL, 0UL, NULL, NULL)); 02438 } 02439 02440 #pragma mark -dictionaryByMatchingRegex: 02441 02442 - (NSDictionary *)RKL_METHOD_PREPEND(dictionaryByMatchingRegex):(NSString *)regex withKeysAndCaptures:(id)firstKey, ... 02443 { 02444 NSRange searchRange = NSMaxiumRange; 02445 id returnObject = NULL; 02446 va_list varArgsList; 02447 va_start(varArgsList, firstKey); 02448 returnObject = rkl_performDictionaryVarArgsOp(self, _cmd, (RKLRegexOp)RKLDictionaryOfCapturesOp, regex, (RKLRegexOptions)RKLNoOptions, 0L, self, &searchRange, NULL, NULL, NULL, firstKey, varArgsList); 02449 va_end(varArgsList); 02450 return(returnObject); 02451 } 02452 02453 - (NSDictionary *)RKL_METHOD_PREPEND(dictionaryByMatchingRegex):(NSString *)regex range:(NSRange)range withKeysAndCaptures:(id)firstKey, ... 02454 { 02455 id returnObject = NULL; 02456 va_list varArgsList; 02457 va_start(varArgsList, firstKey); 02458 returnObject = rkl_performDictionaryVarArgsOp(self, _cmd, (RKLRegexOp)RKLDictionaryOfCapturesOp, regex, (RKLRegexOptions)RKLNoOptions, 0L, self, &range, NULL, NULL, NULL, firstKey, varArgsList); 02459 va_end(varArgsList); 02460 return(returnObject); 02461 } 02462 02463 - (NSDictionary *)RKL_METHOD_PREPEND(dictionaryByMatchingRegex):(NSString *)regex options:(RKLRegexOptions)options range:(NSRange)range error:(NSError **)error withKeysAndCaptures:(id)firstKey, ... 02464 { 02465 id returnObject = NULL; 02466 va_list varArgsList; 02467 va_start(varArgsList, firstKey); 02468 returnObject = rkl_performDictionaryVarArgsOp(self, _cmd, (RKLRegexOp)RKLDictionaryOfCapturesOp, regex, options, 0L, self, &range, NULL, error, NULL, firstKey, varArgsList); 02469 va_end(varArgsList); 02470 return(returnObject); 02471 } 02472 02473 - (NSDictionary *)RKL_METHOD_PREPEND(dictionaryByMatchingRegex):(NSString *)regex options:(RKLRegexOptions)options range:(NSRange)range error:(NSError **)error withFirstKey:(id)firstKey arguments:(va_list)varArgsList 02474 { 02475 return(rkl_performDictionaryVarArgsOp(self, _cmd, (RKLRegexOp)RKLDictionaryOfCapturesOp, regex, options, 0L, self, &range, NULL, error, NULL, firstKey, varArgsList)); 02476 } 02477 02478 - (NSDictionary *)RKL_METHOD_PREPEND(dictionaryByMatchingRegex):(NSString *)regex options:(RKLRegexOptions)options range:(NSRange)range error:(NSError **)error withKeys:(id *)keys forCaptures:(int *)captures count:(NSUInteger)count 02479 { 02480 return(rkl_performRegexOp(self, _cmd, (RKLRegexOp)RKLDictionaryOfCapturesOp, regex, options, 0L, self, &range, NULL, error, NULL, count, keys, captures)); 02481 } 02482 02483 #pragma mark -arrayOfDictionariesByMatchingRegex: 02484 02485 - (NSArray *)RKL_METHOD_PREPEND(arrayOfDictionariesByMatchingRegex):(NSString *)regex withKeysAndCaptures:(id)firstKey, ... 02486 { 02487 NSRange searchRange = NSMaxiumRange; 02488 id returnObject = NULL; 02489 va_list varArgsList; 02490 va_start(varArgsList, firstKey); 02491 returnObject = rkl_performDictionaryVarArgsOp(self, _cmd, (RKLRegexOp)RKLArrayOfDictionariesOfCapturesOp, regex, (RKLRegexOptions)RKLNoOptions, 0L, self, &searchRange, NULL, NULL, NULL, firstKey, varArgsList); 02492 va_end(varArgsList); 02493 return(returnObject); 02494 } 02495 02496 - (NSArray *)RKL_METHOD_PREPEND(arrayOfDictionariesByMatchingRegex):(NSString *)regex range:(NSRange)range withKeysAndCaptures:(id)firstKey, ... 02497 { 02498 id returnObject = NULL; 02499 va_list varArgsList; 02500 va_start(varArgsList, firstKey); 02501 returnObject = rkl_performDictionaryVarArgsOp(self, _cmd, (RKLRegexOp)RKLArrayOfDictionariesOfCapturesOp, regex, (RKLRegexOptions)RKLNoOptions, 0L, self, &range, NULL, NULL, NULL, firstKey, varArgsList); 02502 va_end(varArgsList); 02503 return(returnObject); 02504 } 02505 02506 - (NSArray *)RKL_METHOD_PREPEND(arrayOfDictionariesByMatchingRegex):(NSString *)regex options:(RKLRegexOptions)options range:(NSRange)range error:(NSError **)error withKeysAndCaptures:(id)firstKey, ... 02507 { 02508 id returnObject = NULL; 02509 va_list varArgsList; 02510 va_start(varArgsList, firstKey); 02511 returnObject = rkl_performDictionaryVarArgsOp(self, _cmd, (RKLRegexOp)RKLArrayOfDictionariesOfCapturesOp, regex, options, 0L, self, &range, NULL, error, NULL, firstKey, varArgsList); 02512 va_end(varArgsList); 02513 return(returnObject); 02514 } 02515 02516 - (NSArray *)RKL_METHOD_PREPEND(arrayOfDictionariesByMatchingRegex):(NSString *)regex options:(RKLRegexOptions)options range:(NSRange)range error:(NSError **)error withFirstKey:(id)firstKey arguments:(va_list)varArgsList 02517 { 02518 return(rkl_performDictionaryVarArgsOp(self, _cmd, (RKLRegexOp)RKLArrayOfDictionariesOfCapturesOp, regex, options, 0L, self, &range, NULL, error, NULL, firstKey, varArgsList)); 02519 } 02520 02521 - (NSArray *)RKL_METHOD_PREPEND(arrayOfDictionariesByMatchingRegex):(NSString *)regex options:(RKLRegexOptions)options range:(NSRange)range error:(NSError **)error withKeys:(id *)keys forCaptures:(int *)captures count:(NSUInteger)count 02522 { 02523 return(rkl_performRegexOp(self, _cmd, (RKLRegexOp)RKLArrayOfDictionariesOfCapturesOp, regex, options, 0L, self, &range, NULL, error, NULL, count, keys, captures)); 02524 } 02525 02526 #ifdef _RKL_BLOCKS_ENABLED 02527 02529 #pragma mark - 02530 #pragma mark ^Blocks Related NSString Methods 02531 02532 #pragma mark -enumerateStringsMatchedByRegex:usingBlock: 02533 02534 - (BOOL)RKL_METHOD_PREPEND(enumerateStringsMatchedByRegex):(NSString *)regex usingBlock:(void (^)(NSInteger captureCount, NSString * const capturedStrings[captureCount], const NSRange capturedRanges[captureCount], volatile BOOL * const stop))block 02535 { 02536 NSUInteger errorFree = NO; 02537 rkl_performEnumerationUsingBlock(self, _cmd, (RKLRegexOp)RKLCapturesArrayOp, regex, (RKLRegexOptions)RKLNoOptions, self, NSMaxiumRange, (RKLBlockEnumerationOp)RKLBlockEnumerationMatchOp, 0UL, NULL, &errorFree, NULL, block, NULL); 02538 return(errorFree == NO ? NO : YES); 02539 } 02540 02541 - (BOOL)RKL_METHOD_PREPEND(enumerateStringsMatchedByRegex):(NSString *)regex options:(RKLRegexOptions)options inRange:(NSRange)range error:(NSError **)error enumerationOptions:(RKLRegexEnumerationOptions)enumerationOptions usingBlock:(void (^)(NSInteger captureCount, NSString * const capturedStrings[captureCount], const NSRange capturedRanges[captureCount], volatile BOOL * const stop))block 02542 { 02543 NSUInteger errorFree = NO; 02544 rkl_performEnumerationUsingBlock(self, _cmd, (RKLRegexOp)RKLCapturesArrayOp, regex, options, self, range, (RKLBlockEnumerationOp)RKLBlockEnumerationMatchOp, enumerationOptions, NULL, &errorFree, error, block, NULL); 02545 return(errorFree == NO ? NO : YES); 02546 } 02547 02548 #pragma mark -enumerateStringsSeparatedByRegex:usingBlock: 02549 02550 - (BOOL)RKL_METHOD_PREPEND(enumerateStringsSeparatedByRegex):(NSString *)regex usingBlock:(void (^)(NSInteger captureCount, NSString * const capturedStrings[captureCount], const NSRange capturedRanges[captureCount], volatile BOOL * const stop))block 02551 { 02552 NSUInteger errorFree = NO; 02553 rkl_performEnumerationUsingBlock(self, _cmd, (RKLRegexOp)RKLSplitOp, regex, (RKLRegexOptions)RKLNoOptions, self, NSMaxiumRange, (RKLBlockEnumerationOp)RKLBlockEnumerationMatchOp, 0UL, NULL, &errorFree, NULL, block, NULL); 02554 return(errorFree == NO ? NO : YES); 02555 } 02556 02557 - (BOOL)RKL_METHOD_PREPEND(enumerateStringsSeparatedByRegex):(NSString *)regex options:(RKLRegexOptions)options inRange:(NSRange)range error:(NSError **)error enumerationOptions:(RKLRegexEnumerationOptions)enumerationOptions usingBlock:(void (^)(NSInteger captureCount, NSString * const capturedStrings[captureCount], const NSRange capturedRanges[captureCount], volatile BOOL * const stop))block 02558 { 02559 NSUInteger errorFree = NO; 02560 rkl_performEnumerationUsingBlock(self, _cmd, (RKLRegexOp)RKLSplitOp, regex, options, self, range, (RKLBlockEnumerationOp)RKLBlockEnumerationMatchOp, enumerationOptions, NULL, &errorFree, error, block, NULL); 02561 return(errorFree == NO ? NO : YES); 02562 } 02563 02564 #pragma mark -stringByReplacingOccurrencesOfRegex:usingBlock: 02565 02566 - (NSString *)RKL_METHOD_PREPEND(stringByReplacingOccurrencesOfRegex):(NSString *)regex usingBlock:(NSString *(^)(NSInteger captureCount, NSString * const capturedStrings[captureCount], const NSRange capturedRanges[captureCount], volatile BOOL * const stop))block 02567 { 02568 return(rkl_performEnumerationUsingBlock(self, _cmd, (RKLRegexOp)RKLCapturesArrayOp, regex, (RKLRegexOptions)RKLNoOptions, self, NSMaxiumRange, (RKLBlockEnumerationOp)RKLBlockEnumerationReplaceOp, 0UL, NULL, NULL, NULL, NULL, block)); 02569 } 02570 02571 - (NSString *)RKL_METHOD_PREPEND(stringByReplacingOccurrencesOfRegex):(NSString *)regex options:(RKLRegexOptions)options inRange:(NSRange)range error:(NSError **)error enumerationOptions:(RKLRegexEnumerationOptions)enumerationOptions usingBlock:(NSString *(^)(NSInteger captureCount, NSString * const capturedStrings[captureCount], const NSRange capturedRanges[captureCount], volatile BOOL * const stop))block 02572 { 02573 return(rkl_performEnumerationUsingBlock(self, _cmd, (RKLRegexOp)RKLCapturesArrayOp, regex, options, self, range, (RKLBlockEnumerationOp)RKLBlockEnumerationReplaceOp, enumerationOptions, NULL, NULL, error, NULL, block)); 02574 } 02575 02576 #endif // _RKL_BLOCKS_ENABLED 02577 02578 @end 02579 02581 #pragma mark - 02582 @implementation NSMutableString (RegexKitLiteAdditions) 02583 02584 #pragma mark -replaceOccurrencesOfRegex: 02585 02586 - (NSInteger)RKL_METHOD_PREPEND(replaceOccurrencesOfRegex):(NSString *)regex withString:(NSString *)replacement 02587 { 02588 NSRange searchRange = NSMaxiumRange; 02589 NSInteger replacedCount = -1L; 02590 rkl_performRegexOp(self, _cmd, (RKLRegexOp)(RKLReplaceOp | RKLReplaceMutable), regex, RKLNoOptions, 0L, self, &searchRange, replacement, NULL, (void **)((void *)&replacedCount), 0UL, NULL, NULL); 02591 return(replacedCount); 02592 } 02593 02594 - (NSInteger)RKL_METHOD_PREPEND(replaceOccurrencesOfRegex):(NSString *)regex withString:(NSString *)replacement range:(NSRange)searchRange 02595 { 02596 NSInteger replacedCount = -1L; 02597 rkl_performRegexOp(self, _cmd, (RKLRegexOp)(RKLReplaceOp | RKLReplaceMutable), regex, RKLNoOptions, 0L, self, &searchRange, replacement, NULL, (void **)((void *)&replacedCount), 0UL, NULL, NULL); 02598 return(replacedCount); 02599 } 02600 02601 - (NSInteger)RKL_METHOD_PREPEND(replaceOccurrencesOfRegex):(NSString *)regex withString:(NSString *)replacement options:(RKLRegexOptions)options range:(NSRange)searchRange error:(NSError **)error 02602 { 02603 NSInteger replacedCount = -1L; 02604 rkl_performRegexOp(self, _cmd, (RKLRegexOp)(RKLReplaceOp | RKLReplaceMutable), regex, options, 0L, self, &searchRange, replacement, error, (void **)((void *)&replacedCount), 0UL, NULL, NULL); 02605 return(replacedCount); 02606 } 02607 02608 #ifdef _RKL_BLOCKS_ENABLED 02609 02611 #pragma mark - 02612 #pragma mark ^Blocks Related NSMutableString Methods 02613 02614 #pragma mark -replaceOccurrencesOfRegex:usingBlock: 02615 02616 - (NSInteger)RKL_METHOD_PREPEND(replaceOccurrencesOfRegex):(NSString *)regex usingBlock:(NSString *(^)(NSInteger captureCount, NSString * const capturedStrings[captureCount], const NSRange capturedRanges[captureCount], volatile BOOL * const stop))block 02617 { 02618 NSUInteger errorFree = 0UL; 02619 NSInteger replacedCount = -1L; 02620 NSString *replacedString = rkl_performEnumerationUsingBlock(self, _cmd, (RKLRegexOp)RKLCapturesArrayOp, regex, RKLNoOptions, self, NSMaxiumRange, (RKLBlockEnumerationOp)RKLBlockEnumerationReplaceOp, 0UL, &replacedCount, &errorFree, NULL, NULL, block); 02621 if((errorFree == YES) && (replacedCount > 0L)) { [self replaceCharactersInRange:NSMakeRange(0UL, [self length]) withString:replacedString]; } 02622 return(replacedCount); 02623 } 02624 02625 - (NSInteger)RKL_METHOD_PREPEND(replaceOccurrencesOfRegex):(NSString *)regex options:(RKLRegexOptions)options inRange:(NSRange)range error:(NSError **)error enumerationOptions:(RKLRegexEnumerationOptions)enumerationOptions usingBlock:(NSString *(^)(NSInteger captureCount, NSString * const capturedStrings[captureCount], const NSRange capturedRanges[captureCount], volatile BOOL * const stop))block 02626 { 02627 NSUInteger errorFree = 0UL; 02628 NSInteger replacedCount = -1L; 02629 NSString *replacedString = rkl_performEnumerationUsingBlock(self, _cmd, (RKLRegexOp)RKLCapturesArrayOp, regex, options, self, range, (RKLBlockEnumerationOp)RKLBlockEnumerationReplaceOp, enumerationOptions, &replacedCount, &errorFree, error, NULL, block); 02630 if((errorFree == YES) && (replacedCount > 0L)) { [self replaceCharactersInRange:range withString:replacedString]; } 02631 return(replacedCount); 02632 } 02633 02634 #endif // _RKL_BLOCKS_ENABLED 02635 02636 @end