Permalink
Cannot retrieve contributors at this time
// This source file is part of the Swift.org open source project | |
// | |
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors | |
// Licensed under Apache License v2.0 with Runtime Library Exception | |
// | |
// See http://swift.org/LICENSE.txt for license information | |
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors | |
// | |
import CoreFoundation | |
public typealias unichar = UInt16 | |
extension unichar : UnicodeScalarLiteralConvertible { | |
public typealias UnicodeScalarLiteralType = UnicodeScalar | |
public init(unicodeScalarLiteral scalar: UnicodeScalar) { | |
self.init(scalar.value) | |
} | |
} | |
#if os(OSX) || os(iOS) | |
internal let kCFStringEncodingMacRoman = CFStringBuiltInEncodings.MacRoman.rawValue | |
internal let kCFStringEncodingWindowsLatin1 = CFStringBuiltInEncodings.WindowsLatin1.rawValue | |
internal let kCFStringEncodingISOLatin1 = CFStringBuiltInEncodings.ISOLatin1.rawValue | |
internal let kCFStringEncodingNextStepLatin = CFStringBuiltInEncodings.NextStepLatin.rawValue | |
internal let kCFStringEncodingASCII = CFStringBuiltInEncodings.ASCII.rawValue | |
internal let kCFStringEncodingUnicode = CFStringBuiltInEncodings.Unicode.rawValue | |
internal let kCFStringEncodingUTF8 = CFStringBuiltInEncodings.UTF8.rawValue | |
internal let kCFStringEncodingNonLossyASCII = CFStringBuiltInEncodings.NonLossyASCII.rawValue | |
internal let kCFStringEncodingUTF16 = CFStringBuiltInEncodings.UTF16.rawValue | |
internal let kCFStringEncodingUTF16BE = CFStringBuiltInEncodings.UTF16BE.rawValue | |
internal let kCFStringEncodingUTF16LE = CFStringBuiltInEncodings.UTF16LE.rawValue | |
internal let kCFStringEncodingUTF32 = CFStringBuiltInEncodings.UTF32.rawValue | |
internal let kCFStringEncodingUTF32BE = CFStringBuiltInEncodings.UTF32BE.rawValue | |
internal let kCFStringEncodingUTF32LE = CFStringBuiltInEncodings.UTF32LE.rawValue | |
internal let kCFStringGraphemeCluster = CFStringCharacterClusterType.GraphemeCluster | |
internal let kCFStringComposedCharacterCluster = CFStringCharacterClusterType.ComposedCharacterCluster | |
internal let kCFStringCursorMovementCluster = CFStringCharacterClusterType.CursorMovementCluster | |
internal let kCFStringBackwardDeletionCluster = CFStringCharacterClusterType.BackwardDeletionCluster | |
internal let kCFStringNormalizationFormD = CFStringNormalizationForm.D | |
internal let kCFStringNormalizationFormKD = CFStringNormalizationForm.KD | |
internal let kCFStringNormalizationFormC = CFStringNormalizationForm.C | |
internal let kCFStringNormalizationFormKC = CFStringNormalizationForm.KC | |
#endif | |
public struct NSStringEncodingConversionOptions : OptionSetType { | |
public let rawValue : UInt | |
public init(rawValue: UInt) { self.rawValue = rawValue } | |
public static let AllowLossy = NSStringEncodingConversionOptions(rawValue: 1) | |
public static let ExternalRepresentation = NSStringEncodingConversionOptions(rawValue: 2) | |
internal static let FailOnPartialEncodingConversion = NSStringEncodingConversionOptions(rawValue: 1 << 20) | |
} | |
public struct NSStringEnumerationOptions : OptionSetType { | |
public let rawValue : UInt | |
public init(rawValue: UInt) { self.rawValue = rawValue } | |
public static let ByLines = NSStringEnumerationOptions(rawValue: 0) | |
public static let ByParagraphs = NSStringEnumerationOptions(rawValue: 1) | |
public static let ByComposedCharacterSequences = NSStringEnumerationOptions(rawValue: 2) | |
public static let ByWords = NSStringEnumerationOptions(rawValue: 3) | |
public static let BySentences = NSStringEnumerationOptions(rawValue: 4) | |
public static let Reverse = NSStringEnumerationOptions(rawValue: 1 << 8) | |
public static let SubstringNotRequired = NSStringEnumerationOptions(rawValue: 1 << 9) | |
public static let Localized = NSStringEnumerationOptions(rawValue: 1 << 10) | |
internal static let ForceFullTokens = NSStringEnumerationOptions(rawValue: 1 << 20) | |
} | |
extension String : _ObjectTypeBridgeable { | |
public func _bridgeToObject() -> NSString { | |
return NSString(self) | |
} | |
public static func _forceBridgeFromObject(x: NSString, inout result: String?) { | |
if x.dynamicType == NSString.self || x.dynamicType == NSMutableString.self { | |
result = x._storage | |
} else if x.dynamicType == _NSCFString.self { | |
let cf = unsafeBitCast(x, CFString.self) | |
let str = CFStringGetCStringPtr(cf, CFStringEncoding(kCFStringEncodingUTF8)) | |
if str != nil { | |
result = String.fromCString(str) | |
} else { | |
let length = CFStringGetLength(cf) | |
let buffer = UnsafeMutablePointer<UniChar>.alloc(length) | |
CFStringGetCharacters(cf, CFRangeMake(0, length), buffer) | |
let str = String._fromCodeUnitSequence(UTF16.self, input: UnsafeBufferPointer(start: buffer, count: length)) | |
buffer.destroy(length) | |
buffer.dealloc(length) | |
result = str | |
} | |
} else if x.dynamicType == _NSCFConstantString.self { | |
let conststr = unsafeBitCast(x, _NSCFConstantString.self) | |
let str = String._fromCodeUnitSequence(UTF8.self, input: UnsafeBufferPointer(start: conststr._ptr, count: Int(conststr._length))) | |
result = str | |
} else { | |
let len = x.length | |
var characters = [unichar](count: len, repeatedValue: 0) | |
result = characters.withUnsafeMutableBufferPointer() { (inout buffer: UnsafeMutableBufferPointer<unichar>) -> String? in | |
x.getCharacters(buffer.baseAddress, range: NSMakeRange(0, len)) | |
return String._fromCodeUnitSequence(UTF16.self, input: buffer) | |
} | |
} | |
} | |
public static func _conditionallyBridgeFromObject(x: NSString, inout result: String?) -> Bool { | |
self._forceBridgeFromObject(x, result: &result) | |
return result != nil | |
} | |
} | |
public struct NSStringCompareOptions : OptionSetType { | |
public let rawValue : UInt | |
public init(rawValue: UInt) { self.rawValue = rawValue } | |
public static let CaseInsensitiveSearch = NSStringCompareOptions(rawValue: 1) | |
public static let LiteralSearch = NSStringCompareOptions(rawValue: 2) | |
public static let BackwardsSearch = NSStringCompareOptions(rawValue: 4) | |
public static let AnchoredSearch = NSStringCompareOptions(rawValue: 8) | |
public static let NumericSearch = NSStringCompareOptions(rawValue: 64) | |
public static let DiacriticInsensitiveSearch = NSStringCompareOptions(rawValue: 128) | |
public static let WidthInsensitiveSearch = NSStringCompareOptions(rawValue: 256) | |
public static let ForcedOrderingSearch = NSStringCompareOptions(rawValue: 512) | |
public static let RegularExpressionSearch = NSStringCompareOptions(rawValue: 1024) | |
internal func _cfValue(fixLiteral: Bool = false) -> CFStringCompareFlags { | |
#if os(OSX) || os(iOS) | |
return contains(.LiteralSearch) || !fixLiteral ? CFStringCompareFlags(rawValue: rawValue) : CFStringCompareFlags(rawValue: rawValue).union(.CompareNonliteral) | |
#else | |
return contains(.LiteralSearch) || !fixLiteral ? CFStringCompareFlags(rawValue) : CFStringCompareFlags(rawValue) | UInt(kCFCompareNonliteral) | |
#endif | |
} | |
} | |
internal func _createRegexForPattern(pattern: String, _ options: NSRegularExpressionOptions) -> NSRegularExpression? { | |
struct local { | |
static let __NSRegularExpressionCache: NSCache = { | |
let cache = NSCache() | |
cache.name = "NSRegularExpressionCache" | |
cache.countLimit = 10 | |
return cache | |
}() | |
} | |
let key = "\(options):\(pattern)" | |
if let regex = local.__NSRegularExpressionCache.objectForKey(key._nsObject) { | |
return (regex as! NSRegularExpression) | |
} | |
do { | |
let regex = try NSRegularExpression(pattern: pattern, options: options) | |
local.__NSRegularExpressionCache.setObject(regex, forKey: key._nsObject) | |
return regex | |
} catch { | |
} | |
return nil | |
} | |
internal func _bytesInEncoding(str: NSString, _ encoding: NSStringEncoding, _ fatalOnError: Bool, _ externalRep: Bool, _ lossy: Bool) -> UnsafePointer<Int8> { | |
let theRange = NSMakeRange(0, str.length) | |
var cLength = 0 | |
var used = 0 | |
var options: NSStringEncodingConversionOptions = [] | |
if externalRep { | |
options.unionInPlace(.ExternalRepresentation) | |
} | |
if lossy { | |
options.unionInPlace(.AllowLossy) | |
} | |
if !str.getBytes(nil, maxLength: Int.max - 1, usedLength: &cLength, encoding: encoding, options: options, range: theRange, remainingRange: nil) { | |
if fatalOnError { | |
fatalError("Conversion on encoding failed") | |
} | |
return nil | |
} | |
let buffer = malloc(cLength + 1) | |
if !str.getBytes(buffer, maxLength: cLength, usedLength: &used, encoding: encoding, options: options, range: theRange, remainingRange: nil) { | |
fatalError("Internal inconsistency; previously claimed getBytes returned success but failed with similar invocation") | |
} | |
UnsafeMutablePointer<Int8>(buffer).advancedBy(cLength).initialize(0) | |
return UnsafePointer<Int8>(buffer) // leaked and should be autoreleased via a NSData backing but we cannot here | |
} | |
internal func isALineSeparatorTypeCharacter(ch: unichar) -> Bool { | |
if ch > 0x0d && ch < 0x0085 { /* Quick test to cover most chars */ | |
return false | |
} | |
return ch == 0x0a || ch == 0x0d || ch == 0x0085 || ch == 0x2028 || ch == 0x2029 | |
} | |
internal func isAParagraphSeparatorTypeCharacter(ch: unichar) -> Bool { | |
if ch > 0x0d && ch < 0x2029 { /* Quick test to cover most chars */ | |
return false | |
} | |
return ch == 0x0a || ch == 0x0d || ch == 0x2029 | |
} | |
public class NSString : NSObject, NSCopying, NSMutableCopying, NSSecureCoding, NSCoding { | |
private let _cfinfo = _CFInfo(typeID: CFStringGetTypeID()) | |
internal var _storage: String | |
public var length: Int { | |
if self.dynamicType === NSString.self || self.dynamicType === NSMutableString.self { | |
return _storage.utf16.count | |
} else { | |
NSRequiresConcreteImplementation() | |
} | |
} | |
public func characterAtIndex(index: Int) -> unichar { | |
if self.dynamicType === NSString.self || self.dynamicType === NSMutableString.self { | |
let start = _storage.utf16.startIndex | |
return _storage.utf16[start.advancedBy(index)] | |
} else { | |
NSRequiresConcreteImplementation() | |
} | |
} | |
public override convenience init() { | |
let characters = Array<unichar>(count: 1, repeatedValue: 0) | |
self.init(characters: characters, length: 0) | |
} | |
internal init(_ string: String) { | |
_storage = string | |
} | |
public convenience required init?(coder aDecoder: NSCoder) { | |
if !aDecoder.allowsKeyedCoding { | |
let archiveVersion = aDecoder.versionForClassName("NSString") | |
if archiveVersion == 1 { | |
var length = 0 | |
let buffer = aDecoder.decodeBytesWithReturnedLength(&length) | |
self.init(bytes: buffer, length: length, encoding: NSUTF8StringEncoding) | |
} else { | |
aDecoder.failWithError(NSError(domain: NSCocoaErrorDomain, code: NSCocoaError.CoderReadCorruptError.rawValue, userInfo: [ | |
"NSDebugDescription": "NSString cannot decode class version \(archiveVersion)" | |
])) | |
return nil | |
} | |
} else if aDecoder.dynamicType == NSKeyedUnarchiver.self || aDecoder.containsValueForKey("NS.string") { | |
let str = aDecoder._decodePropertyListForKey("NS.string") as! String | |
self.init(string: str) | |
} else { | |
var length = 0 | |
let buffer = UnsafeMutablePointer<Void>(aDecoder.decodeBytesForKey("NS.bytes", returnedLength: &length)) | |
self.init(bytes: buffer, length: length, encoding: NSUTF8StringEncoding) | |
} | |
} | |
public required convenience init(string aString: String) { | |
self.init(aString) | |
} | |
public override func copy() -> AnyObject { | |
return copyWithZone(nil) | |
} | |
public func copyWithZone(zone: NSZone) -> AnyObject { | |
return self | |
} | |
public override func mutableCopy() -> AnyObject { | |
return mutableCopyWithZone(nil) | |
} | |
public func mutableCopyWithZone(zone: NSZone) -> AnyObject { | |
if self.dynamicType === NSString.self || self.dynamicType === NSMutableString.self { | |
let contents = _fastContents | |
if contents != nil { | |
return NSMutableString(characters: contents, length: length) | |
} | |
} | |
let characters = UnsafeMutablePointer<unichar>.alloc(length) | |
getCharacters(characters, range: NSMakeRange(0, length)) | |
let result = NSMutableString(characters: characters, length: length) | |
characters.destroy() | |
characters.dealloc(length) | |
return result | |
} | |
public static func supportsSecureCoding() -> Bool { | |
return true | |
} | |
public func encodeWithCoder(aCoder: NSCoder) { | |
if let aKeyedCoder = aCoder as? NSKeyedArchiver { | |
aKeyedCoder._encodePropertyList(self, forKey: "NS.string") | |
} else { | |
aCoder.encodeObject(self) | |
} | |
} | |
public init(characters: UnsafePointer<unichar>, length: Int) { | |
_storage = String._fromWellFormedCodeUnitSequence(UTF16.self, input: UnsafeBufferPointer(start: characters, count: length)) | |
} | |
public required convenience init(unicodeScalarLiteral value: StaticString) { | |
self.init(stringLiteral: value) | |
} | |
public required convenience init(extendedGraphemeClusterLiteral value: StaticString) { | |
self.init(stringLiteral: value) | |
} | |
public required init(stringLiteral value: StaticString) { | |
_storage = value.stringValue | |
} | |
internal var _fastCStringContents: UnsafePointer<Int8> { | |
if self.dynamicType == NSString.self || self.dynamicType == NSMutableString.self { | |
if _storage._core.isASCII { | |
return unsafeBitCast(_storage._core.startASCII, UnsafePointer<Int8>.self) | |
} | |
} | |
return nil | |
} | |
internal var _fastContents: UnsafePointer<UniChar> { | |
if self.dynamicType == NSString.self || self.dynamicType == NSMutableString.self { | |
if !_storage._core.isASCII { | |
return unsafeBitCast(_storage._core.startUTF16, UnsafePointer<UniChar>.self) | |
} | |
} | |
return nil | |
} | |
internal var _encodingCantBeStoredInEightBitCFString: Bool { | |
if self.dynamicType == NSString.self || self.dynamicType == NSMutableString.self { | |
return !_storage._core.isASCII | |
} | |
return false | |
} | |
override public var _cfTypeID: CFTypeID { | |
return CFStringGetTypeID() | |
} | |
public override func isEqual(object: AnyObject?) -> Bool { | |
guard let string = (object as? NSString)?._swiftObject else { return false } | |
return self.isEqualToString(string) | |
} | |
public override var description: String { | |
return _swiftObject | |
} | |
public override var hash: Int { | |
return Int(bitPattern:CFStringHashNSString(self._cfObject)) | |
} | |
} | |
extension NSString { | |
public func getCharacters(buffer: UnsafeMutablePointer<unichar>, range: NSRange) { | |
for idx in 0..<range.length { | |
buffer[idx] = characterAtIndex(idx + range.location) | |
} | |
} | |
public func substringFromIndex(from: Int) -> String { | |
if self.dynamicType == NSString.self || self.dynamicType == NSMutableString.self { | |
return String(_storage.utf16.suffixFrom(_storage.utf16.startIndex.advancedBy(from))) | |
} else { | |
return substringWithRange(NSMakeRange(from, length - from)) | |
} | |
} | |
public func substringToIndex(to: Int) -> String { | |
if self.dynamicType == NSString.self || self.dynamicType == NSMutableString.self { | |
return String(_storage.utf16.prefixUpTo(_storage.utf16.startIndex | |
.advancedBy(to))) | |
} else { | |
return substringWithRange(NSMakeRange(0, to)) | |
} | |
} | |
public func substringWithRange(range: NSRange) -> String { | |
if self.dynamicType == NSString.self || self.dynamicType == NSMutableString.self { | |
let start = _storage.utf16.startIndex | |
let min = start.advancedBy(range.location) | |
let max = start.advancedBy(range.location + range.length) | |
return String(_storage.utf16[min..<max]) | |
} else { | |
let buff = UnsafeMutablePointer<unichar>.alloc(range.length) | |
getCharacters(buff, range: range) | |
let result = String(buff) | |
buff.destroy() | |
buff.dealloc(range.length) | |
return result | |
} | |
} | |
public func compare(string: String) -> NSComparisonResult { | |
return compare(string, options: [], range: NSMakeRange(0, length)) | |
} | |
public func compare(string: String, options mask: NSStringCompareOptions) -> NSComparisonResult { | |
return compare(string, options: mask, range: NSMakeRange(0, length)) | |
} | |
public func compare(string: String, options mask: NSStringCompareOptions, range compareRange: NSRange) -> NSComparisonResult { | |
return compare(string, options: mask, range: compareRange, locale: nil) | |
} | |
public func compare(string: String, options mask: NSStringCompareOptions, range compareRange: NSRange, locale: AnyObject?) -> NSComparisonResult { | |
var res: CFComparisonResult | |
if let loc = locale { | |
res = CFStringCompareWithOptionsAndLocale(_cfObject, string._cfObject, CFRange(compareRange), mask._cfValue(true), (loc as! NSLocale)._cfObject) | |
} else { | |
res = CFStringCompareWithOptionsAndLocale(_cfObject, string._cfObject, CFRange(compareRange), mask._cfValue(true), nil) | |
} | |
return NSComparisonResult._fromCF(res) | |
} | |
public func caseInsensitiveCompare(string: String) -> NSComparisonResult { | |
return compare(string, options: .CaseInsensitiveSearch, range: NSMakeRange(0, length)) | |
} | |
public func localizedCompare(string: String) -> NSComparisonResult { | |
return compare(string, options: [], range: NSMakeRange(0, length), locale: NSLocale.currentLocale()) | |
} | |
public func localizedCaseInsensitiveCompare(string: String) -> NSComparisonResult { | |
return compare(string, options: .CaseInsensitiveSearch, range: NSMakeRange(0, length), locale: NSLocale.currentLocale()) | |
} | |
public func localizedStandardCompare(string: String) -> NSComparisonResult { | |
return compare(string, options: [.CaseInsensitiveSearch, .NumericSearch, .WidthInsensitiveSearch, .ForcedOrderingSearch], range: NSMakeRange(0, length), locale: NSLocale.currentLocale()) | |
} | |
public func isEqualToString(aString: String) -> Bool { | |
if self.dynamicType == NSString.self || self.dynamicType == NSMutableString.self { | |
return _storage == aString | |
} else { | |
return length == aString.length && compare(aString, options: .LiteralSearch, range: NSMakeRange(0, length)) == .OrderedSame | |
} | |
} | |
public func hasPrefix(str: String) -> Bool { | |
return rangeOfString(str, options: .AnchoredSearch, range: NSMakeRange(0, length)).location != NSNotFound | |
} | |
public func hasSuffix(str: String) -> Bool { | |
return rangeOfString(str, options: [.AnchoredSearch, .BackwardsSearch], range: NSMakeRange(0, length)).location != NSNotFound | |
} | |
public func commonPrefixWithString(str: String, options mask: NSStringCompareOptions) -> String { | |
var currentSubstring: CFMutableString? | |
let isLiteral = mask.contains(.LiteralSearch) | |
var lastMatch = NSRange() | |
let selfLen = length | |
let otherLen = str.length | |
var low = 0 | |
var high = selfLen | |
var probe = (low + high) / 2 | |
if (probe > otherLen) { | |
probe = otherLen // A little heuristic to avoid some extra work | |
} | |
if selfLen == 0 || otherLen == 0 { | |
return "" | |
} | |
var numCharsBuffered = 0 | |
var arrayBuffer = [unichar](count: 100, repeatedValue: 0) | |
let other = str._nsObject | |
return arrayBuffer.withUnsafeMutablePointerOrAllocation(selfLen, fastpath: UnsafeMutablePointer<unichar>(_fastContents)) { (selfChars: UnsafeMutablePointer<unichar>) -> String in | |
// Now do the binary search. Note that the probe value determines the length of the substring to check. | |
while true { | |
let range = NSMakeRange(0, isLiteral ? probe + 1 : NSMaxRange(rangeOfComposedCharacterSequenceAtIndex(probe))) // Extend the end of the composed char sequence | |
if range.length > numCharsBuffered { // Buffer more characters if needed | |
getCharacters(selfChars, range: NSMakeRange(numCharsBuffered, range.length - numCharsBuffered)) | |
numCharsBuffered = range.length | |
} | |
if currentSubstring == nil { | |
currentSubstring = CFStringCreateMutableWithExternalCharactersNoCopy(kCFAllocatorSystemDefault, selfChars, range.length, range.length, kCFAllocatorNull) | |
} else { | |
CFStringSetExternalCharactersNoCopy(currentSubstring, selfChars, range.length, range.length) | |
} | |
if other.rangeOfString(currentSubstring!._swiftObject, options: mask.union(.AnchoredSearch), range: NSMakeRange(0, otherLen)).length != 0 { // Match | |
lastMatch = range | |
low = probe + 1 | |
} else { | |
high = probe | |
} | |
if low >= high { | |
break | |
} | |
probe = (low + high) / 2 | |
} | |
return lastMatch.length != 0 ? substringWithRange(lastMatch) : "" | |
} | |
} | |
public func containsString(str: String) -> Bool { | |
return rangeOfString(str, options: [], range: NSMakeRange(0, length), locale: nil).location != NSNotFound | |
} | |
public func localizedCaseInsensitiveContainsString(str: String) -> Bool { | |
return rangeOfString(str, options: .CaseInsensitiveSearch, range: NSMakeRange(0, length), locale: NSLocale.currentLocale()).location != NSNotFound | |
} | |
public func localizedStandardContainsString(str: String) -> Bool { | |
return rangeOfString(str, options: [.CaseInsensitiveSearch, .DiacriticInsensitiveSearch], range: NSMakeRange(0, length), locale: NSLocale.currentLocale()).location != NSNotFound | |
} | |
public func localizedStandardRangeOfString(str: String) -> NSRange { | |
return rangeOfString(str, options: [.CaseInsensitiveSearch, .DiacriticInsensitiveSearch], range: NSMakeRange(0, length), locale: NSLocale.currentLocale()) | |
} | |
public func rangeOfString(searchString: String) -> NSRange { | |
return rangeOfString(searchString, options: [], range: NSMakeRange(0, length), locale: nil) | |
} | |
public func rangeOfString(searchString: String, options mask: NSStringCompareOptions) -> NSRange { | |
return rangeOfString(searchString, options: mask, range: NSMakeRange(0, length), locale: nil) | |
} | |
public func rangeOfString(searchString: String, options mask: NSStringCompareOptions, range searchRange: NSRange) -> NSRange { | |
return rangeOfString(searchString, options: mask, range: searchRange, locale: nil) | |
} | |
internal func _rangeOfRegularExpressionPattern(regex pattern: String, options mask: NSStringCompareOptions, range searchRange: NSRange, locale: NSLocale?) -> NSRange { | |
var matchedRange = NSMakeRange(NSNotFound, 0) | |
let regexOptions: NSRegularExpressionOptions = mask.contains(.CaseInsensitiveSearch) ? .CaseInsensitive : [] | |
let matchingOptions: NSMatchingOptions = mask.contains(.AnchoredSearch) ? .Anchored : [] | |
if let regex = _createRegexForPattern(pattern, regexOptions) { | |
matchedRange = regex.rangeOfFirstMatchInString(_swiftObject, options: matchingOptions, range: searchRange) | |
} | |
return matchedRange | |
} | |
public func rangeOfString(searchString: String, options mask: NSStringCompareOptions, range searchRange: NSRange, locale: NSLocale?) -> NSRange { | |
let findStrLen = searchString.length | |
let len = length | |
precondition(searchRange.length <= len && searchRange.location <= len - searchRange.length, "Bounds Range {\(searchRange.location), \(searchRange.length)} out of bounds; string length \(len)") | |
if mask.contains(.RegularExpressionSearch) { | |
return _rangeOfRegularExpressionPattern(regex: searchString, options: mask, range:searchRange, locale: locale) | |
} | |
if searchRange.length == 0 || findStrLen == 0 { // ??? This last item can't be here for correct Unicode compares | |
return NSMakeRange(NSNotFound, 0) | |
} | |
var result = CFRange() | |
let res = withUnsafeMutablePointer(&result) { (rangep: UnsafeMutablePointer<CFRange>) -> Bool in | |
if let loc = locale { | |
return CFStringFindWithOptionsAndLocale(_cfObject, searchString._cfObject, CFRange(searchRange), mask._cfValue(true), loc._cfObject, rangep) | |
} else { | |
return CFStringFindWithOptionsAndLocale(_cfObject, searchString._cfObject, CFRange(searchRange), mask._cfValue(true), nil, rangep) | |
} | |
} | |
if res { | |
return NSMakeRange(result.location, result.length) | |
} else { | |
return NSMakeRange(NSNotFound, 0) | |
} | |
} | |
public func rangeOfCharacterFromSet(searchSet: NSCharacterSet) -> NSRange { | |
return rangeOfCharacterFromSet(searchSet, options: [], range: NSMakeRange(0, length)) | |
} | |
public func rangeOfCharacterFromSet(searchSet: NSCharacterSet, options mask: NSStringCompareOptions) -> NSRange { | |
return rangeOfCharacterFromSet(searchSet, options: mask, range: NSMakeRange(0, length)) | |
} | |
public func rangeOfCharacterFromSet(searchSet: NSCharacterSet, options mask: NSStringCompareOptions, range searchRange: NSRange) -> NSRange { | |
let len = length | |
precondition(searchRange.length <= len && searchRange.location <= len - searchRange.length, "Bounds Range {\(searchRange.location), \(searchRange.length)} out of bounds; string length \(len)") | |
var result = CFRange() | |
let res = withUnsafeMutablePointer(&result) { (rangep: UnsafeMutablePointer<CFRange>) -> Bool in | |
return CFStringFindCharacterFromSet(_cfObject, searchSet._cfObject, CFRange(searchRange), mask._cfValue(), rangep) | |
} | |
if res { | |
return NSMakeRange(result.location, result.length) | |
} else { | |
return NSMakeRange(NSNotFound, 0) | |
} | |
} | |
public func rangeOfComposedCharacterSequenceAtIndex(index: Int) -> NSRange { | |
let range = CFStringGetRangeOfCharacterClusterAtIndex(_cfObject, index, kCFStringComposedCharacterCluster) | |
return NSMakeRange(range.location, range.length) | |
} | |
public func rangeOfComposedCharacterSequencesForRange(range: NSRange) -> NSRange { | |
let length = self.length | |
var start: Int | |
var end: Int | |
if range.location == length { | |
start = length | |
} else { | |
start = rangeOfComposedCharacterSequenceAtIndex(range.location).location | |
} | |
var endOfRange = NSMaxRange(range) | |
if endOfRange == length { | |
end = length | |
} else { | |
if range.length > 0 { | |
endOfRange = endOfRange - 1 // We want 0-length range to be treated same as 1-length range. | |
} | |
end = NSMaxRange(rangeOfComposedCharacterSequenceAtIndex(endOfRange)) | |
} | |
return NSMakeRange(start, end - start) | |
} | |
public func stringByAppendingString(aString: String) -> String { | |
return _swiftObject + aString | |
} | |
public var doubleValue: Double { | |
var start: Int = 0 | |
var result = 0.0 | |
_swiftObject.scan(NSCharacterSet.whitespaceCharacterSet(), locale: nil, locationToScanFrom: &start) { (value: Double) -> Void in | |
result = value | |
} | |
return result | |
} | |
public var floatValue: Float { | |
var start: Int = 0 | |
var result: Float = 0.0 | |
_swiftObject.scan(NSCharacterSet.whitespaceCharacterSet(), locale: nil, locationToScanFrom: &start) { (value: Float) -> Void in | |
result = value | |
} | |
return result | |
} | |
public var intValue: Int32 { | |
return NSScanner(string: _swiftObject).scanInt() ?? 0 | |
} | |
public var integerValue: Int { | |
let scanner = NSScanner(string: _swiftObject) | |
var value: Int = 0 | |
scanner.scanInteger(&value) | |
return value | |
} | |
public var longLongValue: Int64 { | |
return NSScanner(string: _swiftObject).scanLongLong() ?? 0 | |
} | |
public var boolValue: Bool { | |
let scanner = NSScanner(string: _swiftObject) | |
// skip initial whitespace if present | |
scanner.scanCharactersFromSet(NSCharacterSet.whitespaceCharacterSet()) | |
// scan a single optional '+' or '-' character, followed by zeroes | |
if scanner.scanString(string: "+") == nil { | |
scanner.scanString(string: "-") | |
} | |
// scan any following zeroes | |
scanner.scanCharactersFromSet(NSCharacterSet(charactersInString: "0")) | |
return scanner.scanCharactersFromSet(NSCharacterSet(charactersInString: "tTyY123456789")) != nil | |
} | |
public var uppercaseString: String { | |
return uppercaseStringWithLocale(nil) | |
} | |
public var lowercaseString: String { | |
return lowercaseStringWithLocale(nil) | |
} | |
public var capitalizedString: String { | |
return capitalizedStringWithLocale(nil) | |
} | |
public var localizedUppercaseString: String { | |
return uppercaseStringWithLocale(NSLocale.currentLocale()) | |
} | |
public var localizedLowercaseString: String { | |
return lowercaseStringWithLocale(NSLocale.currentLocale()) | |
} | |
public var localizedCapitalizedString: String { | |
return capitalizedStringWithLocale(NSLocale.currentLocale()) | |
} | |
public func uppercaseStringWithLocale(locale: NSLocale?) -> String { | |
let mutableCopy = CFStringCreateMutableCopy(kCFAllocatorSystemDefault, 0, self._cfObject) | |
CFStringUppercase(mutableCopy, locale?._cfObject ?? nil) | |
return mutableCopy._swiftObject | |
} | |
public func lowercaseStringWithLocale(locale: NSLocale?) -> String { | |
let mutableCopy = CFStringCreateMutableCopy(kCFAllocatorSystemDefault, 0, self._cfObject) | |
CFStringLowercase(mutableCopy, locale?._cfObject ?? nil) | |
return mutableCopy._swiftObject | |
} | |
public func capitalizedStringWithLocale(locale: NSLocale?) -> String { | |
let mutableCopy = CFStringCreateMutableCopy(kCFAllocatorSystemDefault, 0, self._cfObject) | |
CFStringCapitalize(mutableCopy, locale?._cfObject ?? nil) | |
return mutableCopy._swiftObject | |
} | |
internal func _getBlockStart(startPtr: UnsafeMutablePointer<Int>, end endPtr: UnsafeMutablePointer<Int>, contentsEnd contentsEndPtr: UnsafeMutablePointer<Int>, forRange range: NSRange, stopAtLineSeparators line: Bool) { | |
let len = length | |
var ch: unichar | |
precondition(range.length <= len && range.location < len - range.length, "Range {\(range.location), \(range.length)} is out of bounds of length \(len)") | |
if range.location == 0 && range.length == len && contentsEndPtr == nil { // This occurs often | |
if startPtr != nil { | |
startPtr.memory = 0 | |
} | |
if endPtr != nil { | |
endPtr.memory = range.length | |
} | |
return | |
} | |
/* Find the starting point first */ | |
if startPtr != nil { | |
var start: Int = 0 | |
if range.location == 0 { | |
start = 0 | |
} else { | |
var buf = _NSStringBuffer(string: self, start: range.location, end: len) | |
/* Take care of the special case where start happens to fall right between \r and \n */ | |
ch = buf.currentCharacter | |
buf.rewind() | |
if ch == 0x0a && buf.currentCharacter == 0x0d { | |
buf.rewind() | |
} | |
while true { | |
if line ? isALineSeparatorTypeCharacter(buf.currentCharacter) : isAParagraphSeparatorTypeCharacter(buf.currentCharacter) { | |
start = buf.location + 1 | |
break | |
} else if buf.location <= 0 { | |
start = 0 | |
break | |
} else { | |
buf.rewind() | |
} | |
} | |
startPtr.memory = start | |
} | |
} | |
if (endPtr != nil || contentsEndPtr != nil) { | |
var endOfContents = 1 | |
var lineSeparatorLength = 1 | |
var buf = _NSStringBuffer(string: self, start: NSMaxRange(range) - (range.length > 0 ? 1 : 0), end: len) | |
/* First look at the last char in the range (if the range is zero length, the char after the range) to see if we're already on or within a end of line sequence... */ | |
ch = buf.currentCharacter | |
if ch == 0x0a { | |
endOfContents = buf.location | |
buf.rewind() | |
if buf.currentCharacter == 0x0d { | |
lineSeparatorLength = 2 | |
endOfContents -= 1 | |
} | |
} else { | |
while true { | |
if line ? isALineSeparatorTypeCharacter(ch) : isAParagraphSeparatorTypeCharacter(ch) { | |
endOfContents = buf.location /* This is actually end of contentsRange */ | |
buf.advance() /* OK for this to go past the end */ | |
if ch == 0x0d && buf.currentCharacter == 0x0a { | |
lineSeparatorLength = 2 | |
} | |
break | |
} else if buf.location == len { | |
endOfContents = len | |
lineSeparatorLength = 0 | |
break | |
} else { | |
buf.advance() | |
ch = buf.currentCharacter | |
} | |
} | |
} | |
if contentsEndPtr != nil { | |
contentsEndPtr.memory = endOfContents | |
} | |
if endPtr != nil { | |
endPtr.memory = endOfContents + lineSeparatorLength | |
} | |
} | |
} | |
public func getLineStart(startPtr: UnsafeMutablePointer<Int>, end lineEndPtr: UnsafeMutablePointer<Int>, contentsEnd contentsEndPtr: UnsafeMutablePointer<Int>, forRange range: NSRange) { | |
_getBlockStart(startPtr, end: lineEndPtr, contentsEnd: contentsEndPtr, forRange: range, stopAtLineSeparators: true) | |
} | |
public func lineRangeForRange(range: NSRange) -> NSRange { | |
var start = 0 | |
var lineEnd = 0 | |
getLineStart(&start, end: &lineEnd, contentsEnd: nil, forRange: range) | |
return NSMakeRange(start, lineEnd - start) | |
} | |
public func getParagraphStart(startPtr: UnsafeMutablePointer<Int>, end parEndPtr: UnsafeMutablePointer<Int>, contentsEnd contentsEndPtr: UnsafeMutablePointer<Int>, forRange range: NSRange) { | |
_getBlockStart(startPtr, end: parEndPtr, contentsEnd: contentsEndPtr, forRange: range, stopAtLineSeparators: false) | |
} | |
public func paragraphRangeForRange(range: NSRange) -> NSRange { | |
var start = 0 | |
var parEnd = 0 | |
getParagraphStart(&start, end: &parEnd, contentsEnd: nil, forRange: range) | |
return NSMakeRange(start, parEnd - start) | |
} | |
public func enumerateSubstringsInRange(range: NSRange, options opts: NSStringEnumerationOptions, usingBlock block: (String?, NSRange, NSRange, UnsafeMutablePointer<ObjCBool>) -> Void) { | |
NSUnimplemented() | |
} | |
public func enumerateLinesUsingBlock(block: (String, UnsafeMutablePointer<ObjCBool>) -> Void) { | |
enumerateSubstringsInRange(NSMakeRange(0, length), options:.ByLines) { substr, substrRange, enclosingRange, stop in | |
block(substr!, stop) | |
} | |
} | |
public var UTF8String: UnsafePointer<Int8> { | |
return _bytesInEncoding(self, NSUTF8StringEncoding, false, false, false) | |
} | |
public var fastestEncoding: UInt { | |
return NSUnicodeStringEncoding | |
} | |
public var smallestEncoding: UInt { | |
if canBeConvertedToEncoding(NSASCIIStringEncoding) { | |
return NSASCIIStringEncoding | |
} | |
return NSUnicodeStringEncoding | |
} | |
public func dataUsingEncoding(encoding: UInt, allowLossyConversion lossy: Bool) -> NSData? { | |
let len = length | |
var reqSize = 0 | |
let cfStringEncoding = CFStringConvertNSStringEncodingToEncoding(encoding) | |
if !CFStringIsEncodingAvailable(cfStringEncoding) { | |
return nil | |
} | |
let convertedLen = __CFStringEncodeByteStream(_cfObject, 0, len, true, cfStringEncoding, lossy ? (encoding == NSASCIIStringEncoding ? 0xFF : 0x3F) : 0, nil, 0, &reqSize) | |
if convertedLen != len { | |
return nil // Not able to do it all... | |
} | |
if let data = NSMutableData(length: reqSize) { | |
if 0 < reqSize { | |
if __CFStringEncodeByteStream(_cfObject, 0, len, true, cfStringEncoding, lossy ? (encoding == NSASCIIStringEncoding ? 0xFF : 0x3F) : 0, UnsafeMutablePointer<UInt8>(data.mutableBytes), reqSize, &reqSize) == convertedLen { | |
data.length = reqSize | |
} else { | |
fatalError("didn't convert all characters") | |
} | |
return data | |
} | |
} | |
return nil | |
} | |
public func dataUsingEncoding(encoding: UInt) -> NSData? { | |
return dataUsingEncoding(encoding, allowLossyConversion: false) | |
} | |
public func canBeConvertedToEncoding(encoding: UInt) -> Bool { | |
if encoding == NSUnicodeStringEncoding || encoding == NSNonLossyASCIIStringEncoding || encoding == NSUTF8StringEncoding { | |
return true | |
} | |
return __CFStringEncodeByteStream(_cfObject, 0, length, false, CFStringConvertNSStringEncodingToEncoding(encoding), 0, nil, 0, nil) == length | |
} | |
public func cStringUsingEncoding(encoding: UInt) -> UnsafePointer<Int8> { | |
return _bytesInEncoding(self, encoding, false, false, false) | |
} | |
public func getCString(buffer: UnsafeMutablePointer<Int8>, maxLength maxBufferCount: Int, encoding: UInt) -> Bool { | |
var used = 0 | |
if self.dynamicType == NSString.self || self.dynamicType == NSMutableString.self { | |
if _storage._core.isASCII { | |
used = min(self.length, maxBufferCount - 1) | |
buffer.moveAssignFrom(unsafeBitCast(_storage._core.startASCII, UnsafeMutablePointer<Int8>.self) | |
, count: used) | |
buffer.advancedBy(used).initialize(0) | |
return true | |
} | |
} | |
if getBytes(UnsafeMutablePointer<Void>(buffer), maxLength: maxBufferCount, usedLength: &used, encoding: encoding, options: [], range: NSMakeRange(0, self.length), remainingRange: nil) { | |
buffer.advancedBy(used).initialize(0) | |
return true | |
} | |
return false | |
} | |
public func getBytes(buffer: UnsafeMutablePointer<Void>, maxLength maxBufferCount: Int, usedLength usedBufferCount: UnsafeMutablePointer<Int>, encoding: UInt, options: NSStringEncodingConversionOptions, range: NSRange, remainingRange leftover: NSRangePointer) -> Bool { | |
var totalBytesWritten = 0 | |
var numCharsProcessed = 0 | |
let cfStringEncoding = CFStringConvertNSStringEncodingToEncoding(encoding) | |
var result = true | |
if length > 0 { | |
if CFStringIsEncodingAvailable(cfStringEncoding) { | |
let lossyOk = options.contains(.AllowLossy) | |
let externalRep = options.contains(.ExternalRepresentation) | |
let failOnPartial = options.contains(.FailOnPartialEncodingConversion) | |
numCharsProcessed = __CFStringEncodeByteStream(_cfObject, range.location, range.length, externalRep, cfStringEncoding, lossyOk ? (encoding == NSASCIIStringEncoding ? 0xFF : 0x3F) : 0, UnsafeMutablePointer<UInt8>(buffer), buffer != nil ? maxBufferCount : 0, &totalBytesWritten) | |
if (failOnPartial && numCharsProcessed < range.length) || numCharsProcessed == 0 { | |
result = false | |
} | |
} else { | |
result = false /* ??? Need other encodings */ | |
} | |
} | |
if usedBufferCount != nil { | |
usedBufferCount.memory = totalBytesWritten | |
} | |
if leftover != nil { | |
leftover.memory = NSMakeRange(range.location + numCharsProcessed, range.length - numCharsProcessed) | |
} | |
return result | |
} | |
public func maximumLengthOfBytesUsingEncoding(enc: UInt) -> Int { | |
let cfEnc = CFStringConvertNSStringEncodingToEncoding(enc) | |
let result = CFStringGetMaximumSizeForEncoding(length, cfEnc) | |
return result == kCFNotFound ? 0 : result | |
} | |
public func lengthOfBytesUsingEncoding(enc: UInt) -> Int { | |
let len = length | |
var numBytes: CFIndex = 0 | |
let cfEnc = CFStringConvertNSStringEncodingToEncoding(enc) | |
let convertedLen = __CFStringEncodeByteStream(_cfObject, 0, len, false, cfEnc, 0, nil, 0, &numBytes) | |
return convertedLen != len ? 0 : numBytes | |
} | |
public class func availableStringEncodings() -> UnsafePointer<UInt> { | |
struct once { | |
static let encodings: UnsafePointer<UInt> = { | |
let cfEncodings = CFStringGetListOfAvailableEncodings() | |
var idx = 0 | |
var numEncodings = 0 | |
while cfEncodings.advancedBy(idx).memory != kCFStringEncodingInvalidId { | |
idx += 1 | |
numEncodings += 1 | |
} | |
let theEncodingList = UnsafeMutablePointer<NSStringEncoding>.alloc(numEncodings + 1) | |
theEncodingList.advancedBy(numEncodings).memory = 0 // Terminator | |
numEncodings -= 1 | |
while numEncodings >= 0 { | |
theEncodingList.advancedBy(numEncodings).memory = CFStringConvertEncodingToNSStringEncoding(cfEncodings.advancedBy(numEncodings).memory) | |
numEncodings -= 1 | |
} | |
return UnsafePointer<UInt>(theEncodingList) | |
}() | |
} | |
return once.encodings | |
} | |
public class func localizedNameOfStringEncoding(encoding: UInt) -> String { | |
if let theString = CFStringGetNameOfEncoding(CFStringConvertNSStringEncodingToEncoding(encoding)) { | |
// TODO: read the localized version from the Foundation "bundle" | |
return theString._swiftObject | |
} | |
return "" | |
} | |
public class func defaultCStringEncoding() -> UInt { | |
return CFStringConvertEncodingToNSStringEncoding(CFStringGetSystemEncoding()) | |
} | |
public var decomposedStringWithCanonicalMapping: String { | |
let string = CFStringCreateMutable(kCFAllocatorSystemDefault, 0) | |
CFStringReplaceAll(string, self._cfObject) | |
CFStringNormalize(string, kCFStringNormalizationFormD) | |
return string._swiftObject | |
} | |
public var precomposedStringWithCanonicalMapping: String { | |
let string = CFStringCreateMutable(kCFAllocatorSystemDefault, 0) | |
CFStringReplaceAll(string, self._cfObject) | |
CFStringNormalize(string, kCFStringNormalizationFormC) | |
return string._swiftObject | |
} | |
public var decomposedStringWithCompatibilityMapping: String { | |
let string = CFStringCreateMutable(kCFAllocatorSystemDefault, 0) | |
CFStringReplaceAll(string, self._cfObject) | |
CFStringNormalize(string, kCFStringNormalizationFormKD) | |
return string._swiftObject | |
} | |
public var precomposedStringWithCompatibilityMapping: String { | |
let string = CFStringCreateMutable(kCFAllocatorSystemDefault, 0) | |
CFStringReplaceAll(string, self._cfObject) | |
CFStringNormalize(string, kCFStringNormalizationFormKC) | |
return string._swiftObject | |
} | |
public func componentsSeparatedByString(separator: String) -> [String] { | |
let len = length | |
var range = rangeOfString(separator, options: [], range: NSMakeRange(0, len)) | |
if range.length == 0 { | |
return [_swiftObject] | |
} else { | |
var array = [String]() | |
var srange = NSMakeRange(0, len) | |
while true { | |
let trange = NSMakeRange(srange.location, range.location - srange.location) | |
array.append(substringWithRange(trange)) | |
srange.location = range.location + range.length | |
srange.length = len - srange.location | |
range = rangeOfString(separator, options: [], range: srange) | |
if range.length == 0 { | |
break | |
} | |
} | |
array.append(substringWithRange(srange)) | |
return array | |
} | |
} | |
public func componentsSeparatedByCharactersInSet(separator: NSCharacterSet) -> [String] { | |
let len = length | |
var range = rangeOfCharacterFromSet(separator, options: [], range: NSMakeRange(0, len)) | |
if range.length == 0 { | |
return [_swiftObject] | |
} else { | |
var array = [String]() | |
var srange = NSMakeRange(0, len) | |
while true { | |
let trange = NSMakeRange(srange.location, range.location - srange.location) | |
array.append(substringWithRange(trange)) | |
srange.location = range.location + range.length | |
srange.length = len - srange.location | |
range = rangeOfCharacterFromSet(separator, options: [], range: srange) | |
if range.length == 0 { | |
break | |
} | |
} | |
array.append(substringWithRange(srange)) | |
return array | |
} | |
} | |
public func stringByTrimmingCharactersInSet(set: NSCharacterSet) -> String { | |
let len = length | |
var buf = _NSStringBuffer(string: self, start: 0, end: len) | |
while !buf.isAtEnd && set.characterIsMember(buf.currentCharacter) { | |
buf.advance() | |
} | |
let startOfNonTrimmedRange = buf.location // This points at the first char not in the set | |
if startOfNonTrimmedRange == len { // Note that this also covers the len == 0 case, which is important to do here before the len-1 in the next line. | |
return "" | |
} else if startOfNonTrimmedRange < len - 1 { | |
buf.location = len - 1 | |
while set.characterIsMember(buf.currentCharacter) && buf.location >= startOfNonTrimmedRange { | |
buf.rewind() | |
} | |
let endOfNonTrimmedRange = buf.location | |
return substringWithRange(NSMakeRange(startOfNonTrimmedRange, endOfNonTrimmedRange + 1 - startOfNonTrimmedRange)) | |
} else { | |
return substringWithRange(NSMakeRange(startOfNonTrimmedRange, 1)) | |
} | |
} | |
public func stringByPaddingToLength(newLength: Int, withString padString: String, startingAtIndex padIndex: Int) -> String { | |
let len = length | |
if newLength <= len { // The simple cases (truncation) | |
return newLength == len ? _swiftObject : substringWithRange(NSMakeRange(0, newLength)) | |
} | |
let padLen = padString.length | |
if padLen < 1 { | |
fatalError("empty pad string") | |
} | |
if padIndex >= padLen { | |
fatalError("out of range padIndex") | |
} | |
let mStr = CFStringCreateMutableCopy(kCFAllocatorSystemDefault, 0, _cfObject) | |
CFStringPad(mStr, padString._cfObject, newLength, padIndex) | |
return mStr._swiftObject | |
} | |
public func stringByFoldingWithOptions(options: NSStringCompareOptions, locale: NSLocale?) -> String { | |
let string = CFStringCreateMutable(kCFAllocatorSystemDefault, 0) | |
CFStringReplaceAll(string, self._cfObject) | |
CFStringFold(string, options._cfValue(), locale?._cfObject) | |
return string._swiftObject | |
} | |
internal func _stringByReplacingOccurrencesOfRegularExpressionPattern(pattern: String, withTemplate replacement: String, options: NSStringCompareOptions, range: NSRange) -> String { | |
let regexOptions: NSRegularExpressionOptions = options.contains(.CaseInsensitiveSearch) ? .CaseInsensitive : [] | |
let matchingOptions: NSMatchingOptions = options.contains(.AnchoredSearch) ? .Anchored : [] | |
if let regex = _createRegexForPattern(pattern, regexOptions) { | |
return regex.stringByReplacingMatchesInString(_swiftObject, options: matchingOptions, range: range, withTemplate: replacement) | |
} | |
return "" | |
} | |
public func stringByReplacingOccurrencesOfString(target: String, withString replacement: String, options: NSStringCompareOptions, range searchRange: NSRange) -> String { | |
if options.contains(.RegularExpressionSearch) { | |
return _stringByReplacingOccurrencesOfRegularExpressionPattern(target, withTemplate: replacement, options: options, range: searchRange) | |
} | |
let str = mutableCopyWithZone(nil) as! NSMutableString | |
if str.replaceOccurrencesOfString(target, withString: replacement, options: options, range: searchRange) == 0 { | |
return _swiftObject | |
} else { | |
return str._swiftObject | |
} | |
} | |
public func stringByReplacingOccurrencesOfString(target: String, withString replacement: String) -> String { | |
return stringByReplacingOccurrencesOfString(target, withString: replacement, options: [], range: NSMakeRange(0, length)) | |
} | |
public func stringByReplacingCharactersInRange(range: NSRange, withString replacement: String) -> String { | |
let str = mutableCopyWithZone(nil) as! NSMutableString | |
str.replaceCharactersInRange(range, withString: replacement) | |
return str._swiftObject | |
} | |
public func stringByApplyingTransform(transform: String, reverse: Bool) -> String? { | |
let string = CFStringCreateMutable(kCFAllocatorSystemDefault, 0) | |
CFStringReplaceAll(string, _cfObject) | |
if (CFStringTransform(string, nil, transform._cfObject, reverse)) { | |
return string._swiftObject | |
} else { | |
return nil | |
} | |
} | |
internal func _getExternalRepresentation(inout data: NSData, _ dest: NSURL, _ enc: UInt) throws { | |
let length = self.length | |
var numBytes = 0 | |
let theRange = NSMakeRange(0, length) | |
if !getBytes(nil, maxLength: Int.max - 1, usedLength: &numBytes, encoding: enc, options: [], range: theRange, remainingRange: nil) { | |
throw NSError(domain: NSCocoaErrorDomain, code: NSCocoaError.FileWriteInapplicableStringEncodingError.rawValue, userInfo: [ | |
NSURLErrorKey: dest, | |
]) | |
} | |
let mData = NSMutableData(length: numBytes)! | |
// The getBytes:... call should hopefully not fail, given it succeeded above, but check anyway (mutable string changing behind our back?) | |
var used = 0 | |
if !getBytes(mData.mutableBytes, maxLength: numBytes, usedLength: &used, encoding: enc, options: [], range: theRange, remainingRange: nil) { | |
throw NSError(domain: NSCocoaErrorDomain, code: NSCocoaError.FileWriteUnknownError.rawValue, userInfo: [ | |
NSURLErrorKey: dest, | |
]) | |
} | |
data = mData | |
} | |
internal func _writeTo(url: NSURL, _ useAuxiliaryFile: Bool, _ enc: UInt) throws { | |
var data = NSData() | |
try _getExternalRepresentation(&data, url, enc) | |
if url.fileURL { | |
try data.writeToURL(url, options: useAuxiliaryFile ? .DataWritingAtomic : []) | |
} else { | |
if let path = url.path { | |
try data.writeToFile(path, options: useAuxiliaryFile ? .DataWritingAtomic : []) | |
} else { | |
throw NSError(domain: NSCocoaErrorDomain, code: NSCocoaError.FileNoSuchFileError.rawValue, userInfo: [ | |
NSURLErrorKey: url, | |
]) | |
} | |
} | |
} | |
public func writeToURL(url: NSURL, atomically useAuxiliaryFile: Bool, encoding enc: UInt) throws { | |
try _writeTo(url, useAuxiliaryFile, enc) | |
} | |
public func writeToFile(path: String, atomically useAuxiliaryFile: Bool, encoding enc: UInt) throws { | |
try _writeTo(NSURL(fileURLWithPath: path), useAuxiliaryFile, enc) | |
} | |
public convenience init(charactersNoCopy characters: UnsafeMutablePointer<unichar>, length: Int, freeWhenDone freeBuffer: Bool) /* "NoCopy" is a hint */ { | |
// ignore the no-copy-ness | |
self.init(characters: characters, length: length) | |
if freeBuffer { // cant take a hint here... | |
free(UnsafeMutablePointer<Void>(characters)) | |
} | |
} | |
public convenience init?(UTF8String nullTerminatedCString: UnsafePointer<Int8>) { | |
let buffer = UnsafeBufferPointer<UInt8>(start: UnsafePointer<UInt8>(nullTerminatedCString), count: Int(strlen(nullTerminatedCString))) | |
if let str = String._fromCodeUnitSequence(UTF8.self, input: buffer) { | |
self.init(str) | |
} else { | |
return nil | |
} | |
} | |
public convenience init(format: String, arguments argList: CVaListPointer) { | |
let str = CFStringCreateWithFormatAndArguments(kCFAllocatorSystemDefault, nil, format._cfObject, argList) | |
self.init(str._swiftObject) | |
} | |
public convenience init(format: String, locale: AnyObject?, arguments argList: CVaListPointer) { | |
NSUnimplemented() | |
} | |
public convenience init(format: NSString, _ args: CVarArgType...) { | |
let str = withVaList(args) { (vaPtr) -> CFString! in | |
CFStringCreateWithFormatAndArguments(kCFAllocatorSystemDefault, nil, format._cfObject, vaPtr) | |
} | |
self.init(str._swiftObject) | |
} | |
public convenience init?(data: NSData, encoding: UInt) { | |
self.init(bytes: data.bytes, length: data.length, encoding: encoding) | |
} | |
public convenience init?(bytes: UnsafePointer<Void>, length len: Int, encoding: UInt) { | |
guard let cf = CFStringCreateWithBytes(kCFAllocatorDefault, UnsafePointer<UInt8>(bytes), len, CFStringConvertNSStringEncodingToEncoding(encoding), true) else { | |
return nil | |
} | |
var str: String? | |
if String._conditionallyBridgeFromObject(cf._nsObject, result: &str) { | |
self.init(str!) | |
} else { | |
return nil | |
} | |
} | |
public convenience init?(bytesNoCopy bytes: UnsafeMutablePointer<Void>, length len: Int, encoding: UInt, freeWhenDone freeBuffer: Bool) /* "NoCopy" is a hint */ { | |
// just copy for now since the internal storage will be a copy anyhow | |
self.init(bytes: bytes, length: len, encoding: encoding) | |
if freeBuffer { // dont take the hint | |
free(bytes) | |
} | |
} | |
public convenience init?(CString nullTerminatedCString: UnsafePointer<Int8>, encoding: UInt) { | |
guard let cf = CFStringCreateWithCString(kCFAllocatorSystemDefault, nullTerminatedCString, CFStringConvertNSStringEncodingToEncoding(encoding)) else { | |
return nil | |
} | |
var str: String? | |
if String._conditionallyBridgeFromObject(cf._nsObject, result: &str) { | |
self.init(str!) | |
} else { | |
return nil | |
} | |
} | |
public convenience init(contentsOfURL url: NSURL, encoding enc: UInt) throws { | |
let readResult = try NSData.init(contentsOfURL: url, options: []) | |
guard let cf = CFStringCreateWithBytes(kCFAllocatorDefault, UnsafePointer<UInt8>(readResult.bytes), readResult.length, CFStringConvertNSStringEncodingToEncoding(enc), true) else { | |
throw NSError(domain: NSCocoaErrorDomain, code: NSCocoaError.FileReadInapplicableStringEncodingError.rawValue, userInfo: [ | |
"NSDebugDescription" : "Unable to create a string using the specified encoding." | |
]) | |
} | |
var str: String? | |
if String._conditionallyBridgeFromObject(cf._nsObject, result: &str) { | |
self.init(str!) | |
} else { | |
throw NSError(domain: NSCocoaErrorDomain, code: NSCocoaError.FileReadInapplicableStringEncodingError.rawValue, userInfo: [ | |
"NSDebugDescription" : "Unable to bridge CFString to String." | |
]) | |
} | |
} | |
public convenience init(contentsOfFile path: String, encoding enc: UInt) throws { | |
try self.init(contentsOfURL: NSURL(fileURLWithPath: path), encoding: enc) | |
} | |
public convenience init(contentsOfURL url: NSURL, usedEncoding enc: UnsafeMutablePointer<UInt>) throws { | |
NSUnimplemented() | |
} | |
public convenience init(contentsOfFile path: String, usedEncoding enc: UnsafeMutablePointer<UInt>) throws { | |
NSUnimplemented() | |
} | |
} | |
extension NSString : StringLiteralConvertible { } | |
public class NSMutableString : NSString { | |
public func replaceCharactersInRange(range: NSRange, withString aString: String) { | |
if self.dynamicType === NSString.self || self.dynamicType === NSMutableString.self { | |
// this is incorrectly calculated for grapheme clusters that have a size greater than a single unichar | |
let start = _storage.startIndex | |
let min = start.advancedBy(range.location) | |
let max = start.advancedBy(range.location + range.length) | |
_storage.replaceRange(min..<max, with: aString) | |
} else { | |
NSRequiresConcreteImplementation() | |
} | |
} | |
public required override init(characters: UnsafePointer<unichar>, length: Int) { | |
super.init(characters: characters, length: length) | |
} | |
public required init(capacity: Int) { | |
super.init(characters: nil, length: 0) | |
} | |
public convenience required init?(coder aDecoder: NSCoder) { | |
guard let str = NSString(coder: aDecoder) else { | |
return nil | |
} | |
self.init(string: str.bridge()) | |
} | |
public required convenience init(unicodeScalarLiteral value: StaticString) { | |
self.init(stringLiteral: value) | |
} | |
public required convenience init(extendedGraphemeClusterLiteral value: StaticString) { | |
self.init(stringLiteral: value) | |
} | |
public required init(stringLiteral value: StaticString) { | |
if value.hasPointerRepresentation { | |
super.init(String._fromWellFormedCodeUnitSequence(UTF8.self, input: UnsafeBufferPointer(start: value.utf8Start, count: Int(value.byteSize)))) | |
} else { | |
var uintValue = value.unicodeScalar.value | |
super.init(String._fromWellFormedCodeUnitSequence(UTF32.self, input: UnsafeBufferPointer(start: &uintValue, count: 1))) | |
} | |
} | |
public required init(string aString: String) { | |
super.init(aString) | |
} | |
internal func appendCharacters(characters: UnsafePointer<unichar>, length: Int) { | |
if self.dynamicType == NSMutableString.self { | |
_storage.appendContentsOf(String._fromWellFormedCodeUnitSequence(UTF16.self, input: UnsafeBufferPointer(start: characters, count: length))) | |
} else { | |
replaceCharactersInRange(NSMakeRange(self.length, 0), withString: String._fromWellFormedCodeUnitSequence(UTF16.self, input: UnsafeBufferPointer(start: characters, count: length))) | |
} | |
} | |
internal func _cfAppendCString(characters: UnsafePointer<Int8>, length: Int) { | |
if self.dynamicType == NSMutableString.self { | |
_storage.appendContentsOf(String.fromCString(characters)!) | |
} | |
} | |
} | |
extension NSMutableString { | |
public func insertString(aString: String, atIndex loc: Int) { | |
replaceCharactersInRange(NSMakeRange(loc, 0), withString: aString) | |
} | |
public func deleteCharactersInRange(range: NSRange) { | |
replaceCharactersInRange(range, withString: "") | |
} | |
public func appendString(aString: String) { | |
replaceCharactersInRange(NSMakeRange(length, 0), withString: aString) | |
} | |
public func setString(aString: String) { | |
replaceCharactersInRange(NSMakeRange(0, length), withString: aString) | |
} | |
internal func _replaceOccurrencesOfRegularExpressionPattern(pattern: String, withTemplate replacement: String, options: NSStringCompareOptions, range searchRange: NSRange) -> Int { | |
let regexOptions: NSRegularExpressionOptions = options.contains(.CaseInsensitiveSearch) ? .CaseInsensitive : [] | |
let matchingOptions: NSMatchingOptions = options.contains(.AnchoredSearch) ? .Anchored : [] | |
if let regex = _createRegexForPattern(pattern, regexOptions) { | |
return regex.replaceMatchesInString(self, options: matchingOptions, range: searchRange, withTemplate: replacement) | |
} | |
return 0 | |
} | |
public func replaceOccurrencesOfString(target: String, withString replacement: String, options: NSStringCompareOptions, range searchRange: NSRange) -> Int { | |
let backwards = options.contains(.BackwardsSearch) | |
let len = length | |
precondition(searchRange.length <= len && searchRange.location <= len - searchRange.length, "Search range is out of bounds") | |
if options.contains(.RegularExpressionSearch) { | |
return _replaceOccurrencesOfRegularExpressionPattern(target, withTemplate:replacement, options:options, range: searchRange) | |
} | |
if let findResults = CFStringCreateArrayWithFindResults(kCFAllocatorSystemDefault, _cfObject, target._cfObject, CFRange(searchRange), options._cfValue(true)) { | |
let numOccurrences = CFArrayGetCount(findResults) | |
for cnt in 0..<numOccurrences { | |
let range = UnsafePointer<CFRange>(CFArrayGetValueAtIndex(findResults, backwards ? cnt : numOccurrences - cnt - 1)) | |
replaceCharactersInRange(NSRange(range.memory), withString: replacement) | |
} | |
return numOccurrences | |
} else { | |
return 0 | |
} | |
} | |
public func applyTransform(transform: String, reverse: Bool, range: NSRange, updatedRange resultingRange: NSRangePointer) -> Bool { | |
var cfRange = CFRangeMake(range.location, range.length) | |
return withUnsafeMutablePointer(&cfRange) { (rangep: UnsafeMutablePointer<CFRange>) -> Bool in | |
if CFStringTransform(_cfMutableObject, rangep, transform._cfObject, reverse) { | |
if resultingRange != nil { | |
resultingRange.memory.location = rangep.memory.location | |
resultingRange.memory.length = rangep.memory.length | |
} | |
return true | |
} | |
return false | |
} | |
} | |
} | |
extension String { | |
// this is only valid for the usage for CF since it expects the length to be in unicode characters instead of grapheme clusters "✌🏾".utf16.count = 3 and CFStringGetLength(CFSTR("✌🏾")) = 3 not 1 as it would be represented with grapheme clusters | |
internal var length: Int { | |
return utf16.count | |
} | |
} | |
extension NSString : _CFBridgable, _SwiftBridgable { | |
typealias SwiftType = String | |
internal var _cfObject: CFString { return unsafeBitCast(self, CFString.self) } | |
internal var _swiftObject: String { | |
var str: String? | |
String._forceBridgeFromObject(self, result: &str) | |
return str! | |
} | |
} | |
extension NSMutableString { | |
internal var _cfMutableObject: CFMutableString { return unsafeBitCast(self, CFMutableString.self) } | |
} | |
extension CFString : _NSBridgable, _SwiftBridgable { | |
typealias NSType = NSString | |
typealias SwiftType = String | |
internal var _nsObject: NSType { return unsafeBitCast(self, NSString.self) } | |
internal var _swiftObject: String { return _nsObject._swiftObject } | |
} | |
extension String : _NSBridgable, _CFBridgable { | |
typealias NSType = NSString | |
typealias CFType = CFString | |
internal var _nsObject: NSType { return _bridgeToObject() } | |
internal var _cfObject: CFType { return _nsObject._cfObject } | |
} | |
extension String : Bridgeable { | |
public func bridge() -> NSString { return _nsObject } | |
} | |
extension NSString : Bridgeable { | |
public func bridge() -> String { return _swiftObject } | |
} | |
extension NSString : CustomPlaygroundQuickLookable { | |
public func customPlaygroundQuickLook() -> PlaygroundQuickLook { | |
return .Text(self.bridge()) | |
} | |
} |