blob: 610294c9434b572299a0d5bb13c9946a94a77a69 [file] [log] [blame]
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2016 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 : ExpressibleByUnicodeScalarLiteral {
public typealias UnicodeScalarLiteralType = UnicodeScalar
public init(unicodeScalarLiteral scalar: UnicodeScalar) {
self.init(scalar.value)
}
}
/// Returns a localized string, using the main bundle if one is not specified.
public
func NSLocalizedString(_ key: String,
tableName: String? = nil,
bundle: Bundle = Bundle.main,
value: String = "",
comment: String) -> String {
return bundle.localizedString(forKey: key, value: value, table: tableName)
}
#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
extension NSString {
public struct EncodingConversionOptions : OptionSet {
public let rawValue : UInt
public init(rawValue: UInt) { self.rawValue = rawValue }
public static let allowLossy = EncodingConversionOptions(rawValue: 1)
public static let externalRepresentation = EncodingConversionOptions(rawValue: 2)
internal static let failOnPartialEncodingConversion = EncodingConversionOptions(rawValue: 1 << 20)
}
public struct EnumerationOptions : OptionSet {
public let rawValue : UInt
public init(rawValue: UInt) { self.rawValue = rawValue }
public static let byLines = EnumerationOptions(rawValue: 0)
public static let byParagraphs = EnumerationOptions(rawValue: 1)
public static let byComposedCharacterSequences = EnumerationOptions(rawValue: 2)
public static let byWords = EnumerationOptions(rawValue: 3)
public static let bySentences = EnumerationOptions(rawValue: 4)
public static let reverse = EnumerationOptions(rawValue: 1 << 8)
public static let substringNotRequired = EnumerationOptions(rawValue: 1 << 9)
public static let localized = EnumerationOptions(rawValue: 1 << 10)
internal static let forceFullTokens = EnumerationOptions(rawValue: 1 << 20)
}
}
extension NSString {
public struct CompareOptions : OptionSet {
public let rawValue : UInt
public init(rawValue: UInt) { self.rawValue = rawValue }
public static let caseInsensitive = CompareOptions(rawValue: 1)
public static let literal = CompareOptions(rawValue: 2)
public static let backwards = CompareOptions(rawValue: 4)
public static let anchored = CompareOptions(rawValue: 8)
public static let numeric = CompareOptions(rawValue: 64)
public static let diacriticInsensitive = CompareOptions(rawValue: 128)
public static let widthInsensitive = CompareOptions(rawValue: 256)
public static let forcedOrdering = CompareOptions(rawValue: 512)
public static let regularExpression = CompareOptions(rawValue: 1024)
internal func _cfValue(_ fixLiteral: Bool = false) -> CFStringCompareFlags {
#if os(OSX) || os(iOS)
return contains(.literal) || !fixLiteral ? CFStringCompareFlags(rawValue: rawValue) : CFStringCompareFlags(rawValue: rawValue).union(.compareNonliteral)
#else
return contains(.literal) || !fixLiteral ? CFStringCompareFlags(rawValue) : CFStringCompareFlags(rawValue) | UInt(kCFCompareNonliteral)
#endif
}
}
}
internal func _createRegexForPattern(_ pattern: String, _ options: NSRegularExpression.Options) -> NSRegularExpression? {
struct local {
static let __NSRegularExpressionCache: NSCache<NSString, NSRegularExpression> = {
let cache = NSCache<NSString, NSRegularExpression>()
cache.name = "NSRegularExpressionCache"
cache.countLimit = 10
return cache
}()
}
let key = "\(options):\(pattern)"
if let regex = local.__NSRegularExpressionCache.object(forKey: key._nsObject) {
return regex
}
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: String.Encoding, _ fatalOnError: Bool, _ externalRep: Bool, _ lossy: Bool) -> UnsafePointer<Int8>? {
let theRange = NSRange(location: 0, length: str.length)
var cLength = 0
var used = 0
var options: NSString.EncodingConversionOptions = []
if externalRep {
options.formUnion(.externalRepresentation)
}
if lossy {
options.formUnion(.allowLossy)
}
if !str.getBytes(nil, maxLength: Int.max - 1, usedLength: &cLength, encoding: encoding.rawValue, options: options, range: theRange, remaining: nil) {
if fatalOnError {
fatalError("Conversion on encoding failed")
}
return nil
}
let buffer = malloc(cLength + 1)!.bindMemory(to: Int8.self, capacity: cLength + 1)
if !str.getBytes(buffer, maxLength: cLength, usedLength: &used, encoding: encoding.rawValue, options: options, range: theRange, remaining: nil) {
fatalError("Internal inconsistency; previously claimed getBytes returned success but failed with similar invocation")
}
buffer.advanced(by: cLength).initialize(to: 0)
return UnsafePointer(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
}
open class NSString : NSObject, NSCopying, NSMutableCopying, NSSecureCoding, NSCoding {
private let _cfinfo = _CFInfo(typeID: CFStringGetTypeID())
internal var _storage: String
open var length: Int {
guard type(of: self) === NSString.self || type(of: self) === NSMutableString.self else {
NSRequiresConcreteImplementation()
}
return _storage.utf16.count
}
open func character(at index: Int) -> unichar {
guard type(of: self) === NSString.self || type(of: self) === NSMutableString.self else {
NSRequiresConcreteImplementation()
}
let start = _storage.utf16.startIndex
return _storage.utf16[_storage.utf16.index(start, offsetBy: index)]
}
public override convenience init() {
let characters = Array<unichar>(repeating: 0, count: 1)
self.init(characters: characters, length: 0)
}
internal init(_ string: String) {
_storage = string
}
public convenience required init?(coder aDecoder: NSCoder) {
guard aDecoder.allowsKeyedCoding else {
preconditionFailure("Unkeyed coding is unsupported.")
}
if type(of: aDecoder) == NSKeyedUnarchiver.self || aDecoder.containsValue(forKey: "NS.string") {
let str = aDecoder._decodePropertyListForKey("NS.string") as! String
self.init(string: str)
} else {
let decodedData : Data? = aDecoder.withDecodedUnsafeBufferPointer(forKey: "NS.bytes") {
guard let buffer = $0 else { return nil }
return Data(buffer: buffer)
}
guard let data = decodedData else { return nil }
self.init(data: data, encoding: String.Encoding.utf8.rawValue)
}
}
public required convenience init(string aString: String) {
self.init(aString)
}
open override func copy() -> Any {
return copy(with: nil)
}
open func copy(with zone: NSZone? = nil) -> Any {
if type(of: self) === NSString.self {
return self
}
let characters = UnsafeMutablePointer<unichar>.allocate(capacity: length)
getCharacters(characters, range: NSRange(location: 0, length: length))
let result = NSString(characters: characters, length: length)
characters.deinitialize(count: length)
characters.deallocate()
return result
}
open override func mutableCopy() -> Any {
return mutableCopy(with: nil)
}
open func mutableCopy(with zone: NSZone? = nil) -> Any {
if type(of: self) === NSString.self || type(of: self) === NSMutableString.self {
if let contents = _fastContents {
return NSMutableString(characters: contents, length: length)
}
}
let characters = UnsafeMutablePointer<unichar>.allocate(capacity: length)
getCharacters(characters, range: NSRange(location: 0, length: length))
let result = NSMutableString(characters: characters, length: length)
characters.deinitialize(count: 1)
characters.deallocate()
return result
}
public static var supportsSecureCoding: Bool {
return true
}
open func encode(with aCoder: NSCoder) {
if let aKeyedCoder = aCoder as? NSKeyedArchiver {
aKeyedCoder._encodePropertyList(self, forKey: "NS.string")
} else {
aCoder.encode(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 = String(describing: value)
}
public convenience init?(cString nullTerminatedCString: UnsafePointer<Int8>, encoding: UInt) {
guard let str = CFStringCreateWithCString(kCFAllocatorSystemDefault, nullTerminatedCString, CFStringConvertNSStringEncodingToEncoding(encoding)) else {
return nil
}
self.init(string: str._swiftObject)
}
internal func _fastCStringContents(_ nullTerminated: Bool) -> UnsafePointer<Int8>? {
if type(of: self) == NSString.self || type(of: self) == NSMutableString.self {
if _storage._core.isASCII {
return unsafeBitCast(_storage._core.startASCII, to: UnsafePointer<Int8>.self)
}
}
return nil
}
internal var _fastContents: UnsafePointer<UniChar>? {
if type(of: self) == NSString.self || type(of: self) == NSMutableString.self {
if !_storage._core.isASCII {
return UnsafePointer<UniChar>(_storage._core.startUTF16)
}
}
return nil
}
internal var _encodingCantBeStoredInEightBitCFString: Bool {
if type(of: self) == NSString.self || type(of: self) == NSMutableString.self {
return !_storage._core.isASCII
}
return false
}
override open var _cfTypeID: CFTypeID {
return CFStringGetTypeID()
}
open override func isEqual(_ object: Any?) -> Bool {
guard let string = (object as? NSString)?._swiftObject else { return false }
return self.isEqual(to: string)
}
open override var description: String {
return _swiftObject
}
open 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] = character(at: idx + range.location)
}
}
public func substring(from: Int) -> String {
if type(of: self) == NSString.self || type(of: self) == NSMutableString.self {
return String(_storage.utf16.suffix(from: _storage.utf16.index(_storage.utf16.startIndex, offsetBy: from)))!
} else {
return substring(with: NSRange(location: from, length: length - from))
}
}
public func substring(to: Int) -> String {
if type(of: self) == NSString.self || type(of: self) == NSMutableString.self {
return String(_storage.utf16.prefix(upTo: _storage.utf16.index(_storage.utf16.startIndex, offsetBy: to)))!
} else {
return substring(with: NSRange(location: 0, length: to))
}
}
public func substring(with range: NSRange) -> String {
if type(of: self) == NSString.self || type(of: self) == NSMutableString.self {
let start = _storage.utf16.startIndex
let min = _storage.utf16.index(start, offsetBy: range.location)
let max = _storage.utf16.index(start, offsetBy: range.location + range.length)
return String(decoding: _storage.utf16[min..<max], as: UTF16.self)
} else {
let buff = UnsafeMutablePointer<unichar>.allocate(capacity: range.length)
getCharacters(buff, range: range)
let result = String(describing: buff)
buff.deinitialize(count: 1)
buff.deallocate()
return result
}
}
public func compare(_ string: String) -> ComparisonResult {
return compare(string, options: [], range: NSRange(location: 0, length: length))
}
public func compare(_ string: String, options mask: CompareOptions) -> ComparisonResult {
return compare(string, options: mask, range: NSRange(location: 0, length: length))
}
public func compare(_ string: String, options mask: CompareOptions, range compareRange: NSRange) -> ComparisonResult {
return compare(string, options: mask, range: compareRange, locale: nil)
}
public func compare(_ string: String, options mask: CompareOptions, range compareRange: NSRange, locale: Any?) -> ComparisonResult {
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 ComparisonResult._fromCF(res)
}
public func caseInsensitiveCompare(_ string: String) -> ComparisonResult {
return compare(string, options: .caseInsensitive, range: NSRange(location: 0, length: length))
}
public func localizedCompare(_ string: String) -> ComparisonResult {
return compare(string, options: [], range: NSRange(location: 0, length: length), locale: Locale.current._bridgeToObjectiveC())
}
public func localizedCaseInsensitiveCompare(_ string: String) -> ComparisonResult {
return compare(string, options: .caseInsensitive, range: NSRange(location: 0, length: length), locale: Locale.current._bridgeToObjectiveC())
}
public func localizedStandardCompare(_ string: String) -> ComparisonResult {
return compare(string, options: [.caseInsensitive, .numeric, .widthInsensitive, .forcedOrdering], range: NSRange(location: 0, length: length), locale: Locale.current._bridgeToObjectiveC())
}
public func isEqual(to aString: String) -> Bool {
if type(of: self) == NSString.self || type(of: self) == NSMutableString.self {
return _storage == aString
} else {
return length == aString.length && compare(aString, options: .literal, range: NSRange(location: 0, length: length)) == .orderedSame
}
}
public func hasPrefix(_ str: String) -> Bool {
return range(of: str, options: .anchored, range: NSRange(location: 0, length: length)).location != NSNotFound
}
public func hasSuffix(_ str: String) -> Bool {
return range(of: str, options: [.anchored, .backwards], range: NSRange(location: 0, length: length)).location != NSNotFound
}
public func commonPrefix(with str: String, options mask: CompareOptions = []) -> String {
var currentSubstring: CFMutableString?
let isLiteral = mask.contains(.literal)
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](repeating: 0, count: 100)
let other = str._nsObject
return arrayBuffer.withUnsafeMutablePointerOrAllocation(selfLen, fastpath: UnsafeMutablePointer<unichar>(mutating: _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 = NSRange(location: 0, length: isLiteral ? probe + 1 : NSMaxRange(rangeOfComposedCharacterSequence(at: probe))) // Extend the end of the composed char sequence
if range.length > numCharsBuffered { // Buffer more characters if needed
getCharacters(selfChars, range: NSRange(location: numCharsBuffered, length: 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.range(of: currentSubstring!._swiftObject, options: mask.union(.anchored), range: NSRange(location: 0, length: otherLen)).length != 0 { // Match
lastMatch = range
low = probe + 1
} else {
high = probe
}
if low >= high {
break
}
probe = (low + high) / 2
}
return lastMatch.length != 0 ? substring(with: lastMatch) : ""
}
}
public func contains(_ str: String) -> Bool {
return range(of: str, options: [], range: NSRange(location: 0, length: length), locale: nil).location != NSNotFound
}
public func localizedCaseInsensitiveContains(_ str: String) -> Bool {
return range(of: str, options: .caseInsensitive, range: NSRange(location: 0, length: length), locale: Locale.current).location != NSNotFound
}
public func localizedStandardContains(_ str: String) -> Bool {
return range(of: str, options: [.caseInsensitive, .diacriticInsensitive], range: NSRange(location: 0, length: length), locale: Locale.current).location != NSNotFound
}
public func localizedStandardRange(of str: String) -> NSRange {
return range(of: str, options: [.caseInsensitive, .diacriticInsensitive], range: NSRange(location: 0, length: length), locale: Locale.current)
}
public func range(of searchString: String) -> NSRange {
return range(of: searchString, options: [], range: NSRange(location: 0, length: length), locale: nil)
}
public func range(of searchString: String, options mask: CompareOptions = []) -> NSRange {
return range(of: searchString, options: mask, range: NSRange(location: 0, length: length), locale: nil)
}
public func range(of searchString: String, options mask: CompareOptions = [], range searchRange: NSRange) -> NSRange {
return range(of: searchString, options: mask, range: searchRange, locale: nil)
}
internal func _rangeOfRegularExpressionPattern(regex pattern: String, options mask: CompareOptions, range searchRange: NSRange, locale: Locale?) -> NSRange {
var matchedRange = NSRange(location: NSNotFound, length: 0)
let regexOptions: NSRegularExpression.Options = mask.contains(.caseInsensitive) ? .caseInsensitive : []
let matchingOptions: NSRegularExpression.MatchingOptions = mask.contains(.anchored) ? .anchored : []
if let regex = _createRegexForPattern(pattern, regexOptions) {
matchedRange = regex.rangeOfFirstMatch(in: _swiftObject, options: matchingOptions, range: searchRange)
}
return matchedRange
}
public func range(of searchString: String, options mask: CompareOptions = [], range searchRange: NSRange, locale: Locale?) -> 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(.regularExpression) {
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 NSRange(location: NSNotFound, length: 0)
}
var result = CFRange()
let res = withUnsafeMutablePointer(to: &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 NSRange(location: result.location, length: result.length)
} else {
return NSRange(location: NSNotFound, length: 0)
}
}
public func rangeOfCharacter(from searchSet: CharacterSet) -> NSRange {
return rangeOfCharacter(from: searchSet, options: [], range: NSRange(location: 0, length: length))
}
public func rangeOfCharacter(from searchSet: CharacterSet, options mask: CompareOptions = []) -> NSRange {
return rangeOfCharacter(from: searchSet, options: mask, range: NSRange(location: 0, length: length))
}
public func rangeOfCharacter(from searchSet: CharacterSet, options mask: CompareOptions = [], 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(to: &result) { (rangep: UnsafeMutablePointer<CFRange>) -> Bool in
return CFStringFindCharacterFromSet(_cfObject, searchSet._cfObject, CFRange(searchRange), mask._cfValue(), rangep)
}
if res {
return NSRange(location: result.location, length: result.length)
} else {
return NSRange(location: NSNotFound, length: 0)
}
}
public func rangeOfComposedCharacterSequence(at index: Int) -> NSRange {
let range = CFStringGetRangeOfCharacterClusterAtIndex(_cfObject, index, kCFStringComposedCharacterCluster)
return NSRange(location: range.location, length: range.length)
}
public func rangeOfComposedCharacterSequences(for range: NSRange) -> NSRange {
let length = self.length
var start: Int
var end: Int
if range.location == length {
start = length
} else {
start = rangeOfComposedCharacterSequence(at: 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(rangeOfComposedCharacterSequence(at: endOfRange))
}
return NSRange(location: start, length: end - start)
}
public func appending(_ aString: String) -> String {
return _swiftObject + aString
}
public var doubleValue: Double {
var start: Int = 0
var result = 0.0
let _ = _swiftObject.scan(CharacterSet.whitespaces, 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
let _ = _swiftObject.scan(CharacterSet.whitespaces, locale: nil, locationToScanFrom: &start) { (value: Float) -> Void in
result = value
}
return result
}
public var intValue: Int32 {
return Scanner(string: _swiftObject).scanInt32() ?? 0
}
public var integerValue: Int {
let scanner = Scanner(string: _swiftObject)
var value: Int = 0
let _ = scanner.scanInt(&value)
return value
}
public var longLongValue: Int64 {
return Scanner(string: _swiftObject).scanInt64() ?? 0
}
public var boolValue: Bool {
let scanner = Scanner(string: _swiftObject)
// skip initial whitespace if present
let _ = scanner.scanCharactersFromSet(.whitespaces)
// scan a single optional '+' or '-' character, followed by zeroes
if scanner.scanString("+") == nil {
let _ = scanner.scanString("-")
}
// scan any following zeroes
let _ = scanner.scanCharactersFromSet(CharacterSet(charactersIn: "0"))
return scanner.scanCharactersFromSet(CharacterSet(charactersIn: "tTyY123456789")) != nil
}
public var uppercased: String {
return uppercased(with: nil)
}
public var lowercased: String {
return lowercased(with: nil)
}
public var capitalized: String {
return capitalized(with: nil)
}
public var localizedUppercase: String {
return uppercased(with: Locale.current)
}
public var localizedLowercase: String {
return lowercased(with: Locale.current)
}
public var localizedCapitalized: String {
return capitalized(with: Locale.current)
}
public func uppercased(with locale: Locale?) -> String {
let mutableCopy = CFStringCreateMutableCopy(kCFAllocatorSystemDefault, 0, self._cfObject)!
CFStringUppercase(mutableCopy, locale?._cfObject ?? nil)
return mutableCopy._swiftObject
}
public func lowercased(with locale: Locale?) -> String {
let mutableCopy = CFStringCreateMutableCopy(kCFAllocatorSystemDefault, 0, self._cfObject)!
CFStringLowercase(mutableCopy, locale?._cfObject ?? nil)
return mutableCopy._swiftObject
}
public func capitalized(with locale: Locale?) -> 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
startPtr?.pointee = 0
endPtr?.pointee = range.length
return
}
/* Find the starting point first */
if let startPtr = startPtr {
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.pointee = 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
}
}
}
contentsEndPtr?.pointee = endOfContents
endPtr?.pointee = endOfContents + lineSeparatorLength
}
}
public func getLineStart(_ startPtr: UnsafeMutablePointer<Int>?, end lineEndPtr: UnsafeMutablePointer<Int>?, contentsEnd contentsEndPtr: UnsafeMutablePointer<Int>?, for range: NSRange) {
_getBlockStart(startPtr, end: lineEndPtr, contentsEnd: contentsEndPtr, forRange: range, stopAtLineSeparators: true)
}
public func lineRange(for range: NSRange) -> NSRange {
var start = 0
var lineEnd = 0
getLineStart(&start, end: &lineEnd, contentsEnd: nil, for: range)
return NSRange(location: start, length: lineEnd - start)
}
public func getParagraphStart(_ startPtr: UnsafeMutablePointer<Int>?, end parEndPtr: UnsafeMutablePointer<Int>?, contentsEnd contentsEndPtr: UnsafeMutablePointer<Int>?, for range: NSRange) {
_getBlockStart(startPtr, end: parEndPtr, contentsEnd: contentsEndPtr, forRange: range, stopAtLineSeparators: false)
}
public func paragraphRange(for range: NSRange) -> NSRange {
var start = 0
var parEnd = 0
getParagraphStart(&start, end: &parEnd, contentsEnd: nil, for: range)
return NSRange(location: start, length: parEnd - start)
}
public func enumerateSubstrings(in range: NSRange, options opts: EnumerationOptions = [], using block: (String?, NSRange, NSRange, UnsafeMutablePointer<ObjCBool>) -> Void) {
NSUnimplemented()
}
public func enumerateLines(_ block: (String, UnsafeMutablePointer<ObjCBool>) -> Void) {
enumerateSubstrings(in: NSRange(location: 0, length: length), options:.byLines) { substr, substrRange, enclosingRange, stop in
block(substr!, stop)
}
}
public var utf8String: UnsafePointer<Int8>? {
return _bytesInEncoding(self, String.Encoding.utf8, false, false, false)
}
public var fastestEncoding: UInt {
return String.Encoding.unicode.rawValue
}
public var smallestEncoding: UInt {
if canBeConverted(to: String.Encoding.ascii.rawValue) {
return String.Encoding.ascii.rawValue
}
return String.Encoding.unicode.rawValue
}
public func data(using encoding: UInt, allowLossyConversion lossy: Bool = false) -> Data? {
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 == String.Encoding.ascii.rawValue ? 0xFF : 0x3F) : 0, nil, 0, &reqSize)
if convertedLen != len {
return nil // Not able to do it all...
}
if 0 < reqSize {
var data = Data(count: reqSize)
data.count = data.withUnsafeMutableBytes { (mutableBytes: UnsafeMutablePointer<UInt8>) -> Int in
if __CFStringEncodeByteStream(_cfObject, 0, len, true, cfStringEncoding, lossy ? (encoding == String.Encoding.ascii.rawValue ? 0xFF : 0x3F) : 0, UnsafeMutablePointer<UInt8>(mutableBytes), reqSize, &reqSize) == convertedLen {
return reqSize
} else {
fatalError("didn't convert all characters")
}
}
return data
}
return Data()
}
public func data(using encoding: UInt) -> Data? {
return data(using: encoding, allowLossyConversion: false)
}
public func canBeConverted(to encoding: UInt) -> Bool {
if encoding == String.Encoding.unicode.rawValue || encoding == String.Encoding.nonLossyASCII.rawValue || encoding == String.Encoding.utf8.rawValue {
return true
}
return __CFStringEncodeByteStream(_cfObject, 0, length, false, CFStringConvertNSStringEncodingToEncoding(encoding), 0, nil, 0, nil) == length
}
public func cString(using encoding: UInt) -> UnsafePointer<Int8>? {
return _bytesInEncoding(self, String.Encoding(rawValue: encoding), false, false, false)
}
public func getCString(_ buffer: UnsafeMutablePointer<Int8>, maxLength maxBufferCount: Int, encoding: UInt) -> Bool {
var used = 0
if type(of: self) == NSString.self || type(of: self) == NSMutableString.self {
if _storage._core.isASCII {
used = min(self.length, maxBufferCount - 1)
_storage._core.startASCII.withMemoryRebound(to: Int8.self,
capacity: used) {
buffer.moveAssign(from: $0, count: used)
}
buffer.advanced(by: used).initialize(to: 0)
return true
}
}
if getBytes(UnsafeMutableRawPointer(buffer), maxLength: maxBufferCount, usedLength: &used, encoding: encoding, options: [], range: NSRange(location: 0, length: self.length), remaining: nil) {
buffer.advanced(by: used).initialize(to: 0)
return true
}
return false
}
public func getBytes(_ buffer: UnsafeMutableRawPointer?, maxLength maxBufferCount: Int, usedLength usedBufferCount: UnsafeMutablePointer<Int>?, encoding: UInt, options: EncodingConversionOptions = [], range: NSRange, remaining 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)
let bytePtr = buffer?.bindMemory(to: UInt8.self, capacity: maxBufferCount)
numCharsProcessed = __CFStringEncodeByteStream(_cfObject, range.location, range.length, externalRep, cfStringEncoding, lossyOk ? (encoding == String.Encoding.ascii.rawValue ? 0xFF : 0x3F) : 0, bytePtr, bytePtr != nil ? maxBufferCount : 0, &totalBytesWritten)
if (failOnPartial && numCharsProcessed < range.length) || numCharsProcessed == 0 {
result = false
}
} else {
result = false /* ??? Need other encodings */
}
}
usedBufferCount?.pointee = totalBytesWritten
leftover?.pointee = NSRange(location: range.location + numCharsProcessed, length: range.length - numCharsProcessed)
return result
}
public func maximumLengthOfBytes(using enc: UInt) -> Int {
let cfEnc = CFStringConvertNSStringEncodingToEncoding(enc)
let result = CFStringGetMaximumSizeForEncoding(length, cfEnc)
return result == kCFNotFound ? 0 : result
}
public func lengthOfBytes(using 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
}
open class var availableStringEncodings: UnsafePointer<UInt> {
struct once {
static let encodings: UnsafePointer<UInt> = {
let cfEncodings = CFStringGetListOfAvailableEncodings()!
var idx = 0
var numEncodings = 0
while cfEncodings.advanced(by: idx).pointee != kCFStringEncodingInvalidId {
idx += 1
numEncodings += 1
}
let theEncodingList = UnsafeMutablePointer<String.Encoding.RawValue>.allocate(capacity: numEncodings + 1)
theEncodingList.advanced(by: numEncodings).pointee = 0 // Terminator
numEncodings -= 1
while numEncodings >= 0 {
theEncodingList.advanced(by: numEncodings).pointee = CFStringConvertEncodingToNSStringEncoding(cfEncodings.advanced(by: numEncodings).pointee)
numEncodings -= 1
}
return UnsafePointer<UInt>(theEncodingList)
}()
}
return once.encodings
}
open class func localizedName(of encoding: UInt) -> String {
if let theString = CFStringGetNameOfEncoding(CFStringConvertNSStringEncodingToEncoding(encoding)) {
// TODO: read the localized version from the Foundation "bundle"
return theString._swiftObject
}
return ""
}
open class var defaultCStringEncoding: UInt {
return CFStringConvertEncodingToNSStringEncoding(CFStringGetSystemEncoding())
}
open var decomposedStringWithCanonicalMapping: String {
let string = CFStringCreateMutable(kCFAllocatorSystemDefault, 0)!
CFStringReplaceAll(string, self._cfObject)
CFStringNormalize(string, kCFStringNormalizationFormD)
return string._swiftObject
}
open var precomposedStringWithCanonicalMapping: String {
let string = CFStringCreateMutable(kCFAllocatorSystemDefault, 0)!
CFStringReplaceAll(string, self._cfObject)
CFStringNormalize(string, kCFStringNormalizationFormC)
return string._swiftObject
}
open var decomposedStringWithCompatibilityMapping: String {
let string = CFStringCreateMutable(kCFAllocatorSystemDefault, 0)!
CFStringReplaceAll(string, self._cfObject)
CFStringNormalize(string, kCFStringNormalizationFormKD)
return string._swiftObject
}
open var precomposedStringWithCompatibilityMapping: String {
let string = CFStringCreateMutable(kCFAllocatorSystemDefault, 0)!
CFStringReplaceAll(string, self._cfObject)
CFStringNormalize(string, kCFStringNormalizationFormKC)
return string._swiftObject
}
open func components(separatedBy separator: String) -> [String] {
let len = length
var lrange = range(of: separator, options: [], range: NSRange(location: 0, length: len))
if lrange.length == 0 {
return [_swiftObject]
} else {
var array = [String]()
var srange = NSRange(location: 0, length: len)
while true {
let trange = NSRange(location: srange.location, length: lrange.location - srange.location)
array.append(substring(with: trange))
srange.location = lrange.location + lrange.length
srange.length = len - srange.location
lrange = range(of: separator, options: [], range: srange)
if lrange.length == 0 {
break
}
}
array.append(substring(with: srange))
return array
}
}
open func components(separatedBy separator: CharacterSet) -> [String] {
let len = length
var range = rangeOfCharacter(from: separator, options: [], range: NSRange(location: 0, length: len))
if range.length == 0 {
return [_swiftObject]
} else {
var array = [String]()
var srange = NSRange(location: 0, length: len)
while true {
let trange = NSRange(location: srange.location, length: range.location - srange.location)
array.append(substring(with: trange))
srange.location = range.location + range.length
srange.length = len - srange.location
range = rangeOfCharacter(from: separator, options: [], range: srange)
if range.length == 0 {
break
}
}
array.append(substring(with: srange))
return array
}
}
open func trimmingCharacters(in set: CharacterSet) -> String {
let len = length
var buf = _NSStringBuffer(string: self, start: 0, end: len)
while !buf.isAtEnd,
let character = UnicodeScalar(buf.currentCharacter),
set.contains(character) {
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 let character = UnicodeScalar(buf.currentCharacter),
set.contains(character),
buf.location >= startOfNonTrimmedRange {
buf.rewind()
}
let endOfNonTrimmedRange = buf.location
return substring(with: NSRange(location: startOfNonTrimmedRange, length: endOfNonTrimmedRange + 1 - startOfNonTrimmedRange))
} else {
return substring(with: NSRange(location: startOfNonTrimmedRange, length: 1))
}
}
open func padding(toLength newLength: Int, withPad padString: String, startingAt padIndex: Int) -> String {
let len = length
if newLength <= len { // The simple cases (truncation)
return newLength == len ? _swiftObject : substring(with: NSRange(location: 0, length: 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
}
open func folding(options: CompareOptions = [], locale: Locale?) -> 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: CompareOptions, range: NSRange) -> String {
let regexOptions: NSRegularExpression.Options = options.contains(.caseInsensitive) ? .caseInsensitive : []
let matchingOptions: NSRegularExpression.MatchingOptions = options.contains(.anchored) ? .anchored : []
if let regex = _createRegexForPattern(pattern, regexOptions) {
return regex.stringByReplacingMatches(in: _swiftObject, options: matchingOptions, range: range, withTemplate: replacement)
}
return ""
}
open func replacingOccurrences(of target: String, with replacement: String, options: CompareOptions = [], range searchRange: NSRange) -> String {
if options.contains(.regularExpression) {
return _stringByReplacingOccurrencesOfRegularExpressionPattern(target, withTemplate: replacement, options: options, range: searchRange)
}
let str = mutableCopy(with: nil) as! NSMutableString
if str.replaceOccurrences(of: target, with: replacement, options: options, range: searchRange) == 0 {
return _swiftObject
} else {
return str._swiftObject
}
}
open func replacingOccurrences(of target: String, with replacement: String) -> String {
return replacingOccurrences(of: target, with: replacement, options: [], range: NSRange(location: 0, length: length))
}
open func replacingCharacters(in range: NSRange, with replacement: String) -> String {
let str = mutableCopy(with: nil) as! NSMutableString
str.replaceCharacters(in: range, with: replacement)
return str._swiftObject
}
open func applyingTransform(_ 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(_ data: inout Data, _ dest: URL, _ enc: UInt) throws {
let length = self.length
var numBytes = 0
let theRange = NSRange(location: 0, length: length)
if !getBytes(nil, maxLength: Int.max - 1, usedLength: &numBytes, encoding: enc, options: [], range: theRange, remaining: nil) {
throw NSError(domain: NSCocoaErrorDomain, code: CocoaError.fileWriteInapplicableStringEncoding.rawValue, userInfo: [
NSURLErrorKey: dest,
])
}
var mData = Data(count: numBytes)
// The getBytes:... call should hopefully not fail, given it succeeded above, but check anyway (mutable string changing behind our back?)
var used = 0
// This binds mData memory to UInt8 because Data.withUnsafeMutableBytes does not handle raw pointers.
try mData.withUnsafeMutableBytes { (mutableBytes: UnsafeMutablePointer<UInt8>) -> Void in
if !getBytes(mutableBytes, maxLength: numBytes, usedLength: &used, encoding: enc, options: [], range: theRange, remaining: nil) {
throw NSError(domain: NSCocoaErrorDomain, code: CocoaError.fileWriteUnknown.rawValue, userInfo: [
NSURLErrorKey: dest,
])
}
}
data = mData
}
internal func _writeTo(_ url: URL, _ useAuxiliaryFile: Bool, _ enc: UInt) throws {
var data = Data()
try _getExternalRepresentation(&data, url, enc)
try data.write(to: url, options: useAuxiliaryFile ? .atomic : [])
}
open func write(to url: URL, atomically useAuxiliaryFile: Bool, encoding enc: UInt) throws {
try _writeTo(url, useAuxiliaryFile, enc)
}
open func write(toFile path: String, atomically useAuxiliaryFile: Bool, encoding enc: UInt) throws {
try _writeTo(URL(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(UnsafeMutableRawPointer(characters))
}
}
public convenience init?(utf8String nullTerminatedCString: UnsafePointer<Int8>) {
let count = Int(strlen(nullTerminatedCString))
if let str = nullTerminatedCString.withMemoryRebound(to: UInt8.self, capacity: count, {
let buffer = UnsafeBufferPointer<UInt8>(start: $0, count: count)
return String._fromCodeUnitSequence(UTF8.self, input: buffer)
}) as String?
{
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) {
let str: CFString
if let loc = locale {
if type(of: loc) === NSLocale.self || type(of: loc) === NSDictionary.self {
str = CFStringCreateWithFormatAndArguments(kCFAllocatorSystemDefault, unsafeBitCast(loc, to: CFDictionary.self), format._cfObject, argList)
} else {
fatalError("locale parameter must be a NSLocale or a NSDictionary")
}
} else {
str = CFStringCreateWithFormatAndArguments(kCFAllocatorSystemDefault, nil, format._cfObject, argList)
}
self.init(str._swiftObject)
}
public convenience init(format: NSString, _ args: CVarArg...) {
let str = withVaList(args) { (vaPtr) -> CFString? in
CFStringCreateWithFormatAndArguments(kCFAllocatorSystemDefault, nil, format._cfObject, vaPtr)
}!
self.init(str._swiftObject)
}
public convenience init?(data: Data, encoding: UInt) {
if data.isEmpty {
self.init("")
} else {
guard let cf = data.withUnsafeBytes({ (bytes: UnsafePointer<UInt8>) -> CFString? in
return CFStringCreateWithBytes(kCFAllocatorDefault, bytes, data.count, CFStringConvertNSStringEncodingToEncoding(encoding), true)
}) else { return nil }
var str: String?
if String._conditionallyBridgeFromObjectiveC(cf._nsObject, result: &str) {
self.init(str!)
} else {
return nil
}
}
}
public convenience init?(bytes: UnsafeRawPointer, length len: Int, encoding: UInt) {
let bytePtr = bytes.bindMemory(to: UInt8.self, capacity: len)
guard let cf = CFStringCreateWithBytes(kCFAllocatorDefault, bytePtr, len, CFStringConvertNSStringEncodingToEncoding(encoding), true) else {
return nil
}
var str: String?
if String._conditionallyBridgeFromObjectiveC(cf._nsObject, result: &str) {
self.init(str!)
} else {
return nil
}
}
public convenience init?(bytesNoCopy bytes: UnsafeMutableRawPointer, 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(contentsOf url: URL, encoding enc: UInt) throws {
let readResult = try NSData(contentsOf: url, options: [])
let bytePtr = readResult.bytes.bindMemory(to: UInt8.self, capacity: readResult.length)
guard let cf = CFStringCreateWithBytes(kCFAllocatorDefault, bytePtr, readResult.length, CFStringConvertNSStringEncodingToEncoding(enc), true) else {
throw NSError(domain: NSCocoaErrorDomain, code: CocoaError.fileReadInapplicableStringEncoding.rawValue, userInfo: [
"NSDebugDescription" : "Unable to create a string using the specified encoding."
])
}
var str: String?
if String._conditionallyBridgeFromObjectiveC(cf._nsObject, result: &str) {
self.init(str!)
} else {
throw NSError(domain: NSCocoaErrorDomain, code: CocoaError.fileReadInapplicableStringEncoding.rawValue, userInfo: [
"NSDebugDescription" : "Unable to bridge CFString to String."
])
}
}
public convenience init(contentsOfFile path: String, encoding enc: UInt) throws {
try self.init(contentsOf: URL(fileURLWithPath: path), encoding: enc)
}
public convenience init(contentsOf url: URL, usedEncoding enc: UnsafeMutablePointer<UInt>?) throws {
let readResult = try NSData(contentsOf: url, options:[])
let encoding: UInt
let offset: Int
let bytePtr = readResult.bytes.bindMemory(to: UInt8.self, capacity:readResult.length)
if readResult.length >= 4 && bytePtr[0] == 0xFF && bytePtr[1] == 0xFE && bytePtr[2] == 0x00 && bytePtr[3] == 0x00 {
encoding = String.Encoding.utf32LittleEndian.rawValue
offset = 4
}
else if readResult.length >= 2 && bytePtr[0] == 0xFE && bytePtr[1] == 0xFF {
encoding = String.Encoding.utf16BigEndian.rawValue
offset = 2
}
else if readResult.length >= 2 && bytePtr[0] == 0xFF && bytePtr[1] == 0xFE {
encoding = String.Encoding.utf16LittleEndian.rawValue
offset = 2
}
else if readResult.length >= 4 && bytePtr[0] == 0x00 && bytePtr[1] == 0x00 && bytePtr[2] == 0xFE && bytePtr[3] == 0xFF {
encoding = String.Encoding.utf32BigEndian.rawValue
offset = 4
}
else {
//Need to work on more conditions. This should be the default
encoding = String.Encoding.utf8.rawValue
offset = 0
}
enc?.pointee = encoding
// Since the encoding being passed includes the byte order the BOM wont be checked or skipped, so pass offset to
// manually skip the BOM header.
guard let cf = CFStringCreateWithBytes(kCFAllocatorDefault, bytePtr + offset, readResult.length - offset,
CFStringConvertNSStringEncodingToEncoding(encoding), true) else {
throw NSError(domain: NSCocoaErrorDomain, code: CocoaError.fileReadInapplicableStringEncoding.rawValue, userInfo: [
"NSDebugDescription" : "Unable to create a string using the specified encoding."
])
}
var str: String?
if String._conditionallyBridgeFromObjectiveC(cf._nsObject, result: &str) {
self.init(str!)
} else {
throw NSError(domain: NSCocoaErrorDomain, code: CocoaError.fileReadInapplicableStringEncoding.rawValue, userInfo: [
"NSDebugDescription" : "Unable to bridge CFString to String."
])
}
}
public convenience init(contentsOfFile path: String, usedEncoding enc: UnsafeMutablePointer<UInt>?) throws {
try self.init(contentsOf: URL(fileURLWithPath: path), usedEncoding: enc)
}
}
extension NSString : ExpressibleByStringLiteral { }
open class NSMutableString : NSString {
open func replaceCharacters(in range: NSRange, with aString: String) {
guard type(of: self) === NSString.self || type(of: self) === NSMutableString.self else {
NSRequiresConcreteImplementation()
}
let start = _storage.utf16.startIndex
let min = _storage.utf16.index(start, offsetBy: range.location).samePosition(in: _storage)!
let max = _storage.utf16.index(start, offsetBy: range.location + range.length).samePosition(in: _storage)!
_storage.replaceSubrange(min..<max, with: aString)
}
public required override init(characters: UnsafePointer<unichar>, length: Int) {
super.init(characters: characters, length: length)
}
public required init(capacity: Int) {
super.init(characters: [], length: 0)
}
public convenience required init?(coder aDecoder: NSCoder) {
guard let str = NSString(coder: aDecoder) else {
return nil
}
self.init(string: String._unconditionallyBridgeFromObjectiveC(str))
}
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.utf8CodeUnitCount))))
} 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 type(of: self) == NSMutableString.self {
_storage.append(String._fromWellFormedCodeUnitSequence(UTF16.self, input: UnsafeBufferPointer(start: characters, count: length)))
} else {
replaceCharacters(in: NSRange(location: self.length, length: 0), with: String._fromWellFormedCodeUnitSequence(UTF16.self, input: UnsafeBufferPointer(start: characters, count: length)))
}
}
internal func _cfAppendCString(_ characters: UnsafePointer<Int8>, length: Int) {
if type(of: self) == NSMutableString.self {
_storage.append(String(cString: characters))
}
}
}
extension NSMutableString {
public func insert(_ aString: String, at loc: Int) {
replaceCharacters(in: NSRange(location: loc, length: 0), with: aString)
}
public func deleteCharacters(in range: NSRange) {
replaceCharacters(in: range, with: "")
}
public func append(_ aString: String) {
replaceCharacters(in: NSRange(location: length, length: 0), with: aString)
}
public func setString(_ aString: String) {
replaceCharacters(in: NSRange(location: 0, length: length), with: aString)
}
internal func _replaceOccurrencesOfRegularExpressionPattern(_ pattern: String, withTemplate replacement: String, options: CompareOptions, range searchRange: NSRange) -> Int {
let regexOptions: NSRegularExpression.Options = options.contains(.caseInsensitive) ? .caseInsensitive : []
let matchingOptions: NSRegularExpression.MatchingOptions = options.contains(.anchored) ? .anchored : []
if let regex = _createRegexForPattern(pattern, regexOptions) {
return regex.replaceMatches(in: self, options: matchingOptions, range: searchRange, withTemplate: replacement)
}
return 0
}
public func replaceOccurrences(of target: String, with replacement: String, options: CompareOptions = [], range searchRange: NSRange) -> Int {
let backwards = options.contains(.backwards)
let len = length
precondition(searchRange.length <= len && searchRange.location <= len - searchRange.length, "Search range is out of bounds")
if options.contains(.regularExpression) {
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 rangePtr = CFArrayGetValueAtIndex(findResults, backwards ? cnt : numOccurrences - cnt - 1)
replaceCharacters(in: NSRange(rangePtr!.load(as: CFRange.self)), with: 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(to: &cfRange) { (rangep: UnsafeMutablePointer<CFRange>) -> Bool in
if CFStringTransform(_cfMutableObject, rangep, transform._cfObject, reverse) {
resultingRange?.pointee.location = rangep.pointee.location
resultingRange?.pointee.length = rangep.pointee.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 : _CFBridgeable, _SwiftBridgeable {
typealias SwiftType = String
internal var _cfObject: CFString { return unsafeBitCast(self, to: CFString.self) }
internal var _swiftObject: String { return String._unconditionallyBridgeFromObjectiveC(self) }
}
extension NSMutableString {
internal var _cfMutableObject: CFMutableString { return unsafeBitCast(self, to: CFMutableString.self) }
}
extension CFString : _NSBridgeable, _SwiftBridgeable {
typealias NSType = NSString
typealias SwiftType = String
internal var _nsObject: NSType { return unsafeBitCast(self, to: NSString.self) }
internal var _swiftObject: String { return _nsObject._swiftObject }
}
extension String : _NSBridgeable, _CFBridgeable {
typealias NSType = NSString
typealias CFType = CFString
internal var _nsObject: NSType { return _bridgeToObjectiveC() }
internal var _cfObject: CFType { return _nsObject._cfObject }
}
#if !(os(OSX) || os(iOS))
extension String {
public func hasPrefix(_ prefix: String) -> Bool {
if prefix.isEmpty {
return true
}
let cfstring = self._cfObject
let range = CFRangeMake(0, CFStringGetLength(cfstring))
let opts = CFStringCompareFlags(
kCFCompareAnchored | kCFCompareNonliteral)
return CFStringFindWithOptions(cfstring, prefix._cfObject,
range, opts, nil)
}
public func hasSuffix(_ suffix: String) -> Bool {
if suffix.isEmpty {
return true
}
let cfstring = self._cfObject
let range = CFRangeMake(0, CFStringGetLength(cfstring))
let opts = CFStringCompareFlags(
kCFCompareAnchored | kCFCompareBackwards | kCFCompareNonliteral)
return CFStringFindWithOptions(cfstring, suffix._cfObject,
range, opts, nil)
}
}
#endif
extension NSString : _StructTypeBridgeable {
public typealias _StructType = String
public func _bridgeToSwift() -> _StructType {
return _StructType._unconditionallyBridgeFromObjectiveC(self)
}
}