Merge pull request #1185 from weissi/jw-test-http-server-auto-port
diff --git a/CoreFoundation/Base.subproj/CFPlatform.c b/CoreFoundation/Base.subproj/CFPlatform.c
index 5f67510..6dd5564 100644
--- a/CoreFoundation/Base.subproj/CFPlatform.c
+++ b/CoreFoundation/Base.subproj/CFPlatform.c
@@ -180,6 +180,12 @@
}
return __CFProcessPath;
}
+
+#else
+
+Boolean _CFIsMainThread(void) {
+ return pthread_main_np() == 1;
+}
#endif
CF_PRIVATE CFStringRef _CFProcessNameString(void) {
diff --git a/CoreFoundation/Base.subproj/ForSwiftFoundationOnly.h b/CoreFoundation/Base.subproj/ForSwiftFoundationOnly.h
index c580409..ea62f88 100644
--- a/CoreFoundation/Base.subproj/ForSwiftFoundationOnly.h
+++ b/CoreFoundation/Base.subproj/ForSwiftFoundationOnly.h
@@ -27,6 +27,7 @@
#include <CoreFoundation/ForFoundationOnly.h>
#include <fts.h>
#include <pthread.h>
+#include <execinfo.h>
_CF_EXPORT_SCOPE_BEGIN
@@ -294,6 +295,7 @@
CF_EXPORT void CFLog1(CFLogLevel lev, CFStringRef message);
CF_EXPORT Boolean _CFIsMainThread(void);
+CF_EXPORT pthread_t _CFMainPThread;
CF_EXPORT CFHashCode __CFHashDouble(double d);
diff --git a/Docs/Status.md b/Docs/Status.md
index 13c9cfd..fa06c03 100644
--- a/Docs/Status.md
+++ b/Docs/Status.md
@@ -278,7 +278,7 @@
| `Process` | Mostly Complete | Substantial | `interrupt()`, `terminate()`, `suspend()`, and `resume()` remain unimplemented |
| `Bundle` | Mostly Complete | Incomplete | `allBundles`, `init(for:)`, `unload()`, `classNamed()`, and `principalClass` remain unimplemented |
| `ProcessInfo` | Complete | Substantial | |
- | `Thread` | Incomplete | Incomplete | `isMainThread`, `mainThread`, `name`, `callStackReturnAddresses`, and `callStackSymbols` remain unimplemented |
+ | `Thread` | Complete | Incomplete | |
| `Operation` | Complete | Incomplete | |
| `BlockOperation` | Complete | Incomplete | |
| `OperationQueue` | Complete | Incomplete | |
diff --git a/Foundation.xcodeproj/project.pbxproj b/Foundation.xcodeproj/project.pbxproj
index ae314b4..5461afe 100644
--- a/Foundation.xcodeproj/project.pbxproj
+++ b/Foundation.xcodeproj/project.pbxproj
@@ -14,6 +14,7 @@
231503DB1D8AEE5D0061694D /* TestDecimal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 231503DA1D8AEE5D0061694D /* TestDecimal.swift */; };
294E3C1D1CC5E19300E4F44C /* TestNSAttributedString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 294E3C1C1CC5E19300E4F44C /* TestNSAttributedString.swift */; };
2EBE67A51C77BF0E006583D5 /* TestDateFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EBE67A31C77BF05006583D5 /* TestDateFormatter.swift */; };
+ 3E55A2331F52463B00082000 /* TestUnit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E55A2321F52463B00082000 /* TestUnit.swift */; };
3EA9D6701EF0532D00B362D6 /* TestJSONEncoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3EA9D66F1EF0532D00B362D6 /* TestJSONEncoder.swift */; };
3EDCE50C1EF04D8100C2EC04 /* Codable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3EDCE5051EF04D8100C2EC04 /* Codable.swift */; };
3EDCE5101EF04D8100C2EC04 /* JSONEncoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3EDCE5091EF04D8100C2EC04 /* JSONEncoder.swift */; };
@@ -490,6 +491,7 @@
231503DA1D8AEE5D0061694D /* TestDecimal.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestDecimal.swift; sourceTree = "<group>"; };
294E3C1C1CC5E19300E4F44C /* TestNSAttributedString.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestNSAttributedString.swift; sourceTree = "<group>"; };
2EBE67A31C77BF05006583D5 /* TestDateFormatter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestDateFormatter.swift; sourceTree = "<group>"; };
+ 3E55A2321F52463B00082000 /* TestUnit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestUnit.swift; sourceTree = "<group>"; };
3EA9D66F1EF0532D00B362D6 /* TestJSONEncoder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestJSONEncoder.swift; sourceTree = "<group>"; };
3EDCE5051EF04D8100C2EC04 /* Codable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Codable.swift; sourceTree = "<group>"; };
3EDCE5091EF04D8100C2EC04 /* JSONEncoder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JSONEncoder.swift; sourceTree = "<group>"; };
@@ -1494,6 +1496,7 @@
D4FE895A1D703D1100DA7986 /* TestURLRequest.swift */,
5B6F17961C48631C00935030 /* TestUtils.swift */,
03B6F5831F15F339004F25AF /* TestURLProtocol.swift */,
+ 3E55A2321F52463B00082000 /* TestUnit.swift */,
);
name = Tests;
sourceTree = "<group>";
@@ -2419,6 +2422,7 @@
F9E0BB371CA70B8000F7FF3C /* TestURLCredential.swift in Sources */,
5B13B3341C582D4C00651CE2 /* TestNSKeyedArchiver.swift in Sources */,
5B13B3441C582D4C00651CE2 /* TestNSSet.swift in Sources */,
+ 3E55A2331F52463B00082000 /* TestUnit.swift in Sources */,
5B13B3321C582D4C00651CE2 /* TestIndexSet.swift in Sources */,
5B13B3511C582D4C00651CE2 /* TestByteCountFormatter.swift in Sources */,
BDFDF0A71DFF5B3E00C04CC5 /* TestPersonNameComponents.swift in Sources */,
diff --git a/Foundation/HTTPCookieStorage.swift b/Foundation/HTTPCookieStorage.swift
index 2e9e080..045b69d 100644
--- a/Foundation/HTTPCookieStorage.swift
+++ b/Foundation/HTTPCookieStorage.swift
@@ -49,7 +49,7 @@
let bundlePath = Bundle.main.bundlePath
var bundleName = bundlePath.components(separatedBy: "/").last!
if let range = bundleName.range(of: ".", options: String.CompareOptions.backwards, range: nil, locale: nil) {
- bundleName = bundleName.substring(to: range.lowerBound)
+ bundleName = String(bundleName[..<range.lowerBound])
}
let cookieFolderPath = _CFXDGCreateDataHomePath()._swiftObject + "/" + bundleName
cookieFilePath = filePath(path: cookieFolderPath, fileName: "/.cookies." + cookieStorageName, bundleName: bundleName)
diff --git a/Foundation/JSONSerialization.swift b/Foundation/JSONSerialization.swift
index d4a1201..a7b011f 100644
--- a/Foundation/JSONSerialization.swift
+++ b/Foundation/JSONSerialization.swift
@@ -577,9 +577,12 @@
writer(" ")
}
}
-
+
//[SR-2151] https://bugs.swift.org/browse/SR-2151
private mutating func _serializationString(for number: NSNumber) -> String {
+ if !CFNumberIsFloatType(number._cfObject) {
+ return number.stringValue
+ }
return CFNumberFormatterCreateStringWithNumber(nil, _numberformatter, number._cfObject)._swiftObject
}
}
diff --git a/Foundation/Measurement.swift b/Foundation/Measurement.swift
index 99f6904..b119af1 100644
--- a/Foundation/Measurement.swift
+++ b/Foundation/Measurement.swift
@@ -275,3 +275,60 @@
return Measurement(value: source!.doubleValue, unit: u)
}
}
+
+extension Measurement : Codable {
+ private enum CodingKeys : Int, CodingKey {
+ case value
+ case unit
+ }
+
+ private enum UnitCodingKeys : Int, CodingKey {
+ case symbol
+ case converter
+ }
+
+ private enum LinearConverterCodingKeys : Int, CodingKey {
+ case coefficient
+ case constant
+ }
+
+ public init(from decoder: Decoder) throws {
+ let container = try decoder.container(keyedBy: CodingKeys.self)
+ let value = try container.decode(Double.self, forKey: .value)
+
+ let unitContainer = try container.nestedContainer(keyedBy: UnitCodingKeys.self, forKey: .unit)
+ let symbol = try unitContainer.decode(String.self, forKey: .symbol)
+
+ let unit: UnitType
+ if UnitType.self is Dimension.Type {
+ let converterContainer = try unitContainer.nestedContainer(keyedBy: LinearConverterCodingKeys.self, forKey: .converter)
+ let coefficient = try converterContainer.decode(Double.self, forKey: .coefficient)
+ let constant = try converterContainer.decode(Double.self, forKey: .constant)
+ let unitMetaType = (UnitType.self as! Dimension.Type)
+ unit = (unitMetaType.init(symbol: symbol, converter: UnitConverterLinear(coefficient: coefficient, constant: constant)) as! UnitType)
+ } else {
+ unit = UnitType(symbol: symbol)
+ }
+
+ self.init(value: value, unit: unit)
+ }
+
+ public func encode(to encoder: Encoder) throws {
+ var container = encoder.container(keyedBy: CodingKeys.self)
+ try container.encode(self.value, forKey: .value)
+
+ var unitContainer = container.nestedContainer(keyedBy: UnitCodingKeys.self, forKey: .unit)
+ try unitContainer.encode(self.unit.symbol, forKey: .symbol)
+
+ if UnitType.self is Dimension.Type {
+ guard type(of: (self.unit as! Dimension).converter) is UnitConverterLinear.Type else {
+ preconditionFailure("Cannot encode a Measurement whose UnitType has a non-linear unit converter.")
+ }
+
+ let converter = (self.unit as! Dimension).converter as! UnitConverterLinear
+ var converterContainer = unitContainer.nestedContainer(keyedBy: LinearConverterCodingKeys.self, forKey: .converter)
+ try converterContainer.encode(converter.coefficient, forKey: .coefficient)
+ try converterContainer.encode(converter.constant, forKey: .constant)
+ }
+ }
+}
diff --git a/Foundation/NSData.swift b/Foundation/NSData.swift
index 6dd8048..97b269d 100644
--- a/Foundation/NSData.swift
+++ b/Foundation/NSData.swift
@@ -146,6 +146,7 @@
public convenience init(bytesNoCopy bytes: UnsafeMutableRawPointer, length: Int, deallocator: ((UnsafeMutableRawPointer, Int) -> Void)? = nil) {
self.init(bytes: bytes, length: length, copy: false, deallocator: deallocator)
}
+
public convenience init(contentsOfFile path: String, options readOptionsMask: ReadingOptions = []) throws {
let readResult = try NSData.readBytesFromFileWithExtendedAttributes(path, options: readOptionsMask)
self.init(bytes: readResult.bytes, length: readResult.length, copy: false, deallocator: readResult.deallocator)
@@ -380,7 +381,10 @@
}
let length = Int(info.st_size)
-
+ if length == 0 && (info.st_mode & S_IFMT == S_IFREG) {
+ return try readZeroSizeFile(fd)
+ }
+
if options.contains(.alwaysMapped) {
let data = mmap(nil, length, PROT_READ, MAP_PRIVATE, fd, 0)
@@ -414,6 +418,37 @@
free(buffer)
}
}
+
+ internal static func readZeroSizeFile(_ fd: Int32) throws -> NSDataReadResult {
+ let blockSize = 1024 * 1024 // 1MB
+ var data: UnsafeMutableRawPointer? = nil
+ var bytesRead = 0
+ var amt = 0
+
+ repeat {
+ data = realloc(data, bytesRead + blockSize)
+ amt = read(fd, data!.advanced(by: bytesRead), blockSize)
+
+ // Dont continue on EINTR or EAGAIN as the file position may not
+ // have changed, see read(2).
+ if amt < 0 {
+ free(data!)
+ throw NSError(domain: NSPOSIXErrorDomain, code: Int(errno), userInfo: nil)
+ }
+ bytesRead += amt
+ } while amt > 0
+
+ if bytesRead == 0 {
+ free(data!)
+ data = malloc(0)
+ } else {
+ data = realloc(data, bytesRead) // shrink down the allocated block.
+ }
+
+ return NSDataReadResult(bytes: data!, length: bytesRead) { buffer, length in
+ free(buffer)
+ }
+ }
internal func makeTemporaryFile(inDirectory dirPath: String) throws -> (Int32, String) {
let template = dirPath._nsObject.appendingPathComponent("tmp.XXXXXX")
diff --git a/Foundation/NSLock.swift b/Foundation/NSLock.swift
index e166b80..f4b41d0 100644
--- a/Foundation/NSLock.swift
+++ b/Foundation/NSLock.swift
@@ -230,12 +230,16 @@
}
var ts = timespec()
ts.tv_sec = Int(floor(ti))
- ts.tv_nsec = Int((ti - Double(ts.tv_sec)) * 1000000000.0)
+ ts.tv_nsec = Int((ti - Double(ts.tv_sec)) * 1_000_000_000.0)
var tv = timeval()
withUnsafeMutablePointer(to: &tv) { t in
gettimeofday(t, nil)
ts.tv_sec += t.pointee.tv_sec
- ts.tv_nsec += Int((t.pointee.tv_usec * 1000000) / 1000000000)
+ ts.tv_nsec += Int(t.pointee.tv_usec) * 1000
+ if ts.tv_nsec >= 1_000_000_000 {
+ ts.tv_sec += ts.tv_nsec / 1_000_000_000
+ ts.tv_nsec = ts.tv_nsec % 1_000_000_000
+ }
}
let retVal: Int32 = withUnsafePointer(to: &ts) { t in
return pthread_cond_timedwait(cond, mutex, t)
diff --git a/Foundation/NSNumber.swift b/Foundation/NSNumber.swift
index b72bdeb..1759771 100644
--- a/Foundation/NSNumber.swift
+++ b/Foundation/NSNumber.swift
@@ -256,7 +256,7 @@
fatalError("unsupported CFNumberType: '\(numberType)'")
}
}
-
+
deinit {
_CFDeinit(self)
}
@@ -536,13 +536,25 @@
}
}
+ private static let _numberFormatterForNilLocale: CFNumberFormatter = {
+ let formatter: CFNumberFormatter
+ formatter = CFNumberFormatterCreate(nil, CFLocaleCopyCurrent(), kCFNumberFormatterNoStyle)
+ CFNumberFormatterSetProperty(formatter, kCFNumberFormatterMaxFractionDigits, 15._bridgeToObjectiveC())
+ return formatter
+ }()
+
open func description(withLocale locale: Locale?) -> String {
+ // CFNumberFormatterCreateStringWithNumber() does not like numbers of type
+ // SInt128Type, as it loses the type when looking it up and treats it as
+ // an SInt64Type, so special case them.
+ if _CFNumberGetType2(_cfObject) == kCFNumberSInt128Type {
+ return String(format: "%@", unsafeBitCast(_cfObject, to: UnsafePointer<CFNumber>.self))
+ }
+
let aLocale = locale
let formatter: CFNumberFormatter
if (aLocale == nil) {
- formatter = CFNumberFormatterCreate(nil, CFLocaleCopyCurrent(), kCFNumberFormatterNoStyle)
- CFNumberFormatterSetProperty(formatter, kCFNumberFormatterMaxFractionDigits, 15._bridgeToObjectiveC())
-
+ formatter = NSNumber._numberFormatterForNilLocale
} else {
formatter = CFNumberFormatterCreate(nil, aLocale?._cfObject, kCFNumberFormatterDecimalStyle)
}
diff --git a/Foundation/NSStringAPI.swift b/Foundation/NSStringAPI.swift
index a5e1b00..03ebc30 100644
--- a/Foundation/NSStringAPI.swift
+++ b/Foundation/NSStringAPI.swift
@@ -2,11 +2,11 @@
//
// This source file is part of the Swift.org open source project
//
-// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
+// Copyright (c) 2014 - 2017 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
+// See https://swift.org/LICENSE.txt for license information
+// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
@@ -14,6 +14,20 @@
//
//===----------------------------------------------------------------------===//
+// Important Note
+// ==============
+//
+// This file is shared between two projects:
+//
+// 1. https://github.com/apple/swift/tree/master/stdlib/public/SDK/Foundation
+// 2. https://github.com/apple/swift-corelibs-foundation/tree/master/Foundation
+//
+// If you change this file, you must update it in both places.
+
+#if !DEPLOYMENT_RUNTIME_SWIFT
+@_exported import Foundation // Clang module
+#endif
+
// Open Issues
// ===========
//
@@ -34,70 +48,38 @@
length: r.upperBound.encodedOffset - r.lowerBound.encodedOffset)
}
+#if !DEPLOYMENT_RUNTIME_SWIFT
+// We only need this for UnsafeMutablePointer, but there's not currently a way
+// to write that constraint.
+extension Optional {
+ /// Invokes `body` with `nil` if `self` is `nil`; otherwise, passes the
+ /// address of `object` to `body`.
+ ///
+ /// This is intended for use with Foundation APIs that return an Objective-C
+ /// type via out-parameter where it is important to be able to *ignore* that
+ /// parameter by passing `nil`. (For some APIs, this may allow the
+ /// implementation to avoid some work.)
+ ///
+ /// In most cases it would be simpler to just write this code inline, but if
+ /// `body` is complicated than that results in unnecessarily repeated code.
+ internal func _withNilOrAddress<NSType : AnyObject, ResultType>(
+ of object: inout NSType?,
+ _ body:
+ (AutoreleasingUnsafeMutablePointer<NSType?>?) -> ResultType
+ ) -> ResultType {
+ return self == nil ? body(nil) : body(&object)
+ }
+}
+#endif
+
extension String {
-
- //===--- Bridging Helpers -----------------------------------------------===//
- //===--------------------------------------------------------------------===//
-
- /// The corresponding `NSString` - a convenience for bridging code.
- var _ns: NSString {
- return self._bridgeToObjectiveC()
- }
-
- /// Return an `Index` corresponding to the given offset in our UTF-16
- /// representation.
- func _index(_ utf16Index: Int) -> Index {
- return Index(encodedOffset: utf16Index)
- }
-
- /// Return a `Range<Index>` corresponding to the given `NSRange` of
- /// our UTF-16 representation.
- func _range(_ r: NSRange) -> Range<Index> {
- return _index(r.location)..<_index(r.location + r.length)
- }
-
- /// Return a `Range<Index>?` corresponding to the given `NSRange` of
- /// our UTF-16 representation.
- func _optionalRange(_ r: NSRange) -> Range<Index>? {
- if r.location == NSNotFound {
- return nil
- }
- return _range(r)
- }
-
- /// Invoke `body` on an `Int` buffer. If `index` was converted from
- /// non-`nil`, convert the buffer to an `Index` and write it into the
- /// memory referred to by `index`
- func _withOptionalOutParameter<Result>(
- _ index: UnsafeMutablePointer<Index>?,
- _ body: (UnsafeMutablePointer<Int>?) -> Result
- ) -> Result {
- var utf16Index: Int = 0
- let result = (index != nil ? body(&utf16Index) : body(nil))
- index?.pointee = self._index(utf16Index)
- return result
- }
-
- /// Invoke `body` on an `NSRange` buffer. If `range` was converted
- /// from non-`nil`, convert the buffer to a `Range<Index>` and write
- /// it into the memory referred to by `range`
- func _withOptionalOutParameter<Result>(
- _ range: UnsafeMutablePointer<Range<Index>>?,
- _ body: (UnsafeMutablePointer<NSRange>?) -> Result
- ) -> Result {
- var nsRange = NSRange(location: 0, length: 0)
- let result = (range != nil ? body(&nsRange) : body(nil))
- range?.pointee = self._range(nsRange)
- return result
- }
-
//===--- Class Methods --------------------------------------------------===//
//===--------------------------------------------------------------------===//
// @property (class) const NSStringEncoding *availableStringEncodings;
- /// Returns an Array of the encodings string objects support
- /// in the application's environment.
+ /// An array of the encodings that strings support in the application's
+ /// environment.
public static var availableStringEncodings: [Encoding] {
var result = [Encoding]()
var p = NSString.availableStringEncodings
@@ -110,15 +92,20 @@
// @property (class) NSStringEncoding defaultCStringEncoding;
- /// Returns the C-string encoding assumed for any method accepting
- /// a C string as an argument.
+ /// The C-string encoding assumed for any method accepting a C string as an
+ /// argument.
public static var defaultCStringEncoding: Encoding {
return Encoding(rawValue: NSString.defaultCStringEncoding)
}
// + (NSString *)localizedNameOfStringEncoding:(NSStringEncoding)encoding
- /// Returns a human-readable string giving the name of a given encoding.
+ /// Returns a human-readable string giving the name of the specified encoding.
+ ///
+ /// - Parameter encoding: A string encoding. For possible values, see
+ /// `String.Encoding`.
+ /// - Returns: A human-readable string giving the name of `encoding` in the
+ /// current locale.
public static func localizedName(
of encoding: Encoding
) -> String {
@@ -137,15 +124,6 @@
arguments: arguments)
}
- // + (NSString *)pathWithComponents:(NSArray *)components
-
- /// Returns a string built from the strings in a given array
- /// by concatenating them with a path separator between each pair.
- @available(*, unavailable, message: "Use fileURL(withPathComponents:) on URL instead.")
- public static func path(withComponents components: [String]) -> String {
- return (NSURL.fileURL(withPathComponents: components)?.path)!
- }
-
//===--------------------------------------------------------------------===//
// NSString factory functions that have a corresponding constructor
// are omitted.
@@ -188,453 +166,18 @@
// + (instancetype)stringWithUTF8String:(const char *)bytes
- /// Produces a string created by copying the data from a given
+ /// Creates a string by copying the data from a given
/// C array of UTF8-encoded bytes.
public init?(utf8String bytes: UnsafePointer<CChar>) {
if let ns = NSString(utf8String: bytes) {
- self = ns._swiftObject
+ self = String._unconditionallyBridgeFromObjectiveC(ns)
} else {
return nil
}
}
+}
- //===--- Instance Methods/Properties-------------------------------------===//
- //===--------------------------------------------------------------------===//
-
- //===--- Omitted by agreement during API review 5/20/2014 ---------------===//
- // @property BOOL boolValue;
-
- // - (BOOL)canBeConvertedToEncoding:(NSStringEncoding)encoding
-
- /// Returns a Boolean value that indicates whether the
- /// `String` can be converted to a given encoding without loss of
- /// information.
- public func canBeConverted(to encoding: Encoding) -> Bool {
- return _ns.canBeConverted(to: encoding.rawValue)
- }
-
- // @property NSString* capitalizedString
-
- /// Produce a string with the first character from each word changed
- /// to the corresponding uppercase value.
- public var capitalized: String {
- return _ns.capitalized as String
- }
-
- // @property (readonly, copy) NSString *localizedCapitalizedString NS_AVAILABLE(10_11, 9_0);
-
- /// A capitalized representation of the `String` that is produced
- /// using the current locale.
- public var localizedCapitalized: String {
- return _ns.localizedCapitalized
- }
-
- // - (NSString *)capitalizedStringWithLocale:(Locale *)locale
-
- /// Returns a capitalized representation of the `String`
- /// using the specified locale.
- public func capitalized(with locale: Locale?) -> String {
- return _ns.capitalized(with: locale) as String
- }
-
- // - (NSComparisonResult)caseInsensitiveCompare:(NSString *)aString
-
- /// Returns the result of invoking `compare:options:` with
- /// `NSCaseInsensitiveSearch` as the only option.
- public func caseInsensitiveCompare(_ aString: String) -> ComparisonResult {
- return _ns.caseInsensitiveCompare(aString)
- }
-
- //===--- Omitted by agreement during API review 5/20/2014 ---------------===//
- // - (unichar)characterAtIndex:(NSUInteger)index
- //
- // We have a different meaning for "Character" in Swift, and we are
- // trying not to expose error-prone UTF-16 integer indexes
-
- // - (NSString *)
- // commonPrefixWithString:(NSString *)aString
- // options:(StringCompareOptions)mask
-
- /// Returns a string containing characters the `String` and a
- /// given string have in common, starting from the beginning of each
- /// up to the first characters that aren't equivalent.
- public func commonPrefix(
- with aString: String, options: CompareOptions = []) -> String {
- return _ns.commonPrefix(with: aString, options: options)
- }
-
- // - (NSComparisonResult)
- // compare:(NSString *)aString
- //
- // - (NSComparisonResult)
- // compare:(NSString *)aString options:(StringCompareOptions)mask
- //
- // - (NSComparisonResult)
- // compare:(NSString *)aString options:(StringCompareOptions)mask
- // range:(NSRange)range
- //
- // - (NSComparisonResult)
- // compare:(NSString *)aString options:(StringCompareOptions)mask
- // range:(NSRange)range locale:(id)locale
-
- /// Compares the string using the specified options and
- /// returns the lexical ordering for the range.
- public func compare(
- _ aString: String,
- options mask: CompareOptions = [],
- range: Range<Index>? = nil,
- locale: Locale? = nil
- ) -> ComparisonResult {
- // According to Ali Ozer, there may be some real advantage to
- // dispatching to the minimal selector for the supplied options.
- // So let's do that; the switch should compile away anyhow.
- return locale != nil ? _ns.compare(
- aString,
- options: mask,
- range: _toNSRange(
- range ?? self.startIndex..<self.endIndex
- ),
- locale: locale?._bridgeToObjectiveC()
- )
-
- : range != nil ? _ns.compare(
- aString,
- options: mask,
- range: _toNSRange(range!)
- )
-
- : !mask.isEmpty ? _ns.compare(aString, options: mask)
-
- : _ns.compare(aString)
- }
-
- // - (NSUInteger)
- // completePathIntoString:(NSString **)outputName
- // caseSensitive:(BOOL)flag
- // matchesIntoArray:(NSArray **)outputArray
- // filterTypes:(NSArray *)filterTypes
-
- /// Interprets the `String` as a path in the file system and
- /// attempts to perform filename completion, returning a numeric
- /// value that indicates whether a match was possible, and by
- /// reference the longest path that matches the `String`.
- /// Returns the actual number of matching paths.
- public func completePath(
- into outputName: UnsafeMutablePointer<String>? = nil,
- caseSensitive: Bool,
- matchesInto outputArray: UnsafeMutablePointer<[String]>? = nil,
- filterTypes: [String]? = nil
- ) -> Int {
-
- var outputNamePlaceholder: String?
- var outputArrayPlaceholder = [String]()
- let res = self._ns.completePath(
- into: &outputNamePlaceholder,
- caseSensitive: caseSensitive,
- matchesInto: &outputArrayPlaceholder,
- filterTypes: filterTypes
- )
- if let n = outputNamePlaceholder {
- outputName?.pointee = n
- } else {
- outputName?.pointee = ""
- }
- outputArray?.pointee = outputArrayPlaceholder
- return res
- }
-
- // - (NSArray *)
- // componentsSeparatedByCharactersInSet:(NSCharacterSet *)separator
-
- /// Returns an array containing substrings from the `String`
- /// that have been divided by characters in a given set.
- public func components(separatedBy separator: CharacterSet) -> [String] {
- return _ns.components(separatedBy: separator)
- }
-
-
- // - (NSArray *)componentsSeparatedByString:(NSString *)separator
-
- /// Returns an array containing substrings from the `String`
- /// that have been divided by a given separator.
- public func components(separatedBy separator: String) -> [String] {
- return _ns.components(separatedBy: separator)
- }
-
- // - (const char *)cStringUsingEncoding:(NSStringEncoding)encoding
-
- /// Returns a representation of the `String` as a C string
- /// using a given encoding.
- public func cString(using encoding: Encoding) -> [CChar]? {
- return withExtendedLifetime(_ns) {
- (s: NSString) -> [CChar]? in
- _persistCString(s.cString(using: encoding.rawValue))
- }
- }
-
- // - (NSData *)dataUsingEncoding:(NSStringEncoding)encoding
- //
- // - (NSData *)
- // dataUsingEncoding:(NSStringEncoding)encoding
- // allowLossyConversion:(BOOL)flag
-
- /// Returns a `Data` containing a representation of
- /// the `String` encoded using a given encoding.
- public func data(
- using encoding: Encoding,
- allowLossyConversion: Bool = false
- ) -> Data? {
- return _ns.data(
- using: encoding.rawValue,
- allowLossyConversion: allowLossyConversion)
- }
-
- // @property NSString* decomposedStringWithCanonicalMapping;
-
- /// Returns a string made by normalizing the `String`'s
- /// contents using Form D.
- public var decomposedStringWithCanonicalMapping: String {
- return _ns.decomposedStringWithCanonicalMapping
- }
-
- // @property NSString* decomposedStringWithCompatibilityMapping;
-
- /// Returns a string made by normalizing the `String`'s
- /// contents using Form KD.
- public var decomposedStringWithCompatibilityMapping: String {
- return _ns.decomposedStringWithCompatibilityMapping
- }
-
- //===--- Importing Foundation should not affect String printing ---------===//
- // Therefore, we're not exposing this:
- //
- // @property NSString* description
-
-
- //===--- Omitted for consistency with API review results 5/20/2014 -----===//
- // @property double doubleValue;
-
- // - (void)
- // enumerateLinesUsing:(void (^)(NSString *line, BOOL *stop))block
-
- /// Enumerates all the lines in a string.
- public func enumerateLines(
- invoking body: @escaping (_ line: String, _ stop: inout Bool) -> Void
- ) {
- _ns.enumerateLines {
- (line: String, stop: UnsafeMutablePointer<ObjCBool>)
- in
- var stop_ = false
- body(line, &stop_)
- if stop_ {
- stop.pointee = true
- }
- }
- }
-
-
- // - (void)
- // enumerateSubstringsInRange:(NSRange)range
- // options:(NSStringEnumerationOptions)opts
- // usingBlock:(
- // void (^)(
- // NSString *substring,
- // NSRange substringRange,
- // NSRange enclosingRange,
- // BOOL *stop)
- // )block
-
- /// Enumerates the substrings of the specified type in the
- /// specified range of the string.
- public func enumerateSubstrings(
- in range: Range<Index>,
- options opts: EnumerationOptions = [],
- _ body: @escaping (
- _ substring: String?, _ substringRange: Range<Index>,
- _ enclosingRange: Range<Index>, inout Bool
- ) -> Void
- ) {
- _ns.enumerateSubstrings(in: _toNSRange(range), options: opts) {
- var stop_ = false
-
- body($0,
- self._range($1),
- self._range($2),
- &stop_)
-
- if stop_ {
- UnsafeMutablePointer($3).pointee = true
- }
- }
- }
-
- // @property NSStringEncoding fastestEncoding;
-
- /// Returns the fastest encoding to which the `String` may be
- /// converted without loss of information.
- public var fastestEncoding: Encoding {
- return Encoding(rawValue: _ns.fastestEncoding)
- }
-
- // - (const char *)fileSystemRepresentation
-
- /// Returns a file system-specific representation of the `String`.
- @available(*, unavailable, message: "Use getFileSystemRepresentation on URL instead.")
- public var fileSystemRepresentation: [CChar] {
- return _persistCString(_ns.fileSystemRepresentation)!
- }
-
- //===--- Omitted for consistency with API review results 5/20/2014 ------===//
- // @property float floatValue;
-
- // - (BOOL)
- // getBytes:(void *)buffer
- // maxLength:(NSUInteger)maxBufferCount
- // usedLength:(NSUInteger*)usedBufferCount
- // encoding:(NSStringEncoding)encoding
- // options:(StringEncodingConversionOptions)options
- // range:(NSRange)range
- // remainingRange:(NSRangePointer)leftover
-
- /// Writes the given `range` of characters into `buffer` in a given
- /// `encoding`, without any allocations. Does not NULL-terminate.
- ///
- /// - Parameter buffer: A buffer into which to store the bytes from
- /// the receiver. The returned bytes are not NUL-terminated.
- ///
- /// - Parameter maxBufferCount: The maximum number of bytes to write
- /// to buffer.
- ///
- /// - Parameter usedBufferCount: The number of bytes used from
- /// buffer. Pass `nil` if you do not need this value.
- ///
- /// - Parameter encoding: The encoding to use for the returned bytes.
- ///
- /// - Parameter options: A mask to specify options to use for
- /// converting the receiver's contents to `encoding` (if conversion
- /// is necessary).
- ///
- /// - Parameter range: The range of characters in the receiver to get.
- ///
- /// - Parameter leftover: The remaining range. Pass `nil` If you do
- /// not need this value.
- ///
- /// - Returns: `true` iff some characters were converted.
- ///
- /// - Note: Conversion stops when the buffer fills or when the
- /// conversion isn't possible due to the chosen encoding.
- ///
- /// - Note: will get a maximum of `min(buffer.count, maxLength)` bytes.
- public func getBytes(
- _ buffer: inout [UInt8],
- maxLength maxBufferCount: Int,
- usedLength usedBufferCount: UnsafeMutablePointer<Int>,
- encoding: Encoding,
- options: EncodingConversionOptions = [],
- range: Range<Index>,
- remaining leftover: UnsafeMutablePointer<Range<Index>>
- ) -> Bool {
- return _withOptionalOutParameter(leftover) {
- self._ns.getBytes(
- &buffer,
- maxLength: min(buffer.count, maxBufferCount),
- usedLength: usedBufferCount,
- encoding: encoding.rawValue,
- options: options,
- range: _toNSRange(range),
- remaining: $0)
- }
- }
-
- // - (BOOL)
- // getCString:(char *)buffer
- // maxLength:(NSUInteger)maxBufferCount
- // encoding:(NSStringEncoding)encoding
-
- /// Converts the `String`'s content to a given encoding and
- /// stores them in a buffer.
- /// - Note: will store a maximum of `min(buffer.count, maxLength)` bytes.
- public func getCString(
- _ buffer: inout [CChar], maxLength: Int, encoding: Encoding
- ) -> Bool {
- return _ns.getCString(&buffer, maxLength: min(buffer.count, maxLength),
- encoding: encoding.rawValue)
- }
-
- // - (BOOL)
- // getFileSystemRepresentation:(char *)buffer
- // maxLength:(NSUInteger)maxLength
-
- /// Interprets the `String` as a system-independent path and
- /// fills a buffer with a C-string in a format and encoding suitable
- /// for use with file-system calls.
- /// - Note: will store a maximum of `min(buffer.count, maxLength)` bytes.
- @available(*, unavailable, message: "Use getFileSystemRepresentation on URL instead.")
- public func getFileSystemRepresentation(
- _ buffer: inout [CChar], maxLength: Int) -> Bool {
- return _ns.getFileSystemRepresentation(
- &buffer, maxLength: min(buffer.count, maxLength))
- }
-
- // - (void)
- // getLineStart:(NSUInteger *)startIndex
- // end:(NSUInteger *)lineEndIndex
- // contentsEnd:(NSUInteger *)contentsEndIndex
- // forRange:(NSRange)aRange
-
- /// Returns by reference the beginning of the first line and
- /// the end of the last line touched by the given range.
- public func getLineStart(
- _ start: UnsafeMutablePointer<Index>,
- end: UnsafeMutablePointer<Index>,
- contentsEnd: UnsafeMutablePointer<Index>,
- for range: Range<Index>
- ) {
- _withOptionalOutParameter(start) {
- start in self._withOptionalOutParameter(end) {
- end in self._withOptionalOutParameter(contentsEnd) {
- contentsEnd in self._ns.getLineStart(
- start, end: end,
- contentsEnd: contentsEnd,
- for: _toNSRange(range))
- }
- }
- }
- }
-
- // - (void)
- // getParagraphStart:(NSUInteger *)startIndex
- // end:(NSUInteger *)endIndex
- // contentsEnd:(NSUInteger *)contentsEndIndex
- // forRange:(NSRange)aRange
-
- /// Returns by reference the beginning of the first paragraph
- /// and the end of the last paragraph touched by the given range.
- public func getParagraphStart(
- _ start: UnsafeMutablePointer<Index>,
- end: UnsafeMutablePointer<Index>,
- contentsEnd: UnsafeMutablePointer<Index>,
- for range: Range<Index>
- ) {
- _withOptionalOutParameter(start) {
- start in self._withOptionalOutParameter(end) {
- end in self._withOptionalOutParameter(contentsEnd) {
- contentsEnd in self._ns.getParagraphStart(
- start, end: end,
- contentsEnd: contentsEnd,
- for: _toNSRange(range))
- }
- }
- }
- }
-
- // - (NSUInteger)hash
-
- /// An unsigned integer that can be used as a hash table address.
- public var hash: Int {
- return _ns.hash
- }
-
+extension String {
//===--- Already provided by String's core ------------------------------===//
// - (instancetype)init
@@ -644,13 +187,18 @@
// length:(NSUInteger)length
// encoding:(NSStringEncoding)encoding
- /// Produces an initialized `NSString` object equivalent to the given
- /// `bytes` interpreted in the given `encoding`.
+ /// Creates a new string equivalent to the given bytes interpreted in the
+ /// specified encoding.
+ ///
+ /// - Parameters:
+ /// - bytes: A sequence of bytes to interpret using `encoding`.
+ /// - encoding: The ecoding to use to interpret `bytes`.
public init? <S: Sequence>(bytes: S, encoding: Encoding)
where S.Iterator.Element == UInt8 {
let byteArray = Array(bytes)
if let ns = NSString(
bytes: byteArray, length: byteArray.count, encoding: encoding.rawValue) {
+
self = String._unconditionallyBridgeFromObjectiveC(ns)
} else {
return nil
@@ -663,10 +211,11 @@
// encoding:(NSStringEncoding)encoding
// freeWhenDone:(BOOL)flag
- /// Produces an initialized `String` object that contains a
- /// given number of bytes from a given buffer of bytes interpreted
- /// in a given encoding, and optionally frees the buffer. WARNING:
- /// this initializer is not memory-safe!
+ /// Creates a new string that contains the specified number of bytes from the
+ /// given buffer, interpreted in the specified encoding, and optionally
+ /// frees the buffer.
+ ///
+ /// - Warning: This initializer is not memory-safe!
public init?(
bytesNoCopy bytes: UnsafeMutableRawPointer, length: Int,
encoding: Encoding, freeWhenDone flag: Bool
@@ -674,6 +223,7 @@
if let ns = NSString(
bytesNoCopy: bytes, length: length, encoding: encoding.rawValue,
freeWhenDone: flag) {
+
self = String._unconditionallyBridgeFromObjectiveC(ns)
} else {
return nil
@@ -685,9 +235,8 @@
// initWithCharacters:(const unichar *)characters
// length:(NSUInteger)length
- /// Returns an initialized `String` object that contains a
- /// given number of characters from a given array of Unicode
- /// characters.
+ /// Creates a new string that contains the specified number of characters
+ /// from the given C array of Unicode characters.
public init(
utf16CodeUnits: UnsafePointer<unichar>,
count: Int
@@ -700,8 +249,8 @@
// length:(NSUInteger)length
// freeWhenDone:(BOOL)flag
- /// Returns an initialized `String` object that contains a given
- /// number of characters from a given array of UTF-16 Code Units
+ /// Creates a new string that contains the specified number of characters
+ /// from the given C array of UTF-16 code units.
public init(
utf16CodeUnitsNoCopy: UnsafePointer<unichar>,
count: Int,
@@ -813,9 +362,9 @@
return nil
}
}
-
+
// FIXME: handle optional locale with default arguments
-
+
// - (instancetype)
// initWithData:(NSData *)data
// encoding:(NSStringEncoding)encoding
@@ -826,7 +375,7 @@
guard let s = NSString(data: data, encoding: encoding.rawValue) else { return nil }
self = String._unconditionallyBridgeFromObjectiveC(s)
}
-
+
// - (instancetype)initWithFormat:(NSString *)format, ...
/// Returns a `String` object initialized by using a given
@@ -865,83 +414,461 @@
/// format string as a template into which the remaining argument
/// values are substituted according to given locale information.
public init(format: String, locale: Locale?, arguments: [CVarArg]) {
+#if DEPLOYMENT_RUNTIME_SWIFT
self = withVaList(arguments) {
String._unconditionallyBridgeFromObjectiveC(
NSString(format: format, locale: locale?._bridgeToObjectiveC(), arguments: $0)
)
}
+#else
+ self = withVaList(arguments) {
+ NSString(format: format, locale: locale, arguments: $0) as String
+ }
+#endif
}
- //===--- Already provided by core Swift ---------------------------------===//
- // - (instancetype)initWithString:(NSString *)aString
+}
- //===--- Initializers that can fail dropped for factory functions -------===//
- // - (instancetype)initWithUTF8String:(const char *)bytes
+extension StringProtocol where Index == String.Index {
+ //===--- Bridging Helpers -----------------------------------------------===//
+ //===--------------------------------------------------------------------===//
- //===--- Omitted for consistency with API review results 5/20/2014 ------===//
- // @property NSInteger integerValue;
- // @property Int intValue;
-
- //===--- Omitted by apparent agreement during API review 5/20/2014 ------===//
- // @property BOOL absolutePath;
- // - (BOOL)isEqualToString:(NSString *)aString
-
- //===--- Kept for consistency with API review results 5/20/2014 ---------===//
- // We decided to keep pathWithComponents, so keeping this too
- // @property NSString lastPathComponent;
-
- /// Returns the last path component of the `String`.
- @available(*, unavailable, message: "Use lastPathComponent on URL instead.")
- public var lastPathComponent: String {
- return _ns.lastPathComponent
+ /// The corresponding `NSString` - a convenience for bridging code.
+ // FIXME(strings): There is probably a better way to bridge Self to NSString
+ var _ns: NSString {
+ return self._ephemeralString._bridgeToObjectiveC()
}
- //===--- Renamed by agreement during API review 5/20/2014 ---------------===//
- // @property NSUInteger length;
+ /// Return an `Index` corresponding to the given offset in our UTF-16
+ /// representation.
+ func _index(_ utf16Index: Int) -> Index {
+ return Index(encodedOffset: utf16Index)
+ }
- /// Returns the number of Unicode characters in the `String`.
- @available(*, unavailable,
- message: "Take the count of a UTF-16 view instead, i.e. str.utf16.count")
- public var utf16Count: Int {
- return _ns.length
+ /// Return a `Range<Index>` corresponding to the given `NSRange` of
+ /// our UTF-16 representation.
+ func _range(_ r: NSRange) -> Range<Index> {
+ return _index(r.location)..<_index(r.location + r.length)
+ }
+
+ /// Return a `Range<Index>?` corresponding to the given `NSRange` of
+ /// our UTF-16 representation.
+ func _optionalRange(_ r: NSRange) -> Range<Index>? {
+ if r.location == NSNotFound {
+ return nil
+ }
+ return _range(r)
+ }
+
+ /// Invoke `body` on an `Int` buffer. If `index` was converted from
+ /// non-`nil`, convert the buffer to an `Index` and write it into the
+ /// memory referred to by `index`
+ func _withOptionalOutParameter<Result>(
+ _ index: UnsafeMutablePointer<Index>?,
+ _ body: (UnsafeMutablePointer<Int>?) -> Result
+ ) -> Result {
+ var utf16Index: Int = 0
+ let result = (index != nil ? body(&utf16Index) : body(nil))
+ index?.pointee = _index(utf16Index)
+ return result
+ }
+
+ /// Invoke `body` on an `NSRange` buffer. If `range` was converted
+ /// from non-`nil`, convert the buffer to a `Range<Index>` and write
+ /// it into the memory referred to by `range`
+ func _withOptionalOutParameter<Result>(
+ _ range: UnsafeMutablePointer<Range<Index>>?,
+ _ body: (UnsafeMutablePointer<NSRange>?) -> Result
+ ) -> Result {
+ var nsRange = NSRange(location: 0, length: 0)
+ let result = (range != nil ? body(&nsRange) : body(nil))
+ range?.pointee = self._range(nsRange)
+ return result
+ }
+
+ //===--- Instance Methods/Properties-------------------------------------===//
+ //===--------------------------------------------------------------------===//
+
+ //===--- Omitted by agreement during API review 5/20/2014 ---------------===//
+ // @property BOOL boolValue;
+
+ // - (BOOL)canBeConvertedToEncoding:(NSStringEncoding)encoding
+
+ /// Returns a Boolean value that indicates whether the string can be
+ /// converted to the specified encoding without loss of information.
+ ///
+ /// - Parameter encoding: A string encoding.
+ /// - Returns: `true` if the string can be encoded in `encoding` without loss
+ /// of information; otherwise, `false`.
+ public func canBeConverted(to encoding: String.Encoding) -> Bool {
+ return _ns.canBeConverted(to: encoding.rawValue)
+ }
+
+ // @property NSString* capitalizedString
+
+ /// A copy of the string with each word changed to its corresponding
+ /// capitalized spelling.
+ ///
+ /// This property performs the canonical (non-localized) mapping. It is
+ /// suitable for programming operations that require stable results not
+ /// depending on the current locale.
+ ///
+ /// A capitalized string is a string with the first character in each word
+ /// changed to its corresponding uppercase value, and all remaining
+ /// characters set to their corresponding lowercase values. A "word" is any
+ /// sequence of characters delimited by spaces, tabs, or line terminators.
+ /// Some common word delimiting punctuation isn't considered, so this
+ /// property may not generally produce the desired results for multiword
+ /// strings. See the `getLineStart(_:end:contentsEnd:for:)` method for
+ /// additional information.
+ ///
+ /// Case transformations aren’t guaranteed to be symmetrical or to produce
+ /// strings of the same lengths as the originals.
+ public var capitalized: String {
+ return _ns.capitalized as String
+ }
+
+ // @property (readonly, copy) NSString *localizedCapitalizedString NS_AVAILABLE(10_11, 9_0);
+
+ /// A capitalized representation of the string that is produced
+ /// using the current locale.
+ @available(OSX 10.11, iOS 9.0, *)
+ public var localizedCapitalized: String {
+ return _ns.localizedCapitalized
+ }
+
+ // - (NSString *)capitalizedStringWithLocale:(Locale *)locale
+
+ /// Returns a capitalized representation of the string
+ /// using the specified locale.
+ public func capitalized(with locale: Locale?) -> String {
+ return _ns.capitalized(with: locale) as String
+ }
+
+ // - (NSComparisonResult)caseInsensitiveCompare:(NSString *)aString
+
+ /// Returns the result of invoking `compare:options:` with
+ /// `NSCaseInsensitiveSearch` as the only option.
+ public func caseInsensitiveCompare<
+ T : StringProtocol
+ >(_ aString: T) -> ComparisonResult {
+ return _ns.caseInsensitiveCompare(aString._ephemeralString)
+ }
+
+ //===--- Omitted by agreement during API review 5/20/2014 ---------------===//
+ // - (unichar)characterAtIndex:(NSUInteger)index
+ //
+ // We have a different meaning for "Character" in Swift, and we are
+ // trying not to expose error-prone UTF-16 integer indexes
+
+ // - (NSString *)
+ // commonPrefixWithString:(NSString *)aString
+ // options:(StringCompareOptions)mask
+
+ /// Returns a string containing characters this string and the
+ /// given string have in common, starting from the beginning of each
+ /// up to the first characters that aren't equivalent.
+ public func commonPrefix<
+ T : StringProtocol
+ >(with aString: T, options: String.CompareOptions = []) -> String {
+ return _ns.commonPrefix(with: aString._ephemeralString, options: options)
+ }
+
+ // - (NSComparisonResult)
+ // compare:(NSString *)aString
+ //
+ // - (NSComparisonResult)
+ // compare:(NSString *)aString options:(StringCompareOptions)mask
+ //
+ // - (NSComparisonResult)
+ // compare:(NSString *)aString options:(StringCompareOptions)mask
+ // range:(NSRange)range
+ //
+ // - (NSComparisonResult)
+ // compare:(NSString *)aString options:(StringCompareOptions)mask
+ // range:(NSRange)range locale:(id)locale
+
+ /// Compares the string using the specified options and
+ /// returns the lexical ordering for the range.
+ public func compare<T : StringProtocol>(
+ _ aString: T,
+ options mask: String.CompareOptions = [],
+ range: Range<Index>? = nil,
+ locale: Locale? = nil
+ ) -> ComparisonResult {
+ // According to Ali Ozer, there may be some real advantage to
+ // dispatching to the minimal selector for the supplied options.
+ // So let's do that; the switch should compile away anyhow.
+ let aString = aString._ephemeralString
+ return locale != nil ? _ns.compare(
+ aString,
+ options: mask,
+ range: _toNSRange(
+ range ?? startIndex..<endIndex
+ ),
+ locale: locale?._bridgeToObjectiveC()
+ )
+
+ : range != nil ? _ns.compare(
+ aString,
+ options: mask,
+ range: _toNSRange(range!)
+ )
+
+ : !mask.isEmpty ? _ns.compare(aString, options: mask)
+
+ : _ns.compare(aString)
+ }
+
+ // - (NSUInteger)
+ // completePathIntoString:(NSString **)outputName
+ // caseSensitive:(BOOL)flag
+ // matchesIntoArray:(NSArray **)outputArray
+ // filterTypes:(NSArray *)filterTypes
+
+ /// Interprets the string as a path in the file system and
+ /// attempts to perform filename completion, returning a numeric
+ /// value that indicates whether a match was possible, and by
+ /// reference the longest path that matches the string.
+ ///
+ /// - Returns: The actual number of matching paths.
+ public func completePath(
+ into outputName: UnsafeMutablePointer<String>? = nil,
+ caseSensitive: Bool,
+ matchesInto outputArray: UnsafeMutablePointer<[String]>? = nil,
+ filterTypes: [String]? = nil
+ ) -> Int {
+#if DEPLOYMENT_RUNTIME_SWIFT
+ var outputNamePlaceholder: String?
+ var outputArrayPlaceholder = [String]()
+ let res = self._ns.completePath(
+ into: &outputNamePlaceholder,
+ caseSensitive: caseSensitive,
+ matchesInto: &outputArrayPlaceholder,
+ filterTypes: filterTypes
+ )
+ if let n = outputNamePlaceholder {
+ outputName?.pointee = n
+ } else {
+ outputName?.pointee = ""
+ }
+ outputArray?.pointee = outputArrayPlaceholder
+ return res
+#else // DEPLOYMENT_RUNTIME_SWIFT
+ var nsMatches: NSArray?
+ var nsOutputName: NSString?
+
+ let result: Int = outputName._withNilOrAddress(of: &nsOutputName) {
+ outputName in outputArray._withNilOrAddress(of: &nsMatches) {
+ outputArray in
+ // FIXME: completePath(...) is incorrectly annotated as requiring
+ // non-optional output parameters. rdar://problem/25494184
+ let outputNonOptionalName = unsafeBitCast(
+ outputName, to: AutoreleasingUnsafeMutablePointer<NSString?>.self)
+ let outputNonOptionalArray = unsafeBitCast(
+ outputArray, to: AutoreleasingUnsafeMutablePointer<NSArray?>.self)
+ return self._ns.completePath(
+ into: outputNonOptionalName,
+ caseSensitive: caseSensitive,
+ matchesInto: outputNonOptionalArray,
+ filterTypes: filterTypes
+ )
+ }
+ }
+
+ if let matches = nsMatches {
+ // Since this function is effectively a bridge thunk, use the
+ // bridge thunk semantics for the NSArray conversion
+ outputArray?.pointee = matches as! [String]
+ }
+
+ if let n = nsOutputName {
+ outputName?.pointee = n as String
+ }
+ return result
+#endif // DEPLOYMENT_RUNTIME_SWIFT
+ }
+
+ // - (NSArray *)
+ // componentsSeparatedByCharactersInSet:(NSCharacterSet *)separator
+
+ /// Returns an array containing substrings from the string
+ /// that have been divided by characters in the given set.
+ public func components(separatedBy separator: CharacterSet) -> [String] {
+ return _ns.components(separatedBy: separator)
+ }
+
+ // - (NSArray *)componentsSeparatedByString:(NSString *)separator
+
+ /// Returns an array containing substrings from the string that have been
+ /// divided by the given separator.
+ ///
+ /// The substrings in the resulting array appear in the same order as the
+ /// original string. Adjacent occurrences of the separator string produce
+ /// empty strings in the result. Similarly, if the string begins or ends
+ /// with the separator, the first or last substring, respectively, is empty.
+ /// The following example shows this behavior:
+ ///
+ /// let list1 = "Karin, Carrie, David"
+ /// let items1 = list1.components(separatedBy: ", ")
+ /// // ["Karin", "Carrie", "David"]
+ ///
+ /// // Beginning with the separator:
+ /// let list2 = ", Norman, Stanley, Fletcher"
+ /// let items2 = list2.components(separatedBy: ", ")
+ /// // ["", "Norman", "Stanley", "Fletcher"
+ ///
+ /// If the list has no separators, the array contains only the original
+ /// string itself.
+ ///
+ /// let name = "Karin"
+ /// let list = name.components(separatedBy: ", ")
+ /// // ["Karin"]
+ ///
+ /// - Parameter separator: The separator string.
+ /// - Returns: An array containing substrings that have been divided from the
+ /// string using `separator`.
+ // FIXME(strings): now when String conforms to Collection, this can be
+ // replaced by split(separator:maxSplits:omittingEmptySubsequences:)
+ public func components<
+ T : StringProtocol
+ >(separatedBy separator: T) -> [String] {
+ return _ns.components(separatedBy: separator._ephemeralString)
+ }
+
+ // - (const char *)cStringUsingEncoding:(NSStringEncoding)encoding
+
+ /// Returns a representation of the string as a C string
+ /// using a given encoding.
+ public func cString(using encoding: String.Encoding) -> [CChar]? {
+ return withExtendedLifetime(_ns) {
+ (s: NSString) -> [CChar]? in
+ _persistCString(s.cString(using: encoding.rawValue))
+ }
+ }
+
+ // - (NSData *)dataUsingEncoding:(NSStringEncoding)encoding
+ //
+ // - (NSData *)
+ // dataUsingEncoding:(NSStringEncoding)encoding
+ // allowLossyConversion:(BOOL)flag
+
+ /// Returns a `Data` containing a representation of
+ /// the `String` encoded using a given encoding.
+ public func data(
+ using encoding: String.Encoding,
+ allowLossyConversion: Bool = false
+ ) -> Data? {
+ return _ns.data(
+ using: encoding.rawValue,
+ allowLossyConversion: allowLossyConversion)
+ }
+
+ // @property NSString* decomposedStringWithCanonicalMapping;
+
+ /// A string created by normalizing the string's contents using Form D.
+ public var decomposedStringWithCanonicalMapping: String {
+ return _ns.decomposedStringWithCanonicalMapping
+ }
+
+ // @property NSString* decomposedStringWithCompatibilityMapping;
+
+ /// A string created by normalizing the string's contents using Form KD.
+ public var decomposedStringWithCompatibilityMapping: String {
+ return _ns.decomposedStringWithCompatibilityMapping
+ }
+
+ //===--- Importing Foundation should not affect String printing ---------===//
+ // Therefore, we're not exposing this:
+ //
+ // @property NSString* description
+
+
+ //===--- Omitted for consistency with API review results 5/20/2014 -----===//
+ // @property double doubleValue;
+
+ // - (void)
+ // enumerateLinesUsing:(void (^)(NSString *line, BOOL *stop))block
+
+ /// Enumerates all the lines in a string.
+ public func enumerateLines(
+ invoking body: @escaping (_ line: String, _ stop: inout Bool) -> Void
+ ) {
+ _ns.enumerateLines {
+ (line: String, stop: UnsafeMutablePointer<ObjCBool>)
+ in
+ var stop_ = false
+ body(line, &stop_)
+ if stop_ {
+ stop.pointee = true
+ }
+ }
+ }
+
+ // @property NSStringEncoding fastestEncoding;
+
+ /// The fastest encoding to which the string can be converted without loss
+ /// of information.
+ public var fastestEncoding: String.Encoding {
+ return String.Encoding(rawValue: _ns.fastestEncoding)
+ }
+
+ // - (BOOL)
+ // getCString:(char *)buffer
+ // maxLength:(NSUInteger)maxBufferCount
+ // encoding:(NSStringEncoding)encoding
+
+ /// Converts the `String`'s content to a given encoding and
+ /// stores them in a buffer.
+ /// - Note: will store a maximum of `min(buffer.count, maxLength)` bytes.
+ public func getCString(
+ _ buffer: inout [CChar], maxLength: Int, encoding: String.Encoding
+ ) -> Bool {
+ return _ns.getCString(&buffer,
+ maxLength: Swift.min(buffer.count, maxLength),
+ encoding: encoding.rawValue)
+ }
+
+ // - (NSUInteger)hash
+
+ /// An unsigned integer that can be used as a hash table address.
+ public var hash: Int {
+ return _ns.hash
}
// - (NSUInteger)lengthOfBytesUsingEncoding:(NSStringEncoding)enc
/// Returns the number of bytes required to store the
/// `String` in a given encoding.
- public func lengthOfBytes(using encoding: Encoding) -> Int {
+ public func lengthOfBytes(using encoding: String.Encoding) -> Int {
return _ns.lengthOfBytes(using: encoding.rawValue)
}
- // - (NSRange)lineRangeForRange:(NSRange)aRange
-
- /// Returns the range of characters representing the line or lines
- /// containing a given range.
- public func lineRange(for aRange: Range<Index>) -> Range<Index> {
- return _range(_ns.lineRange(for: _toNSRange(aRange)))
- }
-
// - (NSComparisonResult)localizedCaseInsensitiveCompare:(NSString *)aString
- /// Compares the string and a given string using a
- /// case-insensitive, localized, comparison.
+ /// Compares the string and the given string using a case-insensitive,
+ /// localized, comparison.
public
- func localizedCaseInsensitiveCompare(_ aString: String) -> ComparisonResult {
- return _ns.localizedCaseInsensitiveCompare(aString)
+ func localizedCaseInsensitiveCompare<
+ T : StringProtocol
+ >(_ aString: T) -> ComparisonResult {
+ return _ns.localizedCaseInsensitiveCompare(aString._ephemeralString)
}
// - (NSComparisonResult)localizedCompare:(NSString *)aString
- /// Compares the string and a given string using a localized
- /// comparison.
- public func localizedCompare(_ aString: String) -> ComparisonResult {
- return _ns.localizedCompare(aString)
+ /// Compares the string and the given string using a localized comparison.
+ public func localizedCompare<
+ T : StringProtocol
+ >(_ aString: T) -> ComparisonResult {
+ return _ns.localizedCompare(aString._ephemeralString)
}
- /// Compares strings as sorted by the Finder.
- public func localizedStandardCompare(_ string: String) -> ComparisonResult {
- return _ns.localizedStandardCompare(string)
+ /// Compares the string and the given string as sorted by the Finder.
+ public func localizedStandardCompare<
+ T : StringProtocol
+ >(_ string: T) -> ComparisonResult {
+ return _ns.localizedStandardCompare(string._ephemeralString)
}
//===--- Omitted for consistency with API review results 5/20/2014 ------===//
@@ -951,6 +878,7 @@
/// A lowercase version of the string that is produced using the current
/// locale.
+ @available(OSX 10.11, iOS 9.0, *)
public var localizedLowercase: String {
return _ns.localizedLowercase
}
@@ -969,53 +897,25 @@
/// Returns the maximum number of bytes needed to store the
/// `String` in a given encoding.
public
- func maximumLengthOfBytes(using encoding: Encoding) -> Int {
+ func maximumLengthOfBytes(using encoding: String.Encoding) -> Int {
return _ns.maximumLengthOfBytes(using: encoding.rawValue)
}
- // - (NSRange)paragraphRangeForRange:(NSRange)aRange
-
- /// Returns the range of characters representing the
- /// paragraph or paragraphs containing a given range.
- public func paragraphRange(for aRange: Range<Index>) -> Range<Index> {
- return _range(_ns.paragraphRange(for: _toNSRange(aRange)))
- }
-
- // @property NSArray* pathComponents
-
- /// Returns an array of NSString objects containing, in
- /// order, each path component of the `String`.
- @available(*, unavailable, message: "Use pathComponents on URL instead.")
- public var pathComponents: [String] {
- return _ns.pathComponents
- }
-
- // @property NSString* pathExtension;
-
- /// Interprets the `String` as a path and returns the
- /// `String`'s extension, if any.
- @available(*, unavailable, message: "Use pathExtension on URL instead.")
- public var pathExtension: String {
- return _ns.pathExtension
- }
-
// @property NSString* precomposedStringWithCanonicalMapping;
- /// Returns a string made by normalizing the `String`'s
- /// contents using Form C.
+ /// A string created by normalizing the string's contents using Form C.
public var precomposedStringWithCanonicalMapping: String {
return _ns.precomposedStringWithCanonicalMapping
}
// @property NSString * precomposedStringWithCompatibilityMapping;
- /// Returns a string made by normalizing the `String`'s
- /// contents using Form KC.
+ /// A string created by normalizing the string's contents using Form KC.
public var precomposedStringWithCompatibilityMapping: String {
return _ns.precomposedStringWithCompatibilityMapping
}
-#if false
+#if !DEPLOYMENT_RUNTIME_SWIFT
// - (id)propertyList
/// Parses the `String` as a text representation of a
@@ -1029,151 +929,41 @@
/// Returns a dictionary object initialized with the keys and
/// values found in the `String`.
- public
- func propertyListFromStringsFileFormat() -> [String : String] {
- return _ns.propertyListFromStringsFileFormat()! as [NSObject : AnyObject]
- as! [String : String]
+ public func propertyListFromStringsFileFormat() -> [String : String] {
+ return _ns.propertyListFromStringsFileFormat() as! [String : String]? ?? [:]
}
#endif
-
- // - (NSRange)rangeOfCharacterFromSet:(NSCharacterSet *)aSet
- //
- // - (NSRange)
- // rangeOfCharacterFromSet:(NSCharacterSet *)aSet
- // options:(StringCompareOptions)mask
- //
- // - (NSRange)
- // rangeOfCharacterFromSet:(NSCharacterSet *)aSet
- // options:(StringCompareOptions)mask
- // range:(NSRange)aRange
-
- /// Finds and returns the range in the `String` of the first
- /// character from a given character set found in a given range with
- /// given options.
- public func rangeOfCharacter(
- from aSet: CharacterSet,
- options mask: CompareOptions = [],
- range aRange: Range<Index>? = nil
- ) -> Range<Index>? {
- return _optionalRange(
- _ns.rangeOfCharacter(
- from: aSet,
- options: mask,
- range: _toNSRange(
- aRange ?? self.startIndex..<self.endIndex
- )
- )
- )
- }
-
- // - (NSRange)rangeOfComposedCharacterSequenceAtIndex:(NSUInteger)anIndex
-
- /// Returns the range in the `String` of the composed
- /// character sequence located at a given index.
- public
- func rangeOfComposedCharacterSequence(at anIndex: Index) -> Range<Index> {
- return _range(
- _ns.rangeOfComposedCharacterSequence(at: anIndex.encodedOffset))
- }
-
- // - (NSRange)rangeOfComposedCharacterSequencesForRange:(NSRange)range
-
- /// Returns the range in the string of the composed character
- /// sequences for a given range.
- public func rangeOfComposedCharacterSequences(
- for range: Range<Index>
- ) -> Range<Index> {
- // Theoretically, this will be the identity function. In practice
- // I think users will be able to observe differences in the input
- // and output ranges due (if nothing else) to locale changes
- return _range(
- _ns.rangeOfComposedCharacterSequences(for: _toNSRange(range)))
- }
-
- // - (NSRange)rangeOfString:(NSString *)aString
- //
- // - (NSRange)
- // rangeOfString:(NSString *)aString options:(StringCompareOptions)mask
- //
- // - (NSRange)
- // rangeOfString:(NSString *)aString
- // options:(StringCompareOptions)mask
- // range:(NSRange)aRange
- //
- // - (NSRange)
- // rangeOfString:(NSString *)aString
- // options:(StringCompareOptions)mask
- // range:(NSRange)searchRange
- // locale:(Locale *)locale
-
- /// Finds and returns the range of the first occurrence of a
- /// given string within a given range of the `String`, subject to
- /// given options, using the specified locale, if any.
- public func range(
- of aString: String,
- options mask: CompareOptions = [],
- range searchRange: Range<Index>? = nil,
- locale: Locale? = nil
- ) -> Range<Index>? {
- return _optionalRange(
- locale != nil ? _ns.range(
- of: aString,
- options: mask,
- range: _toNSRange(
- searchRange ?? self.startIndex..<self.endIndex
- ),
- locale: locale
- )
- : searchRange != nil ? _ns.range(
- of: aString, options: mask, range: _toNSRange(searchRange!)
- )
- : !mask.isEmpty ? _ns.range(of: aString, options: mask)
- : _ns.range(of: aString)
- )
- }
// - (BOOL)localizedStandardContainsString:(NSString *)str NS_AVAILABLE(10_11, 9_0);
- /// Returns `true` if `self` contains `string`, taking the current locale
- /// into account.
+ /// Returns a Boolean value indicating whether the string contains the given
+ /// string, taking the current locale into account.
///
/// This is the most appropriate method for doing user-level string searches,
/// similar to how searches are done generally in the system. The search is
/// locale-aware, case and diacritic insensitive. The exact list of search
/// options applied may change over time.
- public func localizedStandardContains(_ string: String) -> Bool {
- return _ns.localizedStandardContains(string)
- }
-
- // - (NSRange)localizedStandardRangeOfString:(NSString *)str NS_AVAILABLE(10_11, 9_0);
-
- /// Finds and returns the range of the first occurrence of a given string,
- /// taking the current locale into account. Returns `nil` if the string was
- /// not found.
- ///
- /// This is the most appropriate method for doing user-level string searches,
- /// similar to how searches are done generally in the system. The search is
- /// locale-aware, case and diacritic insensitive. The exact list of search
- /// options applied may change over time.
- public func localizedStandardRange(of string: String) -> Range<Index>? {
- return _optionalRange(_ns.localizedStandardRange(of: string))
+ @available(OSX 10.11, iOS 9.0, *)
+ public func localizedStandardContains<
+ T : StringProtocol
+ >(_ string: T) -> Bool {
+ return _ns.localizedStandardContains(string._ephemeralString)
}
// @property NSStringEncoding smallestEncoding;
- /// Returns the smallest encoding to which the `String` can
- /// be converted without loss of information.
- public var smallestEncoding: Encoding {
- return Encoding(rawValue: _ns.smallestEncoding)
+ /// The smallest encoding to which the string can be converted without
+ /// loss of information.
+ public var smallestEncoding: String.Encoding {
+ return String.Encoding(rawValue: _ns.smallestEncoding)
}
// - (NSString *)
// stringByAddingPercentEncodingWithAllowedCharacters:
// (NSCharacterSet *)allowedCharacters
- /// Returns a new string made from the `String` by replacing
- /// all characters not in the specified set with percent encoded
- /// characters.
+ /// Returns a new string created by replacing all characters in the string
+ /// not in the specified set with percent encoded characters.
public func addingPercentEncoding(
withAllowedCharacters allowedCharacters: CharacterSet
) -> String? {
@@ -1193,90 +983,31 @@
// - (NSString *)stringByAppendingFormat:(NSString *)format, ...
- /// Returns a string made by appending to the `String` a
- /// string constructed from a given format string and the following
- /// arguments.
- public func appendingFormat(
- _ format: String, _ arguments: CVarArg...
+ /// Returns a string created by appending a string constructed from a given
+ /// format string and the following arguments.
+ public func appendingFormat<
+ T : StringProtocol
+ >(
+ _ format: T, _ arguments: CVarArg...
) -> String {
return _ns.appending(
- String(format: format, arguments: arguments))
- }
-
- // - (NSString *)stringByAppendingPathComponent:(NSString *)aString
-
- /// Returns a new string made by appending to the `String` a given string.
- @available(*, unavailable, message: "Use appendingPathComponent on URL instead.")
- public func appendingPathComponent(_ aString: String) -> String {
- return _ns.appendingPathComponent(aString)
- }
-
- // - (NSString *)stringByAppendingPathExtension:(NSString *)ext
-
- /// Returns a new string made by appending to the `String` an
- /// extension separator followed by a given extension.
- @available(*, unavailable, message: "Use appendingPathExtension on URL instead.")
- public func appendingPathExtension(_ ext: String) -> String? {
- // FIXME: This method can return nil in practice, for example when self is
- // an empty string. OTOH, this is not documented, documentation says that
- // it always returns a string.
- //
- // <rdar://problem/17902469> -[NSString stringByAppendingPathExtension] can
- // return nil
- return _ns.appendingPathExtension(ext)
+ String(format: format._ephemeralString, arguments: arguments))
}
// - (NSString *)stringByAppendingString:(NSString *)aString
- /// Returns a new string made by appending a given string to
- /// the `String`.
- public func appending(_ aString: String) -> String {
- return _ns.appending(aString)
- }
-
- // @property NSString* stringByDeletingLastPathComponent;
-
- /// Returns a new string made by deleting the last path
- /// component from the `String`, along with any final path
- /// separator.
- @available(*, unavailable, message: "Use deletingLastPathComponent on URL instead.")
- public var deletingLastPathComponent: String {
- return _ns.deletingLastPathComponent
- }
-
- // @property NSString* stringByDeletingPathExtension;
-
- /// Returns a new string made by deleting the extension (if
- /// any, and only the last) from the `String`.
- @available(*, unavailable, message: "Use deletingPathExtension on URL instead.")
- public var deletingPathExtension: String {
- return _ns.deletingPathExtension
- }
-
- // @property NSString* stringByExpandingTildeInPath;
-
- /// Returns a new string made by expanding the initial
- /// component of the `String` to its full path value.
- @available(*, unavailable, message: "Use expandingTildeInPath on NSString instead.")
- public var expandingTildeInPath: String {
- return _ns.expandingTildeInPath
- }
-
- // - (NSString *)
- // stringByFoldingWithOptions:(StringCompareOptions)options
- // locale:(Locale *)locale
-
- @available(*, unavailable, renamed: "folding(options:locale:)")
- public func folding(
- _ options: CompareOptions = [], locale: Locale?
- ) -> String {
- return folding(options: options, locale: locale)
+ /// Returns a new string created by appending the given string.
+ // FIXME(strings): shouldn't it be deprecated in favor of `+`?
+ public func appending<
+ T : StringProtocol
+ >(_ aString: T) -> String {
+ return _ns.appending(aString._ephemeralString)
}
/// Returns a string with the given character folding options
/// applied.
public func folding(
- options: CompareOptions = [], locale: Locale?
+ options: String.CompareOptions = [], locale: Locale?
) -> String {
return _ns.folding(options: options, locale: locale)
}
@@ -1288,20 +1019,23 @@
/// Returns a new string formed from the `String` by either
/// removing characters from the end, or by appending as many
/// occurrences as necessary of a given pad string.
- public func padding(
+ public func padding<
+ T : StringProtocol
+ >(
toLength newLength: Int,
- withPad padString: String,
+ withPad padString: T,
startingAt padIndex: Int
) -> String {
return _ns.padding(
- toLength: newLength, withPad: padString, startingAt: padIndex)
+ toLength: newLength,
+ withPad: padString._ephemeralString,
+ startingAt: padIndex)
}
// @property NSString* stringByRemovingPercentEncoding;
- /// Returns a new string made from the `String` by replacing
- /// all percent encoded sequences with the matching UTF-8
- /// characters.
+ /// A new string made from the string by replacing all percent encoded
+ /// sequences with the matching UTF-8 characters.
public var removingPercentEncoding: String? {
return _ns.removingPercentEncoding
}
@@ -1312,10 +1046,12 @@
/// Returns a new string in which the characters in a
/// specified range of the `String` are replaced by a given string.
- public func replacingCharacters(
- in range: Range<Index>, with replacement: String
- ) -> String {
- return _ns.replacingCharacters(in: _toNSRange(range), with: replacement)
+ public func replacingCharacters<
+ T : StringProtocol, R : RangeExpression
+ >(in range: R, with replacement: T) -> String where R.Bound == Index {
+ return _ns.replacingCharacters(
+ in: _toNSRange(range.relative(to: self)),
+ with: replacement._ephemeralString)
}
// - (NSString *)
@@ -1329,43 +1065,46 @@
// range:(NSRange)searchRange
/// Returns a new string in which all occurrences of a target
- /// string in a specified range of the `String` are replaced by
+ /// string in a specified range of the string are replaced by
/// another given string.
- public func replacingOccurrences(
- of target: String,
- with replacement: String,
- options: CompareOptions = [],
+ public func replacingOccurrences<
+ Target : StringProtocol,
+ Replacement : StringProtocol
+ >(
+ of target: Target,
+ with replacement: Replacement,
+ options: String.CompareOptions = [],
range searchRange: Range<Index>? = nil
) -> String {
+ let target = target._ephemeralString
+ let replacement = replacement._ephemeralString
return (searchRange != nil) || (!options.isEmpty)
? _ns.replacingOccurrences(
of: target,
with: replacement,
options: options,
range: _toNSRange(
- searchRange ?? self.startIndex..<self.endIndex
+ searchRange ?? startIndex..<endIndex
)
)
: _ns.replacingOccurrences(of: target, with: replacement)
}
- // @property NSString* stringByResolvingSymlinksInPath;
+#if !DEPLOYMENT_RUNTIME_SWIFT
+ // - (NSString *)
+ // stringByReplacingPercentEscapesUsingEncoding:(NSStringEncoding)encoding
- /// Returns a new string made from the `String` by resolving
- /// all symbolic links and standardizing path.
- @available(*, unavailable, message: "Use resolvingSymlinksInPath on URL instead.")
- public var resolvingSymlinksInPath: String {
- return _ns.resolvingSymlinksInPath
+ /// Returns a new string made by replacing in the `String`
+ /// all percent escapes with the matching characters as determined
+ /// by a given encoding.
+ @available(swift, deprecated: 3.0, obsoleted: 4.0,
+ message: "Use removingPercentEncoding instead, which always uses the recommended UTF-8 encoding.")
+ public func replacingPercentEscapes(
+ using encoding: String.Encoding
+ ) -> String? {
+ return _ns.replacingPercentEscapes(using: encoding.rawValue)
}
-
- // @property NSString* stringByStandardizingPath;
-
- /// Returns a new string made by removing extraneous path
- /// components from the `String`.
- @available(*, unavailable, message: "Use standardizingPath on URL instead.")
- public var standardizingPath: String {
- return _ns.standardizingPath
- }
+#endif
// - (NSString *)stringByTrimmingCharactersInSet:(NSCharacterSet *)set
@@ -1375,43 +1114,11 @@
return _ns.trimmingCharacters(in: set)
}
- // - (NSArray *)stringsByAppendingPaths:(NSArray *)paths
-
- /// Returns an array of strings made by separately appending
- /// to the `String` each string in a given array.
- @available(*, unavailable, message: "Map over paths with appendingPathComponent instead.")
- public func strings(byAppendingPaths paths: [String]) -> [String] {
- fatalError("This function is not available")
- }
-
- // - (NSString *)substringFromIndex:(NSUInteger)anIndex
-
- /// Returns a new string containing the characters of the
- /// `String` from the one at a given index to the end.
- public func substring(from index: Index) -> String {
- return _ns.substring(from: index.encodedOffset)
- }
-
- // - (NSString *)substringToIndex:(NSUInteger)anIndex
-
- /// Returns a new string containing the characters of the
- /// `String` up to, but not including, the one at a given index.
- public func substring(to index: Index) -> String {
- return _ns.substring(to: index.encodedOffset)
- }
-
- // - (NSString *)substringWithRange:(NSRange)aRange
-
- /// Returns a string object containing the characters of the
- /// `String` that lie within a given range.
- public func substring(with aRange: Range<Index>) -> String {
- return _ns.substring(with: _toNSRange(aRange))
- }
-
// @property (readonly, copy) NSString *localizedUppercaseString NS_AVAILABLE(10_11, 9_0);
/// An uppercase version of the string that is produced using the current
/// locale.
+ @available(OSX 10.11, iOS 9.0, *)
public var localizedUppercase: String {
return _ns.localizedUppercase as String
}
@@ -1436,12 +1143,16 @@
/// Writes the contents of the `String` to a file at a given
/// path using a given encoding.
- public func write(
- toFile path: String, atomically useAuxiliaryFile:Bool,
- encoding enc: Encoding
+ public func write<
+ T : StringProtocol
+ >(
+ toFile path: T, atomically useAuxiliaryFile: Bool,
+ encoding enc: String.Encoding
) throws {
- try self._ns.write(
- toFile: path, atomically: useAuxiliaryFile, encoding: enc.rawValue)
+ try _ns.write(
+ toFile: path._ephemeralString,
+ atomically: useAuxiliaryFile,
+ encoding: enc.rawValue)
}
// - (BOOL)
@@ -1454,23 +1165,458 @@
/// by url using the specified encoding.
public func write(
to url: URL, atomically useAuxiliaryFile: Bool,
- encoding enc: Encoding
+ encoding enc: String.Encoding
) throws {
- try self._ns.write(
+ try _ns.write(
to: url, atomically: useAuxiliaryFile, encoding: enc.rawValue)
}
// - (nullable NSString *)stringByApplyingTransform:(NSString *)transform reverse:(BOOL)reverse NS_AVAILABLE(10_11, 9_0);
+#if !DEPLOYMENT_RUNTIME_SWIFT
/// Perform string transliteration.
-#if false
+ @available(OSX 10.11, iOS 9.0, *)
public func applyingTransform(
_ transform: StringTransform, reverse: Bool
) -> String? {
return _ns.applyingTransform(transform, reverse: reverse)
}
+
+ // - (void)
+ // enumerateLinguisticTagsInRange:(NSRange)range
+ // scheme:(NSString *)tagScheme
+ // options:(LinguisticTaggerOptions)opts
+ // orthography:(Orthography *)orthography
+ // usingBlock:(
+ // void (^)(
+ // NSString *tag, NSRange tokenRange,
+ // NSRange sentenceRange, BOOL *stop)
+ // )block
+
+ /// Performs linguistic analysis on the specified string by
+ /// enumerating the specific range of the string, providing the
+ /// Block with the located tags.
+ public func enumerateLinguisticTags<
+ T : StringProtocol, R : RangeExpression
+ >(
+ in range: R,
+ scheme tagScheme: T,
+ options opts: NSLinguisticTagger.Options = [],
+ orthography: NSOrthography? = nil,
+ invoking body:
+ (String, Range<Index>, Range<Index>, inout Bool) -> Void
+ ) where R.Bound == Index {
+ let range = range.relative(to: self)
+ _ns.enumerateLinguisticTags(
+ in: _toNSRange(range),
+ scheme: tagScheme._ephemeralString,
+ options: opts,
+ orthography: orthography != nil ? orthography! : nil
+ ) {
+ var stop_ = false
+ body($0, self._range($1), self._range($2), &stop_)
+ if stop_ {
+ $3.pointee = true
+ }
+ }
+ }
#endif
-
+
+ // - (void)
+ // enumerateSubstringsInRange:(NSRange)range
+ // options:(NSStringEnumerationOptions)opts
+ // usingBlock:(
+ // void (^)(
+ // NSString *substring,
+ // NSRange substringRange,
+ // NSRange enclosingRange,
+ // BOOL *stop)
+ // )block
+
+ /// Enumerates the substrings of the specified type in the specified range of
+ /// the string.
+ ///
+ /// Mutation of a string value while enumerating its substrings is not
+ /// supported. If you need to mutate a string from within `body`, convert
+ /// your string to an `NSMutableString` instance and then call the
+ /// `enumerateSubstrings(in:options:using:)` method.
+ ///
+ /// - Parameters:
+ /// - range: The range within the string to enumerate substrings.
+ /// - opts: Options specifying types of substrings and enumeration styles.
+ /// If `opts` is omitted or empty, `body` is called a single time with
+ /// the range of the string specified by `range`.
+ /// - body: The closure executed for each substring in the enumeration. The
+ /// closure takes four arguments:
+ /// - The enumerated substring. If `substringNotRequired` is included in
+ /// `opts`, this parameter is `nil` for every execution of the
+ /// closure.
+ /// - The range of the enumerated substring in the string that
+ /// `enumerate(in:options:_:)` was called on.
+ /// - The range that includes the substring as well as any separator or
+ /// filler characters that follow. For instance, for lines,
+ /// `enclosingRange` contains the line terminators. The enclosing
+ /// range for the first string enumerated also contains any characters
+ /// that occur before the string. Consecutive enclosing ranges are
+ /// guaranteed not to overlap, and every single character in the
+ /// enumerated range is included in one and only one enclosing range.
+ /// - An `inout` Boolean value that the closure can use to stop the
+ /// enumeration by setting `stop = true`.
+ public func enumerateSubstrings<
+ R : RangeExpression
+ >(
+ in range: R,
+ options opts: String.EnumerationOptions = [],
+ _ body: @escaping (
+ _ substring: String?, _ substringRange: Range<Index>,
+ _ enclosingRange: Range<Index>, inout Bool
+ ) -> Void
+ ) where R.Bound == Index {
+ _ns.enumerateSubstrings(
+ in: _toNSRange(range.relative(to: self)), options: opts) {
+ var stop_ = false
+
+ body($0,
+ self._range($1),
+ self._range($2),
+ &stop_)
+
+ if stop_ {
+ UnsafeMutablePointer($3).pointee = true
+ }
+ }
+ }
+
+ //===--- Omitted for consistency with API review results 5/20/2014 ------===//
+ // @property float floatValue;
+
+ // - (BOOL)
+ // getBytes:(void *)buffer
+ // maxLength:(NSUInteger)maxBufferCount
+ // usedLength:(NSUInteger*)usedBufferCount
+ // encoding:(NSStringEncoding)encoding
+ // options:(StringEncodingConversionOptions)options
+ // range:(NSRange)range
+ // remainingRange:(NSRangePointer)leftover
+
+ /// Writes the given `range` of characters into `buffer` in a given
+ /// `encoding`, without any allocations. Does not NULL-terminate.
+ ///
+ /// - Parameter buffer: A buffer into which to store the bytes from
+ /// the receiver. The returned bytes are not NUL-terminated.
+ ///
+ /// - Parameter maxBufferCount: The maximum number of bytes to write
+ /// to buffer.
+ ///
+ /// - Parameter usedBufferCount: The number of bytes used from
+ /// buffer. Pass `nil` if you do not need this value.
+ ///
+ /// - Parameter encoding: The encoding to use for the returned bytes.
+ ///
+ /// - Parameter options: A mask to specify options to use for
+ /// converting the receiver's contents to `encoding` (if conversion
+ /// is necessary).
+ ///
+ /// - Parameter range: The range of characters in the receiver to get.
+ ///
+ /// - Parameter leftover: The remaining range. Pass `nil` If you do
+ /// not need this value.
+ ///
+ /// - Returns: `true` iff some characters were converted.
+ ///
+ /// - Note: Conversion stops when the buffer fills or when the
+ /// conversion isn't possible due to the chosen encoding.
+ ///
+ /// - Note: will get a maximum of `min(buffer.count, maxLength)` bytes.
+ public func getBytes<
+ R : RangeExpression
+ >(
+ _ buffer: inout [UInt8],
+ maxLength maxBufferCount: Int,
+ usedLength usedBufferCount: UnsafeMutablePointer<Int>,
+ encoding: String.Encoding,
+ options: String.EncodingConversionOptions = [],
+ range: R,
+ remaining leftover: UnsafeMutablePointer<Range<Index>>
+ ) -> Bool where R.Bound == Index {
+ return _withOptionalOutParameter(leftover) {
+ self._ns.getBytes(
+ &buffer,
+ maxLength: Swift.min(buffer.count, maxBufferCount),
+ usedLength: usedBufferCount,
+ encoding: encoding.rawValue,
+ options: options,
+ range: _toNSRange(range.relative(to: self)),
+ remaining: $0)
+ }
+ }
+
+ // - (void)
+ // getLineStart:(NSUInteger *)startIndex
+ // end:(NSUInteger *)lineEndIndex
+ // contentsEnd:(NSUInteger *)contentsEndIndex
+ // forRange:(NSRange)aRange
+
+ /// Returns by reference the beginning of the first line and
+ /// the end of the last line touched by the given range.
+ public func getLineStart<
+ R : RangeExpression
+ >(
+ _ start: UnsafeMutablePointer<Index>,
+ end: UnsafeMutablePointer<Index>,
+ contentsEnd: UnsafeMutablePointer<Index>,
+ for range: R
+ ) where R.Bound == Index {
+ _withOptionalOutParameter(start) {
+ start in self._withOptionalOutParameter(end) {
+ end in self._withOptionalOutParameter(contentsEnd) {
+ contentsEnd in self._ns.getLineStart(
+ start, end: end,
+ contentsEnd: contentsEnd,
+ for: _toNSRange(range.relative(to: self)))
+ }
+ }
+ }
+ }
+
+ // - (void)
+ // getParagraphStart:(NSUInteger *)startIndex
+ // end:(NSUInteger *)endIndex
+ // contentsEnd:(NSUInteger *)contentsEndIndex
+ // forRange:(NSRange)aRange
+
+ /// Returns by reference the beginning of the first paragraph
+ /// and the end of the last paragraph touched by the given range.
+ public func getParagraphStart<
+ R : RangeExpression
+ >(
+ _ start: UnsafeMutablePointer<Index>,
+ end: UnsafeMutablePointer<Index>,
+ contentsEnd: UnsafeMutablePointer<Index>,
+ for range: R
+ ) where R.Bound == Index {
+ _withOptionalOutParameter(start) {
+ start in self._withOptionalOutParameter(end) {
+ end in self._withOptionalOutParameter(contentsEnd) {
+ contentsEnd in self._ns.getParagraphStart(
+ start, end: end,
+ contentsEnd: contentsEnd,
+ for: _toNSRange(range.relative(to: self)))
+ }
+ }
+ }
+ }
+
+ //===--- Already provided by core Swift ---------------------------------===//
+ // - (instancetype)initWithString:(NSString *)aString
+
+ //===--- Initializers that can fail dropped for factory functions -------===//
+ // - (instancetype)initWithUTF8String:(const char *)bytes
+
+ //===--- Omitted for consistency with API review results 5/20/2014 ------===//
+ // @property NSInteger integerValue;
+ // @property Int intValue;
+
+ //===--- Omitted by apparent agreement during API review 5/20/2014 ------===//
+ // @property BOOL absolutePath;
+ // - (BOOL)isEqualToString:(NSString *)aString
+
+ // - (NSRange)lineRangeForRange:(NSRange)aRange
+
+ /// Returns the range of characters representing the line or lines
+ /// containing a given range.
+ public func lineRange<
+ R : RangeExpression
+ >(for aRange: R) -> Range<Index> where R.Bound == Index {
+ return _range(_ns.lineRange(for: _toNSRange(aRange.relative(to: self))))
+ }
+
+#if !DEPLOYMENT_RUNTIME_SWIFT
+ // - (NSArray *)
+ // linguisticTagsInRange:(NSRange)range
+ // scheme:(NSString *)tagScheme
+ // options:(LinguisticTaggerOptions)opts
+ // orthography:(Orthography *)orthography
+ // tokenRanges:(NSArray**)tokenRanges
+
+ /// Returns an array of linguistic tags for the specified
+ /// range and requested tags within the receiving string.
+ public func linguisticTags<
+ T : StringProtocol, R : RangeExpression
+ >(
+ in range: R,
+ scheme tagScheme: T,
+ options opts: NSLinguisticTagger.Options = [],
+ orthography: NSOrthography? = nil,
+ tokenRanges: UnsafeMutablePointer<[Range<Index>]>? = nil // FIXME:Can this be nil?
+ ) -> [String] where R.Bound == Index {
+ var nsTokenRanges: NSArray?
+ let result = tokenRanges._withNilOrAddress(of: &nsTokenRanges) {
+ self._ns.linguisticTags(
+ in: _toNSRange(range.relative(to: self)),
+ scheme: tagScheme._ephemeralString,
+ options: opts,
+ orthography: orthography,
+ tokenRanges: $0) as NSArray
+ }
+
+ if nsTokenRanges != nil {
+ tokenRanges?.pointee = (nsTokenRanges! as [AnyObject]).map {
+ self._range($0.rangeValue)
+ }
+ }
+
+ return result as! [String]
+ }
+
+ // - (NSRange)paragraphRangeForRange:(NSRange)aRange
+
+ /// Returns the range of characters representing the
+ /// paragraph or paragraphs containing a given range.
+ public func paragraphRange<
+ R : RangeExpression
+ >(for aRange: R) -> Range<Index> where R.Bound == Index {
+ return _range(
+ _ns.paragraphRange(for: _toNSRange(aRange.relative(to: self))))
+ }
+#endif
+
+ // - (NSRange)rangeOfCharacterFromSet:(NSCharacterSet *)aSet
+ //
+ // - (NSRange)
+ // rangeOfCharacterFromSet:(NSCharacterSet *)aSet
+ // options:(StringCompareOptions)mask
+ //
+ // - (NSRange)
+ // rangeOfCharacterFromSet:(NSCharacterSet *)aSet
+ // options:(StringCompareOptions)mask
+ // range:(NSRange)aRange
+
+ /// Finds and returns the range in the `String` of the first
+ /// character from a given character set found in a given range with
+ /// given options.
+ public func rangeOfCharacter(
+ from aSet: CharacterSet,
+ options mask: String.CompareOptions = [],
+ range aRange: Range<Index>? = nil
+ ) -> Range<Index>? {
+ return _optionalRange(
+ _ns.rangeOfCharacter(
+ from: aSet,
+ options: mask,
+ range: _toNSRange(
+ aRange ?? startIndex..<endIndex
+ )
+ )
+ )
+ }
+
+ // - (NSRange)rangeOfComposedCharacterSequenceAtIndex:(NSUInteger)anIndex
+
+ /// Returns the range in the `String` of the composed
+ /// character sequence located at a given index.
+ public
+ func rangeOfComposedCharacterSequence(at anIndex: Index) -> Range<Index> {
+ return _range(
+ _ns.rangeOfComposedCharacterSequence(at: anIndex.encodedOffset))
+ }
+
+ // - (NSRange)rangeOfComposedCharacterSequencesForRange:(NSRange)range
+
+ /// Returns the range in the string of the composed character
+ /// sequences for a given range.
+ public func rangeOfComposedCharacterSequences<
+ R : RangeExpression
+ >(
+ for range: R
+ ) -> Range<Index> where R.Bound == Index {
+ // Theoretically, this will be the identity function. In practice
+ // I think users will be able to observe differences in the input
+ // and output ranges due (if nothing else) to locale changes
+ return _range(
+ _ns.rangeOfComposedCharacterSequences(
+ for: _toNSRange(range.relative(to: self))))
+ }
+
+ // - (NSRange)rangeOfString:(NSString *)aString
+ //
+ // - (NSRange)
+ // rangeOfString:(NSString *)aString options:(StringCompareOptions)mask
+ //
+ // - (NSRange)
+ // rangeOfString:(NSString *)aString
+ // options:(StringCompareOptions)mask
+ // range:(NSRange)aRange
+ //
+ // - (NSRange)
+ // rangeOfString:(NSString *)aString
+ // options:(StringCompareOptions)mask
+ // range:(NSRange)searchRange
+ // locale:(Locale *)locale
+
+ /// Finds and returns the range of the first occurrence of a
+ /// given string within a given range of the `String`, subject to
+ /// given options, using the specified locale, if any.
+ public func range<
+ T : StringProtocol
+ >(
+ of aString: T,
+ options mask: String.CompareOptions = [],
+ range searchRange: Range<Index>? = nil,
+ locale: Locale? = nil
+ ) -> Range<Index>? {
+ let aString = aString._ephemeralString
+ return _optionalRange(
+ locale != nil ? _ns.range(
+ of: aString,
+ options: mask,
+ range: _toNSRange(
+ searchRange ?? startIndex..<endIndex
+ ),
+ locale: locale
+ )
+ : searchRange != nil ? _ns.range(
+ of: aString, options: mask, range: _toNSRange(searchRange!)
+ )
+ : !mask.isEmpty ? _ns.range(of: aString, options: mask)
+ : _ns.range(of: aString)
+ )
+ }
+
+ // - (NSRange)localizedStandardRangeOfString:(NSString *)str NS_AVAILABLE(10_11, 9_0);
+
+ /// Finds and returns the range of the first occurrence of a given string,
+ /// taking the current locale into account. Returns `nil` if the string was
+ /// not found.
+ ///
+ /// This is the most appropriate method for doing user-level string searches,
+ /// similar to how searches are done generally in the system. The search is
+ /// locale-aware, case and diacritic insensitive. The exact list of search
+ /// options applied may change over time.
+ @available(OSX 10.11, iOS 9.0, *)
+ public func localizedStandardRange<
+ T : StringProtocol
+ >(of string: T) -> Range<Index>? {
+ return _optionalRange(
+ _ns.localizedStandardRange(of: string._ephemeralString))
+ }
+
+#if !DEPLOYMENT_RUNTIME_SWIFT
+ // - (NSString *)
+ // stringByAddingPercentEscapesUsingEncoding:(NSStringEncoding)encoding
+
+ /// Returns a representation of the `String` using a given
+ /// encoding to determine the percent escapes necessary to convert
+ /// the `String` into a legal URL string.
+ @available(swift, deprecated: 3.0, obsoleted: 4.0,
+ message: "Use addingPercentEncoding(withAllowedCharacters:) instead, which always uses the recommended UTF-8 encoding, and which encodes for a specific URL component or subcomponent since each URL component or subcomponent has different rules for what characters are valid.")
+ public func addingPercentEscapes(
+ using encoding: String.Encoding
+ ) -> String? {
+ return _ns.addingPercentEscapes(using: encoding.rawValue)
+ }
+#endif
+
//===--- From the 10.10 release notes; not in public documentation ------===//
// No need to make these unavailable on earlier OSes, since they can
// forward trivially to rangeOfString.
@@ -1479,44 +1625,233 @@
/// `self` by case-sensitive, non-literal search.
///
/// Equivalent to `self.rangeOfString(other) != nil`
- public func contains(_ other: String) -> Bool {
+ public func contains<T : StringProtocol>(_ other: T) -> Bool {
let r = self.range(of: other) != nil
if #available(OSX 10.10, iOS 8.0, *) {
- _sanityCheck(r == _ns.contains(other))
+ _sanityCheck(r == _ns.contains(other._ephemeralString))
}
return r
}
-
- /// Returns `true` iff `other` is non-empty and contained within
- /// `self` by case-insensitive, non-literal search, taking into
- /// account the current locale.
+
+ /// Returns a Boolean value indicating whether the given string is non-empty
+ /// and contained within this string by case-insensitive, non-literal
+ /// search, taking into account the current locale.
///
- /// Locale-independent case-insensitive operation, and other needs,
- /// can be achieved by calling
- /// `rangeOfString(_:options:_, range:_locale:_)`.
+ /// Locale-independent case-insensitive operation, and other needs, can be
+ /// achieved by calling `range(of:options:range:locale:)`.
///
- /// Equivalent to
+ /// Equivalent to:
///
- /// self.rangeOf(
- /// other, options: .CaseInsensitiveSearch,
- /// locale: Locale.current) != nil
- public func localizedCaseInsensitiveContains(_ other: String) -> Bool {
+ /// range(of: other, options: .caseInsensitiveSearch,
+ /// locale: Locale.current) != nil
+ public func localizedCaseInsensitiveContains<
+ T : StringProtocol
+ >(_ other: T) -> Bool {
let r = self.range(
of: other, options: .caseInsensitive, locale: Locale.current
) != nil
if #available(OSX 10.10, iOS 8.0, *) {
- _sanityCheck(r == _ns.localizedCaseInsensitiveContains(other))
+ _sanityCheck(r ==
+ _ns.localizedCaseInsensitiveContains(other._ephemeralString))
}
return r
}
}
+// Deprecated slicing
+extension StringProtocol where Index == String.Index {
+ // - (NSString *)substringFromIndex:(NSUInteger)anIndex
+
+ /// Returns a new string containing the characters of the
+ /// `String` from the one at a given index to the end.
+ @available(swift, deprecated: 4.0,
+ message: "Please use String slicing subscript with a 'partial range from' operator.")
+ public func substring(from index: Index) -> String {
+ return _ns.substring(from: index.encodedOffset)
+ }
+
+ // - (NSString *)substringToIndex:(NSUInteger)anIndex
+
+ /// Returns a new string containing the characters of the
+ /// `String` up to, but not including, the one at a given index.
+ @available(swift, deprecated: 4.0,
+ message: "Please use String slicing subscript with a 'partial range upto' operator.")
+ public func substring(to index: Index) -> String {
+ return _ns.substring(to: index.encodedOffset)
+ }
+
+ // - (NSString *)substringWithRange:(NSRange)aRange
+
+ /// Returns a string object containing the characters of the
+ /// `String` that lie within a given range.
+ @available(swift, deprecated: 4.0,
+ message: "Please use String slicing subscript.")
+ public func substring(with aRange: Range<Index>) -> String {
+ return _ns.substring(with: _toNSRange(aRange))
+ }
+}
+
+extension StringProtocol {
+ // - (const char *)fileSystemRepresentation
+
+ /// Returns a file system-specific representation of the `String`.
+ @available(*, unavailable, message: "Use getFileSystemRepresentation on URL instead.")
+ public var fileSystemRepresentation: [CChar] {
+ fatalError("unavailable function can't be called")
+ }
+
+ // - (BOOL)
+ // getFileSystemRepresentation:(char *)buffer
+ // maxLength:(NSUInteger)maxLength
+
+ /// Interprets the `String` as a system-independent path and
+ /// fills a buffer with a C-string in a format and encoding suitable
+ /// for use with file-system calls.
+ /// - Note: will store a maximum of `min(buffer.count, maxLength)` bytes.
+ @available(*, unavailable, message: "Use getFileSystemRepresentation on URL instead.")
+ public func getFileSystemRepresentation(
+ _ buffer: inout [CChar], maxLength: Int) -> Bool {
+ fatalError("unavailable function can't be called")
+ }
+
+ //===--- Kept for consistency with API review results 5/20/2014 ---------===//
+ // We decided to keep pathWithComponents, so keeping this too
+ // @property NSString lastPathComponent;
+
+ /// Returns the last path component of the `String`.
+ @available(*, unavailable, message: "Use lastPathComponent on URL instead.")
+ public var lastPathComponent: String {
+ fatalError("unavailable function can't be called")
+ }
+
+ //===--- Renamed by agreement during API review 5/20/2014 ---------------===//
+ // @property NSUInteger length;
+
+ /// Returns the number of Unicode characters in the `String`.
+ @available(*, unavailable,
+ message: "Take the count of a UTF-16 view instead, i.e. str.utf16.count")
+ public var utf16Count: Int {
+ fatalError("unavailable function can't be called")
+ }
+
+ // @property NSArray* pathComponents
+
+ /// Returns an array of NSString objects containing, in
+ /// order, each path component of the `String`.
+ @available(*, unavailable, message: "Use pathComponents on URL instead.")
+ public var pathComponents: [String] {
+ fatalError("unavailable function can't be called")
+ }
+
+ // @property NSString* pathExtension;
+
+ /// Interprets the `String` as a path and returns the
+ /// `String`'s extension, if any.
+ @available(*, unavailable, message: "Use pathExtension on URL instead.")
+ public var pathExtension: String {
+ fatalError("unavailable function can't be called")
+ }
+
+ // @property NSString *stringByAbbreviatingWithTildeInPath;
+
+ /// Returns a new string that replaces the current home
+ /// directory portion of the current path with a tilde (`~`)
+ /// character.
+ @available(*, unavailable, message: "Use abbreviatingWithTildeInPath on NSString instead.")
+ public var abbreviatingWithTildeInPath: String {
+ fatalError("unavailable function can't be called")
+ }
+
+ // - (NSString *)stringByAppendingPathComponent:(NSString *)aString
+
+ /// Returns a new string made by appending to the `String` a given string.
+ @available(*, unavailable, message: "Use appendingPathComponent on URL instead.")
+ public func appendingPathComponent(_ aString: String) -> String {
+ fatalError("unavailable function can't be called")
+ }
+
+ // - (NSString *)stringByAppendingPathExtension:(NSString *)ext
+
+ /// Returns a new string made by appending to the `String` an
+ /// extension separator followed by a given extension.
+ @available(*, unavailable, message: "Use appendingPathExtension on URL instead.")
+ public func appendingPathExtension(_ ext: String) -> String? {
+ fatalError("unavailable function can't be called")
+ }
+
+ // @property NSString* stringByDeletingLastPathComponent;
+
+ /// Returns a new string made by deleting the last path
+ /// component from the `String`, along with any final path
+ /// separator.
+ @available(*, unavailable, message: "Use deletingLastPathComponent on URL instead.")
+ public var deletingLastPathComponent: String {
+ fatalError("unavailable function can't be called")
+ }
+
+ // @property NSString* stringByDeletingPathExtension;
+
+ /// Returns a new string made by deleting the extension (if
+ /// any, and only the last) from the `String`.
+ @available(*, unavailable, message: "Use deletingPathExtension on URL instead.")
+ public var deletingPathExtension: String {
+ fatalError("unavailable function can't be called")
+ }
+
+ // @property NSString* stringByExpandingTildeInPath;
+
+ /// Returns a new string made by expanding the initial
+ /// component of the `String` to its full path value.
+ @available(*, unavailable, message: "Use expandingTildeInPath on NSString instead.")
+ public var expandingTildeInPath: String {
+ fatalError("unavailable function can't be called")
+ }
+
+ // - (NSString *)
+ // stringByFoldingWithOptions:(StringCompareOptions)options
+ // locale:(Locale *)locale
+
+ @available(*, unavailable, renamed: "folding(options:locale:)")
+ public func folding(
+ _ options: String.CompareOptions = [], locale: Locale?
+ ) -> String {
+ fatalError("unavailable function can't be called")
+ }
+
+ // @property NSString* stringByResolvingSymlinksInPath;
+
+ /// Returns a new string made from the `String` by resolving
+ /// all symbolic links and standardizing path.
+ @available(*, unavailable, message: "Use resolvingSymlinksInPath on URL instead.")
+ public var resolvingSymlinksInPath: String {
+ fatalError("unavailable property")
+ }
+
+ // @property NSString* stringByStandardizingPath;
+
+ /// Returns a new string made by removing extraneous path
+ /// components from the `String`.
+ @available(*, unavailable, message: "Use standardizingPath on URL instead.")
+ public var standardizingPath: String {
+ fatalError("unavailable function can't be called")
+ }
+
+ // - (NSArray *)stringsByAppendingPaths:(NSArray *)paths
+
+ /// Returns an array of strings made by separately appending
+ /// to the `String` each string in a given array.
+ @available(*, unavailable, message: "Map over paths with appendingPathComponent instead.")
+ public func strings(byAppendingPaths paths: [String]) -> [String] {
+ fatalError("unavailable function can't be called")
+ }
+
+}
+
// Pre-Swift-3 method names
extension String {
-
@available(*, unavailable, renamed: "localizedName(of:)")
public static func localizedNameOfStringEncoding(
- _ encoding: Encoding
+ _ encoding: String.Encoding
) -> String {
fatalError("unavailable function can't be called")
}
@@ -1526,8 +1861,20 @@
fatalError("unavailable function can't be called")
}
+ // + (NSString *)pathWithComponents:(NSArray *)components
+
+ /// Returns a string built from the strings in a given array
+ /// by concatenating them with a path separator between each pair.
+ @available(*, unavailable, message: "Use fileURL(withPathComponents:) on URL instead.")
+ public static func path(withComponents components: [String]) -> String {
+ fatalError("unavailable function can't be called")
+ }
+}
+
+extension StringProtocol {
+
@available(*, unavailable, renamed: "canBeConverted(to:)")
- public func canBeConvertedToEncoding(_ encoding: Encoding) -> Bool {
+ public func canBeConvertedToEncoding(_ encoding: String.Encoding) -> Bool {
fatalError("unavailable function can't be called")
}
@@ -1538,7 +1885,7 @@
@available(*, unavailable, renamed: "commonPrefix(with:options:)")
public func commonPrefixWith(
- _ aString: String, options: CompareOptions) -> String {
+ _ aString: String, options: String.CompareOptions) -> String {
fatalError("unavailable function can't be called")
}
@@ -1565,22 +1912,36 @@
}
@available(*, unavailable, renamed: "cString(usingEncoding:)")
- public func cStringUsingEncoding(_ encoding: Encoding) -> [CChar]? {
+ public func cStringUsingEncoding(_ encoding: String.Encoding) -> [CChar]? {
fatalError("unavailable function can't be called")
}
@available(*, unavailable, renamed: "data(usingEncoding:allowLossyConversion:)")
public func dataUsingEncoding(
- _ encoding: Encoding,
+ _ encoding: String.Encoding,
allowLossyConversion: Bool = false
) -> Data? {
fatalError("unavailable function can't be called")
}
+#if !DEPLOYMENT_RUNTIME_SWIFT
+ @available(*, unavailable, renamed: "enumerateLinguisticTags(in:scheme:options:orthography:_:)")
+ public func enumerateLinguisticTagsIn(
+ _ range: Range<Index>,
+ scheme tagScheme: String,
+ options opts: NSLinguisticTagger.Options,
+ orthography: NSOrthography?,
+ _ body:
+ (String, Range<Index>, Range<Index>, inout Bool) -> Void
+ ) {
+ fatalError("unavailable function can't be called")
+ }
+#endif
+
@available(*, unavailable, renamed: "enumerateSubstrings(in:options:_:)")
public func enumerateSubstringsIn(
_ range: Range<Index>,
- options opts: EnumerationOptions = [],
+ options opts: String.EnumerationOptions = [],
_ body: (
_ substring: String?, _ substringRange: Range<Index>,
_ enclosingRange: Range<Index>, inout Bool
@@ -1594,8 +1955,8 @@
_ buffer: inout [UInt8],
maxLength maxBufferCount: Int,
usedLength usedBufferCount: UnsafeMutablePointer<Int>,
- encoding: Encoding,
- options: EncodingConversionOptions = [],
+ encoding: String.Encoding,
+ options: String.EncodingConversionOptions = [],
range: Range<Index>,
remainingRange leftover: UnsafeMutablePointer<Range<Index>>
) -> Bool {
@@ -1623,7 +1984,7 @@
}
@available(*, unavailable, renamed: "lengthOfBytes(using:)")
- public func lengthOfBytesUsingEncoding(_ encoding: Encoding) -> Int {
+ public func lengthOfBytesUsingEncoding(_ encoding: String.Encoding) -> Int {
fatalError("unavailable function can't be called")
}
@@ -1632,6 +1993,19 @@
fatalError("unavailable function can't be called")
}
+#if !DEPLOYMENT_RUNTIME_SWIFT
+ @available(*, unavailable, renamed: "linguisticTags(in:scheme:options:orthography:tokenRanges:)")
+ public func linguisticTagsIn(
+ _ range: Range<Index>,
+ scheme tagScheme: String,
+ options opts: NSLinguisticTagger.Options = [],
+ orthography: NSOrthography? = nil,
+ tokenRanges: UnsafeMutablePointer<[Range<Index>]>? = nil
+ ) -> [String] {
+ fatalError("unavailable function can't be called")
+ }
+#endif
+
@available(*, unavailable, renamed: "lowercased(with:)")
public func lowercaseStringWith(_ locale: Locale?) -> String {
fatalError("unavailable function can't be called")
@@ -1639,7 +2013,7 @@
@available(*, unavailable, renamed: "maximumLengthOfBytes(using:)")
public
- func maximumLengthOfBytesUsingEncoding(_ encoding: Encoding) -> Int {
+ func maximumLengthOfBytesUsingEncoding(_ encoding: String.Encoding) -> Int {
fatalError("unavailable function can't be called")
}
@@ -1651,7 +2025,7 @@
@available(*, unavailable, renamed: "rangeOfCharacter(from:options:range:)")
public func rangeOfCharacterFrom(
_ aSet: CharacterSet,
- options mask: CompareOptions = [],
+ options mask: String.CompareOptions = [],
range aRange: Range<Index>? = nil
) -> Range<Index>? {
fatalError("unavailable function can't be called")
@@ -1673,7 +2047,7 @@
@available(*, unavailable, renamed: "range(of:options:range:locale:)")
public func rangeOf(
_ aString: String,
- options mask: CompareOptions = [],
+ options mask: String.CompareOptions = [],
range searchRange: Range<Index>? = nil,
locale: Locale? = nil
) -> Range<Index>? {
@@ -1694,7 +2068,7 @@
@available(*, unavailable, renamed: "addingPercentEscapes(using:)")
public func addingPercentEscapesUsingEncoding(
- _ encoding: Encoding
+ _ encoding: String.Encoding
) -> String? {
fatalError("unavailable function can't be called")
}
@@ -1712,7 +2086,7 @@
) -> String {
fatalError("unavailable function can't be called")
}
-
+
@available(*, unavailable, renamed: "replacingCharacters(in:with:)")
public func replacingCharactersIn(
_ range: Range<Index>, withString replacement: String
@@ -1724,7 +2098,7 @@
public func replacingOccurrencesOf(
_ target: String,
withString replacement: String,
- options: CompareOptions = [],
+ options: String.CompareOptions = [],
range searchRange: Range<Index>? = nil
) -> String {
fatalError("unavailable function can't be called")
@@ -1732,7 +2106,7 @@
@available(*, unavailable, renamed: "replacingPercentEscapes(usingEncoding:)")
public func replacingPercentEscapesUsingEncoding(
- _ encoding: Encoding
+ _ encoding: String.Encoding
) -> String? {
fatalError("unavailable function can't be called")
}
@@ -1770,7 +2144,7 @@
@available(*, unavailable, renamed: "write(toFile:atomically:encoding:)")
public func writeToFile(
_ path: String, atomically useAuxiliaryFile:Bool,
- encoding enc: Encoding
+ encoding enc: String.Encoding
) throws {
fatalError("unavailable function can't be called")
}
@@ -1778,7 +2152,7 @@
@available(*, unavailable, renamed: "write(to:atomically:encoding:)")
public func writeToURL(
_ url: URL, atomically useAuxiliaryFile: Bool,
- encoding enc: Encoding
+ encoding enc: String.Encoding
) throws {
fatalError("unavailable function can't be called")
}
diff --git a/Foundation/Operation.swift b/Foundation/Operation.swift
index 1c5529f..9c17ad1 100644
--- a/Foundation/Operation.swift
+++ b/Foundation/Operation.swift
@@ -9,13 +9,8 @@
#if DEPLOYMENT_ENABLE_LIBDISPATCH
import Dispatch
-#if os(Linux) || os(Android)
+#endif
import CoreFoundation
-private func pthread_main_np() -> Int32 {
- return _CFIsMainThread() ? 1 : 0
-}
-#endif
-#endif
open class Operation : NSObject {
let lock = NSLock()
@@ -570,7 +565,7 @@
open class var current: OperationQueue? {
#if DEPLOYMENT_ENABLE_LIBDISPATCH
guard let specific = DispatchQueue.getSpecific(key: OperationQueue.OperationQueueKey) else {
- if pthread_main_np() == 1 {
+ if _CFIsMainThread() {
return OperationQueue.main
} else {
return nil
diff --git a/Foundation/Thread.swift b/Foundation/Thread.swift
index 3990db0..340ec7f 100644
--- a/Foundation/Thread.swift
+++ b/Foundation/Thread.swift
@@ -13,29 +13,23 @@
#elseif os(Linux) || CYGWIN
import Glibc
#endif
-
import CoreFoundation
-// for some reason having this take a generic causes a crash...
-private func _compiler_crash_fix(_ key: _CFThreadSpecificKey, _ value: AnyObject?) {
- _CThreadSpecificSet(key, value)
-}
-
internal class NSThreadSpecific<T: NSObject> {
private var key = _CFThreadSpecificKeyCreate()
-
+
internal func get(_ generator: () -> T) -> T {
if let specific = _CFThreadSpecificGet(key) {
return specific as! T
} else {
let value = generator()
- _compiler_crash_fix(key, value)
+ _CThreadSpecificSet(key, value)
return value
}
}
-
+
internal func set(_ value: T) {
- _compiler_crash_fix(key, value)
+ _CThreadSpecificSet(key, value)
}
}
@@ -57,18 +51,33 @@
}
open class Thread : NSObject {
-
+
static internal var _currentThread = NSThreadSpecific<Thread>()
open class var current: Thread {
return Thread._currentThread.get() {
- return Thread(thread: pthread_self())
+ if Thread.isMainThread {
+ return mainThread
+ } else {
+ return Thread(thread: pthread_self())
+ }
}
}
-
- open class var isMainThread: Bool { NSUnimplemented() }
-
+
+ open class var isMainThread: Bool {
+ return _CFIsMainThread()
+ }
+
// !!! NSThread's mainThread property is incorrectly exported as "main", which conflicts with its "main" method.
- open class var mainThread: Thread { NSUnimplemented() }
+ private static let _mainThread: Thread = {
+ var thread = Thread(thread: _CFMainPThread)
+ thread._status = .executing
+ return thread
+ }()
+
+ open class var mainThread: Thread {
+ return _mainThread
+ }
+
/// Alternative API for detached thread creation
/// - Experiment: This is a draft API currently under consideration for official import into Foundation as a suitable alternative to creation via selector
@@ -77,11 +86,11 @@
let t = Thread(block: block)
t.start()
}
-
+
open class func isMultiThreaded() -> Bool {
return true
}
-
+
open class func sleep(until date: Date) {
let start_ut = CFGetSystemUptime()
let start_at = CFAbsoluteTimeGetCurrent()
@@ -127,9 +136,10 @@
}
open class func exit() {
+ Thread.current._status = .finished
pthread_exit(nil)
}
-
+
internal var _main: () -> Void = {}
#if os(OSX) || os(iOS) || CYGWIN
private var _thread: pthread_t? = nil
@@ -145,12 +155,12 @@
internal var _cancelled = false
/// - Note: this differs from the Darwin implementation in that the keys must be Strings
open var threadDictionary = [String : Any]()
-
+
internal init(thread: pthread_t) {
// Note: even on Darwin this is a non-optional pthread_t; this is only used for valid threads, which are never null pointers.
_thread = thread
}
-
+
public override init() {
let _ = withUnsafeMutablePointer(to: &_attr) { attr in
pthread_attr_init(attr)
@@ -158,7 +168,7 @@
pthread_attr_setdetachstate(attr, Int32(PTHREAD_CREATE_DETACHED))
}
}
-
+
public convenience init(block: @escaping () -> Swift.Void) {
self.init()
_main = block
@@ -185,11 +195,11 @@
}
#endif
}
-
+
open func main() {
_main()
}
-
+
open var name: String? {
didSet {
if _thread == Thread.current._thread {
@@ -227,25 +237,52 @@
open var isFinished: Bool {
return _status == .finished
}
-
+
open var isCancelled: Bool {
return _cancelled
}
-
+
open var isMainThread: Bool {
- NSUnimplemented()
+ return self === Thread.mainThread
}
-
+
open func cancel() {
_cancelled = true
}
- open class var callStackReturnAddresses: [NSNumber] {
- NSUnimplemented()
+
+ private class func backtraceAddresses<T>(_ body: (UnsafeMutablePointer<UnsafeMutableRawPointer?>, Int) -> [T]) -> [T] {
+ // Same as swift/stdlib/public/runtime/Errors.cpp backtrace
+ let maxSupportedStackDepth = 128;
+ let addrs = UnsafeMutablePointer<UnsafeMutableRawPointer?>.allocate(capacity: maxSupportedStackDepth)
+ defer { addrs.deallocate(capacity: maxSupportedStackDepth) }
+ let count = backtrace(addrs, Int32(maxSupportedStackDepth))
+ let addressCount = max(0, min(Int(count), maxSupportedStackDepth))
+ return body(addrs, addressCount)
}
-
+
+ open class var callStackReturnAddresses: [NSNumber] {
+ return backtraceAddresses({ (addrs, count) in
+ UnsafeBufferPointer(start: addrs, count: count).map {
+ NSNumber(value: UInt(bitPattern: $0))
+ }
+ })
+ }
+
open class var callStackSymbols: [String] {
- NSUnimplemented()
+ return backtraceAddresses({ (addrs, count) in
+ var symbols: [String] = []
+ if let bs = backtrace_symbols(addrs, Int32(count)) {
+ symbols = UnsafeBufferPointer(start: bs, count: count).map {
+ guard let symbol = $0 else {
+ return "<null>"
+ }
+ return String(cString: symbol)
+ }
+ free(bs)
+ }
+ return symbols
+ })
}
}
diff --git a/Foundation/URLSession/URLSession.swift b/Foundation/URLSession/URLSession.swift
index 9dbb84c..3a07673 100644
--- a/Foundation/URLSession/URLSession.swift
+++ b/Foundation/URLSession/URLSession.swift
@@ -171,12 +171,21 @@
import CoreFoundation
import Dispatch
+extension URLSession {
+ public enum DelayedRequestDisposition {
+ case cancel
+ case continueLoading
+ case useNewRequest
+ }
+}
+fileprivate let globalVarSyncQ = DispatchQueue(label: "org.swift.Foundation.URLSession.GlobalVarSyncQ")
fileprivate var sessionCounter = Int32(0)
fileprivate func nextSessionIdentifier() -> Int32 {
- //TODO: find an alternative for OSAtomicIncrement32Barrier() on Linux
- sessionCounter += 1
- return sessionCounter
+ return globalVarSyncQ.sync {
+ sessionCounter += 1
+ return sessionCounter
+ }
}
public let NSURLSessionTransferSizeUnknown: Int64 = -1
diff --git a/Foundation/URLSession/URLSessionDelegate.swift b/Foundation/URLSession/URLSessionDelegate.swift
index ab9c077..3c65fb9 100644
--- a/Foundation/URLSession/URLSessionDelegate.swift
+++ b/Foundation/URLSession/URLSessionDelegate.swift
@@ -119,6 +119,8 @@
* nil, which implies that no error occurred and this task is complete.
*/
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?)
+
+ func urlSession(_ session: URLSession, task: URLSessionTask, willBeginDelayedRequest request: URLRequest, completionHandler: @escaping (URLSession.DelayedRequestDisposition, URLRequest?) -> Void)
}
extension URLSessionTaskDelegate {
@@ -137,6 +139,8 @@
public func urlSession(_ session: URLSession, task: URLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) { }
public func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { }
+
+ public func urlSession(_ session: URLSession, task: URLSessionTask, willBeginDelayedRequest request: URLRequest, completionHandler: @escaping (URLSession.DelayedRequestDisposition, URLRequest?) -> Void) { }
}
/*
@@ -194,6 +198,7 @@
* message to receive the resource data.
*/
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, willCacheResponse proposedResponse: CachedURLResponse, completionHandler: @escaping (CachedURLResponse?) -> Void)
+
}
extension URLSessionDataDelegate {
diff --git a/Foundation/URLSession/URLSessionTask.swift b/Foundation/URLSession/URLSessionTask.swift
index 1115ef5..6149f17 100644
--- a/Foundation/URLSession/URLSessionTask.swift
+++ b/Foundation/URLSession/URLSessionTask.swift
@@ -21,22 +21,23 @@
import CoreFoundation
import Dispatch
-
/// A cancelable object that refers to the lifetime
/// of processing a given request.
open class URLSessionTask : NSObject, NSCopying {
+
+ public var countOfBytesClientExpectsToReceive: Int64 { NSUnimplemented() }
+ public var countOfBytesClientExpectsToSend: Int64 { NSUnimplemented() }
+ public var earliestBeginDate: Date? { NSUnimplemented() }
+
/// How many times the task has been suspended, 0 indicating a running task.
internal var suspendCount = 1
internal var session: URLSessionProtocol! //change to nil when task completes
internal let body: _Body
fileprivate var _protocol: URLProtocol! = nil
+ private let syncQ = DispatchQueue(label: "org.swift.URLSessionTask.SyncQ")
/// All operations must run on this queue.
internal let workQueue: DispatchQueue
- /// Using dispatch semaphore to make public attributes thread safe.
- /// A semaphore is a simpler option against the usage of concurrent queue
- /// as the critical sections are very short.
- fileprivate let semaphore = DispatchSemaphore(value: 1)
public override init() {
// Darwin Foundation oddly allows calling this initializer, even though
@@ -62,7 +63,8 @@
}
internal init(session: URLSession, request: URLRequest, taskIdentifier: Int, body: _Body) {
self.session = session
- self.workQueue = session.workQueue
+ /* make sure we're actually having a serial queue as it's used for synchronization */
+ self.workQueue = DispatchQueue.init(label: "org.swift.URLSessionTask.WorkQueue", target: session.workQueue)
self.taskIdentifier = taskIdentifier
self.originalRequest = request
self.body = body
@@ -108,31 +110,19 @@
/// May differ from originalRequest due to http server redirection
/*@NSCopying*/ open internal(set) var currentRequest: URLRequest? {
get {
- semaphore.wait()
- defer {
- semaphore.signal()
- }
- return self._currentRequest
+ return self.syncQ.sync { return self._currentRequest }
}
set {
- semaphore.wait()
- self._currentRequest = newValue
- semaphore.signal()
+ self.syncQ.sync { self._currentRequest = newValue }
}
}
fileprivate var _currentRequest: URLRequest? = nil
/*@NSCopying*/ open internal(set) var response: URLResponse? {
get {
- semaphore.wait()
- defer {
- semaphore.signal()
- }
- return self._response
+ return self.syncQ.sync { return self._response }
}
set {
- semaphore.wait()
- self._response = newValue
- semaphore.signal()
+ self.syncQ.sync { self._response = newValue }
}
}
fileprivate var _response: URLResponse? = nil
@@ -145,16 +135,10 @@
/// Number of body bytes already received
open internal(set) var countOfBytesReceived: Int64 {
get {
- semaphore.wait()
- defer {
- semaphore.signal()
- }
- return self._countOfBytesReceived
+ return self.syncQ.sync { return self._countOfBytesReceived }
}
set {
- semaphore.wait()
- self._countOfBytesReceived = newValue
- semaphore.signal()
+ self.syncQ.sync { self._countOfBytesReceived = newValue }
}
}
fileprivate var _countOfBytesReceived: Int64 = 0
@@ -162,16 +146,10 @@
/// Number of body bytes already sent */
open internal(set) var countOfBytesSent: Int64 {
get {
- semaphore.wait()
- defer {
- semaphore.signal()
- }
- return self._countOfBytesSent
+ return self.syncQ.sync { return self._countOfBytesSent }
}
set {
- semaphore.wait()
- self._countOfBytesSent = newValue
- semaphore.signal()
+ self.syncQ.sync { self._countOfBytesSent = newValue }
}
}
@@ -211,16 +189,10 @@
*/
open var state: URLSessionTask.State {
get {
- semaphore.wait()
- defer {
- semaphore.signal()
- }
- return self._state
+ return self.syncQ.sync { self._state }
}
set {
- semaphore.wait()
- self._state = newValue
- semaphore.signal()
+ self.syncQ.sync { self._state = newValue }
}
}
fileprivate var _state: URLSessionTask.State = .suspended
@@ -301,16 +273,10 @@
/// URLSessionTask.highPriority, but use is not restricted to these.
open var priority: Float {
get {
- semaphore.wait()
- defer {
- semaphore.signal()
- }
- return self._priority
+ return self.workQueue.sync { return self._priority }
}
set {
- semaphore.wait()
- self._priority = newValue
- semaphore.signal()
+ self.workQueue.sync { self._priority = newValue }
}
}
fileprivate var _priority: Float = URLSessionTask.defaultPriority
@@ -328,6 +294,12 @@
}
}
+extension URLSessionTask : ProgressReporting {
+ public var progress: Progress {
+ NSUnimplemented()
+ }
+}
+
internal extension URLSessionTask {
/// Updates the (public) state based on private / internal state.
///
@@ -555,7 +527,9 @@
session.delegateQueue.addOperation {
delegate.urlSession(session, task: task, didCompleteWithError: nil)
task.state = .completed
- session.taskRegistry.remove(task)
+ task.workQueue.async {
+ session.taskRegistry.remove(task)
+ }
}
case .noDelegate:
task.state = .completed
@@ -606,7 +580,9 @@
session.delegateQueue.addOperation {
delegate.urlSession(session, task: task, didCompleteWithError: error as Error)
task.state = .completed
- session.taskRegistry.remove(task)
+ task.workQueue.async {
+ session.taskRegistry.remove(task)
+ }
}
case .noDelegate:
task.state = .completed
@@ -615,7 +591,9 @@
session.delegateQueue.addOperation {
completion(nil, nil, error)
task.state = .completed
- session.taskRegistry.remove(task)
+ task.workQueue.async {
+ session.taskRegistry.remove(task)
+ }
}
case .downloadCompletionHandler(let completion):
session.delegateQueue.addOperation {
diff --git a/Foundation/Unit.swift b/Foundation/Unit.swift
index 92a9c6a..f43e5ff 100644
--- a/Foundation/Unit.swift
+++ b/Foundation/Unit.swift
@@ -79,6 +79,19 @@
}
public static var supportsSecureCoding: Bool { return true }
+
+ open override func isEqual(_ object: Any?) -> Bool {
+ guard let other = object as? UnitConverterLinear else {
+ return false
+ }
+
+ if self === other {
+ return true
+ }
+
+ return self.coefficient == other.coefficient
+ && self.constant == other.constant
+ }
}
private class UnitConverterReciprocal : UnitConverter, NSSecureCoding {
@@ -115,6 +128,18 @@
}
fileprivate static var supportsSecureCoding: Bool { return true }
+
+ open override func isEqual(_ object: Any?) -> Bool {
+ guard let other = object as? UnitConverterReciprocal else {
+ return false
+ }
+
+ if self === other {
+ return true
+ }
+
+ return self.reciprocal == other.reciprocal
+ }
}
/*
@@ -127,7 +152,7 @@
open private(set) var symbol: String
- public init(symbol: String) {
+ public required init(symbol: String) {
self.symbol = symbol
}
@@ -150,8 +175,20 @@
}
aCoder.encode(self.symbol._bridgeToObjectiveC(), forKey:"NS.symbol")
}
-
+
public static var supportsSecureCoding: Bool { return true }
+
+ open override func isEqual(_ object: Any?) -> Bool {
+ guard let other = object as? Unit else {
+ return false
+ }
+
+ if self === other {
+ return true
+ }
+
+ return self.symbol == other.symbol
+ }
}
open class Dimension : Unit {
@@ -159,8 +196,7 @@
open private(set) var converter: UnitConverter
-
- public init(symbol: String, converter: UnitConverter) {
+ public required init(symbol: String, converter: UnitConverter) {
self.converter = converter
super.init(symbol: symbol)
}
@@ -186,6 +222,11 @@
super.init(symbol: symbol)
}
+ public required init(symbol: String) {
+ let T = type(of: self)
+ fatalError("\(T) must be initialized with designated initializer \(T).init(symbol: String, converter: UnitConverter)")
+ }
+
open override func encode(with aCoder: NSCoder) {
super.encode(with: aCoder)
guard aCoder.allowsKeyedCoding else {
@@ -193,6 +234,18 @@
}
aCoder.encode(self.converter, forKey:"converter")
}
+
+ open override func isEqual(_ object: Any?) -> Bool {
+ guard let other = object as? Dimension else {
+ return false
+ }
+
+ if self === other {
+ return true
+ }
+
+ return super.isEqual(object) && self.converter == other.converter
+ }
}
open class UnitAcceleration : Dimension {
@@ -211,8 +264,8 @@
static let gravity = 9.81
}
- private init(symbol: String, coefficient: Double) {
- super.init(symbol: symbol, converter: UnitConverterLinear(coefficient: coefficient))
+ private convenience init(symbol: String, coefficient: Double) {
+ self.init(symbol: symbol, converter: UnitConverterLinear(coefficient: coefficient))
}
open class var metersPerSecondSquared: UnitAcceleration {
@@ -231,9 +284,17 @@
return UnitAcceleration.metersPerSecondSquared
}
-
- public required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) }
- open override func encode(with aCoder: NSCoder) { super.encode(with: aCoder) }
+ open override func isEqual(_ object: Any?) -> Bool {
+ guard let other = object as? UnitAcceleration else {
+ return false
+ }
+
+ if self === other {
+ return true
+ }
+
+ return super.isEqual(object)
+ }
}
open class UnitAngle : Dimension {
@@ -260,11 +321,10 @@
static let revolutions = 360.0
}
- private init(symbol: String, coefficient: Double) {
- super.init(symbol: symbol, converter: UnitConverterLinear(coefficient: coefficient))
+ private convenience init(symbol: String, coefficient: Double) {
+ self.init(symbol: symbol, converter: UnitConverterLinear(coefficient: coefficient))
}
-
open class var degrees: UnitAngle {
get {
return UnitAngle(symbol: Symbol.degrees, coefficient: Coefficient.degrees)
@@ -305,9 +365,17 @@
return UnitAngle.degrees
}
-
- public required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) }
- open override func encode(with aCoder: NSCoder) { super.encode(with: aCoder) }
+ open override func isEqual(_ object: Any?) -> Bool {
+ guard let other = object as? UnitAngle else {
+ return false
+ }
+
+ if self === other {
+ return true
+ }
+
+ return super.isEqual(object)
+ }
}
open class UnitArea : Dimension {
@@ -350,11 +418,10 @@
static let hectares = 10000.0
}
- private init(symbol: String, coefficient: Double) {
- super.init(symbol: symbol, converter: UnitConverterLinear(coefficient: coefficient))
+ private convenience init(symbol: String, coefficient: Double) {
+ self.init(symbol: symbol, converter: UnitConverterLinear(coefficient: coefficient))
}
-
open class var squareMegameters: UnitArea {
get {
return UnitArea(symbol: Symbol.squareMegameters, coefficient: Coefficient.squareMegameters)
@@ -443,9 +510,17 @@
return UnitArea.squareMeters
}
-
- public required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) }
- open override func encode(with aCoder: NSCoder) { super.encode(with: aCoder) }
+ open override func isEqual(_ object: Any?) -> Bool {
+ guard let other = object as? UnitArea else {
+ return false
+ }
+
+ if self === other {
+ return true
+ }
+
+ return super.isEqual(object)
+ }
}
open class UnitConcentrationMass : Dimension {
@@ -466,11 +541,10 @@
static let millimolesPerLiter = 18.0
}
- private init(symbol: String, coefficient: Double) {
- super.init(symbol: symbol, converter: UnitConverterLinear(coefficient: coefficient))
+ private convenience init(symbol: String, coefficient: Double) {
+ self.init(symbol: symbol, converter: UnitConverterLinear(coefficient: coefficient))
}
-
open class var gramsPerLiter: UnitConcentrationMass {
get {
return UnitConcentrationMass(symbol: Symbol.gramsPerLiter, coefficient: Coefficient.gramsPerLiter)
@@ -491,9 +565,17 @@
return UnitConcentrationMass.gramsPerLiter
}
-
- public required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) }
- open override func encode(with aCoder: NSCoder) { super.encode(with: aCoder) }
+ open override func isEqual(_ object: Any?) -> Bool {
+ guard let other = object as? UnitConcentrationMass else {
+ return false
+ }
+
+ if self === other {
+ return true
+ }
+
+ return super.isEqual(object)
+ }
}
open class UnitDispersion : Dimension {
@@ -510,11 +592,10 @@
static let partsPerMillion = 1.0
}
- private init(symbol: String, coefficient: Double) {
- super.init(symbol: symbol, converter: UnitConverterLinear(coefficient: coefficient))
+ private convenience init(symbol: String, coefficient: Double) {
+ self.init(symbol: symbol, converter: UnitConverterLinear(coefficient: coefficient))
}
-
open class var partsPerMillion: UnitDispersion {
get {
return UnitDispersion(symbol: Symbol.partsPerMillion, coefficient: Coefficient.partsPerMillion)
@@ -525,9 +606,17 @@
return UnitDispersion.partsPerMillion
}
-
- public required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) }
- open override func encode(with aCoder: NSCoder) { super.encode(with: aCoder) }
+ open override func isEqual(_ object: Any?) -> Bool {
+ guard let other = object as? UnitDispersion else {
+ return false
+ }
+
+ if self === other {
+ return true
+ }
+
+ return super.isEqual(object)
+ }
}
open class UnitDuration : Dimension {
@@ -548,8 +637,8 @@
static let hours = 3600.0
}
- private init(symbol: String, coefficient: Double) {
- super.init(symbol: symbol, converter: UnitConverterLinear(coefficient: coefficient))
+ private convenience init(symbol: String, coefficient: Double) {
+ self.init(symbol: symbol, converter: UnitConverterLinear(coefficient: coefficient))
}
open class var seconds: UnitDuration {
@@ -574,9 +663,17 @@
return UnitDuration.seconds
}
-
- public required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) }
- open override func encode(with aCoder: NSCoder) { super.encode(with: aCoder) }
+ open override func isEqual(_ object: Any?) -> Bool {
+ guard let other = object as? UnitDuration else {
+ return false
+ }
+
+ if self === other {
+ return true
+ }
+
+ return super.isEqual(object)
+ }
}
open class UnitElectricCharge : Dimension {
@@ -602,11 +699,10 @@
static let microampereHours = 0.0036
}
- private init(symbol: String, coefficient: Double) {
- super.init(symbol: symbol, converter: UnitConverterLinear(coefficient: coefficient))
+ private convenience init(symbol: String, coefficient: Double) {
+ self.init(symbol: symbol, converter: UnitConverterLinear(coefficient: coefficient))
}
-
open class var coulombs: UnitElectricCharge {
get {
return UnitElectricCharge(symbol: Symbol.coulombs, coefficient: Coefficient.coulombs)
@@ -647,9 +743,17 @@
return UnitElectricCharge.coulombs
}
-
- public required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) }
- open override func encode(with aCoder: NSCoder) { super.encode(with: aCoder) }
+ open override func isEqual(_ object: Any?) -> Bool {
+ guard let other = object as? UnitElectricCharge else {
+ return false
+ }
+
+ if self === other {
+ return true
+ }
+
+ return super.isEqual(object)
+ }
}
open class UnitElectricCurrent : Dimension {
@@ -657,6 +761,7 @@
/*
Base unit - amperes
*/
+
private struct Symbol {
static let megaamperes = "MA"
static let kiloamperes = "kA"
@@ -674,11 +779,10 @@
}
- private init(symbol: String, coefficient: Double) {
- super.init(symbol: symbol, converter: UnitConverterLinear(coefficient: coefficient))
+ private convenience init(symbol: String, coefficient: Double) {
+ self.init(symbol: symbol, converter: UnitConverterLinear(coefficient: coefficient))
}
-
open class var megaamperes: UnitElectricCurrent {
get {
return UnitElectricCurrent(symbol: Symbol.megaamperes, coefficient: Coefficient.megaamperes)
@@ -713,9 +817,17 @@
return UnitElectricCurrent.amperes
}
-
- public required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) }
- open override func encode(with aCoder: NSCoder) { super.encode(with: aCoder) }
+ open override func isEqual(_ object: Any?) -> Bool {
+ guard let other = object as? UnitElectricCurrent else {
+ return false
+ }
+
+ if self === other {
+ return true
+ }
+
+ return super.isEqual(object)
+ }
}
open class UnitElectricPotentialDifference : Dimension {
@@ -741,11 +853,10 @@
}
- private init(symbol: String, coefficient: Double) {
- super.init(symbol: symbol, converter: UnitConverterLinear(coefficient: coefficient))
+ private convenience init(symbol: String, coefficient: Double) {
+ self.init(symbol: symbol, converter: UnitConverterLinear(coefficient: coefficient))
}
-
open class var megavolts: UnitElectricPotentialDifference {
get {
return UnitElectricPotentialDifference(symbol: Symbol.megavolts, coefficient: Coefficient.megavolts)
@@ -780,9 +891,17 @@
return UnitElectricPotentialDifference.volts
}
-
- public required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) }
- open override func encode(with aCoder: NSCoder) { super.encode(with: aCoder) }
+ open override func isEqual(_ object: Any?) -> Bool {
+ guard let other = object as? UnitElectricPotentialDifference else {
+ return false
+ }
+
+ if self === other {
+ return true
+ }
+
+ return super.isEqual(object)
+ }
}
open class UnitElectricResistance : Dimension {
@@ -808,8 +927,8 @@
}
- private init(symbol: String, coefficient: Double) {
- super.init(symbol: symbol, converter: UnitConverterLinear(coefficient: coefficient))
+ private convenience init(symbol: String, coefficient: Double) {
+ self.init(symbol: symbol, converter: UnitConverterLinear(coefficient: coefficient))
}
open class var megaohms: UnitElectricResistance {
@@ -846,9 +965,17 @@
return UnitElectricResistance.ohms
}
-
- public required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) }
- open override func encode(with aCoder: NSCoder) { super.encode(with: aCoder) }
+ open override func isEqual(_ object: Any?) -> Bool {
+ guard let other = object as? UnitElectricResistance else {
+ return false
+ }
+
+ if self === other {
+ return true
+ }
+
+ return super.isEqual(object)
+ }
}
open class UnitEnergy : Dimension {
@@ -874,8 +1001,8 @@
}
- private init(symbol: String, coefficient: Double) {
- super.init(symbol: symbol, converter: UnitConverterLinear(coefficient: coefficient))
+ private convenience init(symbol: String, coefficient: Double) {
+ self.init(symbol: symbol, converter: UnitConverterLinear(coefficient: coefficient))
}
open class var kilojoules: UnitEnergy {
@@ -912,9 +1039,17 @@
return UnitEnergy.joules
}
-
- public required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) }
- open override func encode(with aCoder: NSCoder) { super.encode(with: aCoder) }
+ open override func isEqual(_ object: Any?) -> Bool {
+ guard let other = object as? UnitEnergy else {
+ return false
+ }
+
+ if self === other {
+ return true
+ }
+
+ return super.isEqual(object)
+ }
}
open class UnitFrequency : Dimension {
@@ -945,11 +1080,10 @@
static let nanohertz = 1e-9
}
- private init(symbol: String, coefficient: Double) {
- super.init(symbol: symbol, converter: UnitConverterLinear(coefficient: coefficient))
+ private convenience init(symbol: String, coefficient: Double) {
+ self.init(symbol: symbol, converter: UnitConverterLinear(coefficient: coefficient))
}
-
open class var terahertz: UnitFrequency {
get {
return UnitFrequency(symbol: Symbol.terahertz, coefficient: Coefficient.terahertz)
@@ -1002,9 +1136,17 @@
return UnitFrequency.hertz
}
-
- public required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) }
- open override func encode(with aCoder: NSCoder) { super.encode(with: aCoder) }
+ open override func isEqual(_ object: Any?) -> Bool {
+ guard let other = object as? UnitFrequency else {
+ return false
+ }
+
+ if self === other {
+ return true
+ }
+
+ return super.isEqual(object)
+ }
}
open class UnitFuelEfficiency : Dimension {
@@ -1025,11 +1167,10 @@
static let milesPerGallon = 235.215
}
- private init(symbol: String, reciprocal: Double) {
- super.init(symbol: symbol, converter: UnitConverterReciprocal(reciprocal: reciprocal))
+ private convenience init(symbol: String, reciprocal: Double) {
+ self.init(symbol: symbol, converter: UnitConverterReciprocal(reciprocal: reciprocal))
}
-
open class var litersPer100Kilometers: UnitFuelEfficiency {
get {
return UnitFuelEfficiency(symbol: Symbol.litersPer100Kilometers, reciprocal: Coefficient.litersPer100Kilometers)
@@ -1052,9 +1193,17 @@
return UnitFuelEfficiency.litersPer100Kilometers
}
-
- public required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) }
- open override func encode(with aCoder: NSCoder) { super.encode(with: aCoder) }
+ open override func isEqual(_ object: Any?) -> Bool {
+ guard let other = object as? UnitFuelEfficiency else {
+ return false
+ }
+
+ if self === other {
+ return true
+ }
+
+ return super.isEqual(object)
+ }
}
open class UnitLength : Dimension {
@@ -1113,8 +1262,8 @@
static let parsecs = 3.086e+16
}
- private init(symbol: String, coefficient: Double) {
- super.init(symbol: symbol, converter: UnitConverterLinear(coefficient: coefficient))
+ private convenience init(symbol: String, coefficient: Double) {
+ self.init(symbol: symbol, converter: UnitConverterLinear(coefficient: coefficient))
}
open class var megameters: UnitLength {
@@ -1253,9 +1402,17 @@
return UnitLength.meters
}
-
- public required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) }
- open override func encode(with aCoder: NSCoder) { super.encode(with: aCoder) }
+ open override func isEqual(_ object: Any?) -> Bool {
+ guard let other = object as? UnitLength else {
+ return false
+ }
+
+ if self === other {
+ return true
+ }
+
+ return super.isEqual(object)
+ }
}
open class UnitIlluminance : Dimension {
@@ -1272,8 +1429,8 @@
static let lux = 1.0
}
- private init(symbol: String, coefficient: Double) {
- super.init(symbol: symbol, converter: UnitConverterLinear(coefficient: coefficient))
+ private convenience init(symbol: String, coefficient: Double) {
+ self.init(symbol: symbol, converter: UnitConverterLinear(coefficient: coefficient))
}
open class var lux: UnitIlluminance {
@@ -1286,9 +1443,17 @@
return UnitIlluminance.lux
}
-
- public required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) }
- open override func encode(with aCoder: NSCoder) { super.encode(with: aCoder) }
+ open override func isEqual(_ object: Any?) -> Bool {
+ guard let other = object as? UnitIlluminance else {
+ return false
+ }
+
+ if self === other {
+ return true
+ }
+
+ return super.isEqual(object)
+ }
}
open class UnitMass : Dimension {
@@ -1335,8 +1500,8 @@
static let slugs = 14.5939
}
- private init(symbol: String, coefficient: Double) {
- super.init(symbol: symbol, converter: UnitConverterLinear(coefficient: coefficient))
+ private convenience init(symbol: String, coefficient: Double) {
+ self.init(symbol: symbol, converter: UnitConverterLinear(coefficient: coefficient))
}
open class var kilograms: UnitMass {
@@ -1439,9 +1604,17 @@
return UnitMass.kilograms
}
-
- public required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) }
- open override func encode(with aCoder: NSCoder) { super.encode(with: aCoder) }
+ open override func isEqual(_ object: Any?) -> Bool {
+ guard let other = object as? UnitMass else {
+ return false
+ }
+
+ if self === other {
+ return true
+ }
+
+ return super.isEqual(object)
+ }
}
open class UnitPower : Dimension {
@@ -1478,8 +1651,8 @@
static let horsepower = 745.7
}
- private init(symbol: String, coefficient: Double) {
- super.init(symbol: symbol, converter: UnitConverterLinear(coefficient: coefficient))
+ private convenience init(symbol: String, coefficient: Double) {
+ self.init(symbol: symbol, converter: UnitConverterLinear(coefficient: coefficient))
}
open class var terawatts: UnitPower {
@@ -1552,9 +1725,17 @@
return UnitPower.watts
}
-
- public required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) }
- open override func encode(with aCoder: NSCoder) { super.encode(with: aCoder) }
+ open override func isEqual(_ object: Any?) -> Bool {
+ guard let other = object as? UnitPower else {
+ return false
+ }
+
+ if self === other {
+ return true
+ }
+
+ return super.isEqual(object)
+ }
}
open class UnitPressure : Dimension {
@@ -1589,11 +1770,10 @@
static let poundsForcePerSquareInch = 6894.76
}
- private init(symbol: String, coefficient: Double) {
- super.init(symbol: symbol, converter: UnitConverterLinear(coefficient: coefficient))
+ private convenience init(symbol: String, coefficient: Double) {
+ self.init(symbol: symbol, converter: UnitConverterLinear(coefficient: coefficient))
}
-
open class var newtonsPerMetersSquared: UnitPressure {
get {
return UnitPressure(symbol: Symbol.newtonsPerMetersSquared, coefficient: Coefficient.newtonsPerMetersSquared)
@@ -1658,9 +1838,17 @@
return UnitPressure.newtonsPerMetersSquared
}
-
- public required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) }
- open override func encode(with aCoder: NSCoder) { super.encode(with: aCoder) }
+ open override func isEqual(_ object: Any?) -> Bool {
+ guard let other = object as? UnitPressure else {
+ return false
+ }
+
+ if self === other {
+ return true
+ }
+
+ return super.isEqual(object)
+ }
}
open class UnitSpeed : Dimension {
@@ -1683,11 +1871,10 @@
static let knots = 0.514444
}
- private init(symbol: String, coefficient: Double) {
- super.init(symbol: symbol, converter: UnitConverterLinear(coefficient: coefficient))
+ private convenience init(symbol: String, coefficient: Double) {
+ self.init(symbol: symbol, converter: UnitConverterLinear(coefficient: coefficient))
}
-
open class var metersPerSecond: UnitSpeed {
get {
return UnitSpeed(symbol: Symbol.metersPerSecond, coefficient: Coefficient.metersPerSecond)
@@ -1716,9 +1903,17 @@
return UnitSpeed.metersPerSecond
}
-
- public required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) }
- open override func encode(with aCoder: NSCoder) { super.encode(with: aCoder) }
+ open override func isEqual(_ object: Any?) -> Bool {
+ guard let other = object as? UnitSpeed else {
+ return false
+ }
+
+ if self === other {
+ return true
+ }
+
+ return super.isEqual(object)
+ }
}
open class UnitTemperature : Dimension {
@@ -1745,8 +1940,8 @@
static let fahrenheit = 255.37222222222427
}
- private init(symbol: String, coefficient: Double, constant: Double) {
- super.init(symbol: symbol, converter: UnitConverterLinear(coefficient: coefficient, constant: constant))
+ private convenience init(symbol: String, coefficient: Double, constant: Double) {
+ self.init(symbol: symbol, converter: UnitConverterLinear(coefficient: coefficient, constant: constant))
}
open class var kelvin: UnitTemperature {
@@ -1771,9 +1966,17 @@
return UnitTemperature.kelvin
}
-
- public required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) }
- open override func encode(with aCoder: NSCoder) { super.encode(with: aCoder) }
+ open override func isEqual(_ object: Any?) -> Bool {
+ guard let other = object as? UnitTemperature else {
+ return false
+ }
+
+ if self === other {
+ return true
+ }
+
+ return super.isEqual(object)
+ }
}
open class UnitVolume : Dimension {
@@ -1850,11 +2053,10 @@
static let metricCups = 0.25
}
- private init(symbol: String, coefficient: Double) {
- super.init(symbol: symbol, converter: UnitConverterLinear(coefficient: coefficient))
+ private convenience init(symbol: String, coefficient: Double) {
+ self.init(symbol: symbol, converter: UnitConverterLinear(coefficient: coefficient))
}
-
open class var megaliters: UnitVolume {
get {
return UnitVolume(symbol: Symbol.megaliters, coefficient: Coefficient.megaliters)
@@ -2045,6 +2247,15 @@
return UnitVolume.liters
}
- public required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) }
- open override func encode(with aCoder: NSCoder) { super.encode(with: aCoder) }
+ open override func isEqual(_ object: Any?) -> Bool {
+ guard let other = object as? UnitVolume else {
+ return false
+ }
+
+ if self === other {
+ return true
+ }
+
+ return super.isEqual(object)
+ }
}
diff --git a/TestFoundation/HTTPServer.swift b/TestFoundation/HTTPServer.swift
index fccbad1..f52b771 100644
--- a/TestFoundation/HTTPServer.swift
+++ b/TestFoundation/HTTPServer.swift
@@ -26,6 +26,8 @@
#endif
public let globalDispatchQueue = DispatchQueue.global()
+public let dispatchQueueMake: (String) -> DispatchQueue = { DispatchQueue.init(label: $0) }
+public let dispatchGroupMake: () -> DispatchGroup = DispatchGroup.init
struct _HTTPUtils {
static let CRLF = "\r\n"
diff --git a/TestFoundation/TestCodable.swift b/TestFoundation/TestCodable.swift
index c43becf..f1fb7d6 100644
--- a/TestFoundation/TestCodable.swift
+++ b/TestFoundation/TestCodable.swift
@@ -399,6 +399,13 @@
let components = calendar.dateComponents(dateComponents, from: Date(timeIntervalSince1970: 1501283776))
expectRoundTripEqualityThroughJSON(for: components)
}
+
+ // MARK: - Measurement
+ func test_Measurement_JSON() {
+ expectRoundTripEqualityThroughJSON(for: Measurement(value: 42, unit: UnitAcceleration.metersPerSecondSquared))
+ expectRoundTripEqualityThroughJSON(for: Measurement(value: 42, unit: UnitMass.kilograms))
+ expectRoundTripEqualityThroughJSON(for: Measurement(value: 42, unit: UnitLength.miles))
+ }
}
extension TestCodable {
@@ -420,6 +427,7 @@
("test_TimeZone_JSON", test_TimeZone_JSON),
("test_Calendar_JSON", test_Calendar_JSON),
("test_DateComponents_JSON", test_DateComponents_JSON),
+ ("test_Measurement_JSON", test_Measurement_JSON),
]
}
}
diff --git a/TestFoundation/TestHTTPCookieStorage.swift b/TestFoundation/TestHTTPCookieStorage.swift
index bea3391..472e34a 100644
--- a/TestFoundation/TestHTTPCookieStorage.swift
+++ b/TestFoundation/TestHTTPCookieStorage.swift
@@ -230,7 +230,7 @@
let bundlePath = Bundle.main.bundlePath
var bundleName = "/" + bundlePath.components(separatedBy: "/").last!
if let range = bundleName.range(of: ".", options: String.CompareOptions.backwards, range: nil, locale: nil) {
- bundleName = bundleName.substring(to: range.lowerBound)
+ bundleName = String(bundleName[..<range.lowerBound])
}
if let xdg_data_home = getenv("XDG_DATA_HOME") {
destPath = String(utf8String: xdg_data_home)! + bundleName + "/.cookies.shared"
@@ -251,7 +251,7 @@
let exeName = "/xdgTestHelper/xdgTestHelper"
#endif
- task.launchPath = bundlePath.substring(to: pathIndex!) + exeName
+ task.launchPath = bundlePath[..<pathIndex!] + exeName
var environment = ProcessInfo.processInfo.environment
let testPath = NSHomeDirectory() + "/TestXDG"
environment["XDG_DATA_HOME"] = testPath
diff --git a/TestFoundation/TestJSONEncoder.swift b/TestFoundation/TestJSONEncoder.swift
index 8631442..6444296 100644
--- a/TestFoundation/TestJSONEncoder.swift
+++ b/TestFoundation/TestJSONEncoder.swift
@@ -401,6 +401,68 @@
test_codingOf(value: URL(string: "https://swift.org")!, toAndFrom: "\"https://swift.org\"")
}
+
+ // UInt and Int
+ func test_codingOfUIntMinMax() {
+
+ let encoder = JSONEncoder()
+
+ struct MyValue: Codable {
+ let intMin:Int = Int.min
+ let intMax:Int = Int.max
+ let uintMin:UInt = UInt.min
+ let uintMax:UInt = UInt.max
+ }
+
+ let myValue = MyValue()
+ let myDictI: [String:Any] = ["intMin": myValue.intMin, "intMax": myValue.intMax]
+ let myDictU: [String:Any] = ["uintMin": myValue.uintMin, "uintMax": myValue.uintMax]
+ let myDict1: [String:Any] = ["intMin": myValue.intMin]
+ let myDict2: [String:Any] = ["intMax": myValue.intMax]
+ let myDict3: [String:Any] = ["uintMin": myValue.uintMin]
+ let myDict4: [String:Any] = ["uintMax": myValue.uintMax]
+
+ func compareJSON(_ s1: String, _ s2: String) {
+ let ss1 = s1.trimmingCharacters(in: CharacterSet(charactersIn: "{}")).split(separator: Character(",")).sorted()
+ let ss2 = s2.trimmingCharacters(in: CharacterSet(charactersIn: "{}")).split(separator: Character(",")).sorted()
+ XCTAssertEqual(ss1, ss2)
+ }
+
+ do {
+ let result = try encoder.encode(myValue)
+ let r = String(data: result, encoding: .utf8) ?? "nil"
+ compareJSON(r, "{\"uintMin\":0,\"uintMax\":18446744073709551615,\"intMin\":-9223372036854775808,\"intMax\":9223372036854775807}")
+
+ let resultI = try JSONSerialization.data(withJSONObject: myDictI)
+ let rI = String(data: resultI, encoding: .utf8) ?? "nil"
+ compareJSON(rI, "{\"intMin\":-9223372036854775808,\"intMax\":9223372036854775807}")
+
+ let resultU = try JSONSerialization.data(withJSONObject: myDictU)
+ let rU = String(data: resultU, encoding: .utf8) ?? "nil"
+ compareJSON(rU, "{\"uintMax\":18446744073709551615,\"uintMin\":0}")
+
+ let result1 = try JSONSerialization.data(withJSONObject: myDict1)
+ let r1 = String(data: result1, encoding: .utf8) ?? "nil"
+ XCTAssertEqual(r1, "{\"intMin\":-9223372036854775808}")
+
+ let result2 = try JSONSerialization.data(withJSONObject: myDict2)
+ let r2 = String(data: result2, encoding: .utf8) ?? "nil"
+ XCTAssertEqual(r2, "{\"intMax\":9223372036854775807}")
+
+ let result3 = try JSONSerialization.data(withJSONObject: myDict3)
+ let r3 = String(data: result3, encoding: .utf8) ?? "nil"
+ XCTAssertEqual(r3, "{\"uintMin\":0}")
+
+ let result4 = try JSONSerialization.data(withJSONObject: myDict4)
+ let r4 = String(data: result4, encoding: .utf8) ?? "nil"
+ XCTAssertEqual(r4, "{\"uintMax\":18446744073709551615}")
+ } catch {
+ XCTFail(String(describing: error))
+ }
+ }
+
+
+
// MARK: - Helper Functions
private var _jsonEmptyDictionary: Data {
return "{}".data(using: .utf8)!
@@ -986,6 +1048,7 @@
("test_codingOfUInt64", test_codingOfUInt64),
("test_codingOfInt", test_codingOfInt),
("test_codingOfUInt", test_codingOfUInt),
+ ("test_codingOfUIntMinMax", test_codingOfUIntMinMax),
("test_codingOfFloat", test_codingOfFloat),
("test_codingOfDouble", test_codingOfDouble),
("test_codingOfString", test_codingOfString),
diff --git a/TestFoundation/TestNSData.swift b/TestFoundation/TestNSData.swift
index 020b2c1..df0cd2e 100644
--- a/TestFoundation/TestNSData.swift
+++ b/TestFoundation/TestNSData.swift
@@ -37,6 +37,8 @@
("test_base64Data_medium", test_base64Data_medium),
("test_base64Data_small", test_base64Data_small),
("test_openingNonExistentFile", test_openingNonExistentFile),
+ ("test_contentsOfFile", test_contentsOfFile),
+ ("test_contentsOfZeroFile", test_contentsOfZeroFile),
("test_basicReadWrite", test_basicReadWrite),
("test_bufferSizeCalculation", test_bufferSizeCalculation),
// ("test_dataHash", test_dataHash), Disabled due to lack of brdiging in swift runtime -- infinite loops
@@ -908,6 +910,47 @@
XCTAssertTrue(didCatchError)
}
+ func test_contentsOfFile() {
+ let testDir = testBundle().resourcePath
+ let filename = testDir!.appending("/NSStringTestData.txt")
+
+ let contents = NSData(contentsOfFile: filename)
+ XCTAssertNotNil(contents)
+ if let contents = contents {
+ let ptr = UnsafeMutableRawPointer(mutating: contents.bytes)
+ let str = String(bytesNoCopy: ptr, length: contents.length,
+ encoding: .ascii, freeWhenDone: false)
+ XCTAssertEqual(str, "swift-corelibs-foundation")
+ }
+ }
+
+ func test_contentsOfZeroFile() {
+#if os(Linux)
+ guard FileManager.default.fileExists(atPath: "/proc/self") else {
+ return
+ }
+ let contents = NSData(contentsOfFile: "/proc/self/cmdline")
+ XCTAssertNotNil(contents)
+ if let contents = contents {
+ XCTAssertTrue(contents.length > 0)
+ let ptr = UnsafeMutableRawPointer(mutating: contents.bytes)
+ let str = String(bytesNoCopy: ptr, length: contents.length,
+ encoding: .ascii, freeWhenDone: false)
+ XCTAssertNotNil(str)
+ if let str = str {
+ XCTAssertTrue(str.hasSuffix("TestFoundation"))
+ }
+ }
+
+ do {
+ let maps = try String(contentsOfFile: "/proc/self/maps", encoding: .utf8)
+ XCTAssertTrue(maps.count > 0)
+ } catch {
+ XCTFail("Cannot read /proc/self/maps: \(String(describing: error))")
+ }
+#endif
+ }
+
func test_basicReadWrite() {
let url = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true).appendingPathComponent("testfile")
let count = 1 << 24
diff --git a/TestFoundation/TestNSNumber.swift b/TestFoundation/TestNSNumber.swift
index d3ddde8..9d7fe2b 100644
--- a/TestFoundation/TestNSNumber.swift
+++ b/TestFoundation/TestNSNumber.swift
@@ -43,6 +43,7 @@
("test_description", test_description ),
("test_descriptionWithLocale", test_descriptionWithLocale ),
("test_objCType", test_objCType ),
+ ("test_stringValue", test_stringValue),
]
}
@@ -1019,4 +1020,71 @@
XCTAssertEqual("f" /* 0x66 */, objCType(NSNumber(value: Float.greatestFiniteMagnitude)))
XCTAssertEqual("d" /* 0x64 */, objCType(NSNumber(value: Double.greatestFiniteMagnitude)))
}
+
+ func test_stringValue() {
+
+ if UInt.max == UInt32.max {
+ XCTAssertEqual(NSNumber(value: UInt.min).stringValue, "0")
+ XCTAssertEqual(NSNumber(value: UInt.min + 1).stringValue, "1")
+ XCTAssertEqual(NSNumber(value: UInt.max).stringValue, "4294967295")
+ XCTAssertEqual(NSNumber(value: UInt.max - 1).stringValue, "4294967294")
+ } else if UInt.max == UInt64.max {
+ XCTAssertEqual(NSNumber(value: UInt.min).stringValue, "0")
+ XCTAssertEqual(NSNumber(value: UInt.min + 1).stringValue, "1")
+ XCTAssertEqual(NSNumber(value: UInt.max).stringValue, "18446744073709551615")
+ XCTAssertEqual(NSNumber(value: UInt.max - 1).stringValue, "18446744073709551614")
+ }
+
+ XCTAssertEqual(NSNumber(value: UInt8.min).stringValue, "0")
+ XCTAssertEqual(NSNumber(value: UInt8.min + 1).stringValue, "1")
+ XCTAssertEqual(NSNumber(value: UInt8.max).stringValue, "255")
+ XCTAssertEqual(NSNumber(value: UInt8.max - 1).stringValue, "254")
+
+ XCTAssertEqual(NSNumber(value: UInt16.min).stringValue, "0")
+ XCTAssertEqual(NSNumber(value: UInt16.min + 1).stringValue, "1")
+ XCTAssertEqual(NSNumber(value: UInt16.max).stringValue, "65535")
+ XCTAssertEqual(NSNumber(value: UInt16.max - 1).stringValue, "65534")
+
+ XCTAssertEqual(NSNumber(value: UInt32.min).stringValue, "0")
+ XCTAssertEqual(NSNumber(value: UInt32.min + 1).stringValue, "1")
+ XCTAssertEqual(NSNumber(value: UInt32.max).stringValue, "4294967295")
+ XCTAssertEqual(NSNumber(value: UInt32.max - 1).stringValue, "4294967294")
+
+ XCTAssertEqual(NSNumber(value: UInt64.min).stringValue, "0")
+ XCTAssertEqual(NSNumber(value: UInt64.min + 1).stringValue, "1")
+ XCTAssertEqual(NSNumber(value: UInt64.max).stringValue, "18446744073709551615")
+ XCTAssertEqual(NSNumber(value: UInt64.max - 1).stringValue, "18446744073709551614")
+
+ if Int.max == Int32.max {
+ XCTAssertEqual(NSNumber(value: Int.min).stringValue, "-2147483648")
+ XCTAssertEqual(NSNumber(value: Int.min + 1).stringValue, "-2147483647")
+ XCTAssertEqual(NSNumber(value: Int.max).stringValue, "2147483647")
+ XCTAssertEqual(NSNumber(value: Int.max - 1).stringValue, "2147483646")
+ } else if Int.max == Int64.max {
+ XCTAssertEqual(NSNumber(value: Int.min).stringValue, "-9223372036854775808")
+ XCTAssertEqual(NSNumber(value: Int.min + 1).stringValue, "-9223372036854775807")
+ XCTAssertEqual(NSNumber(value: Int.max).stringValue, "9223372036854775807")
+ XCTAssertEqual(NSNumber(value: Int.max - 1).stringValue, "9223372036854775806")
+ }
+
+ XCTAssertEqual(NSNumber(value: Int8.min).stringValue, "-128")
+ XCTAssertEqual(NSNumber(value: Int8.min + 1).stringValue, "-127")
+ XCTAssertEqual(NSNumber(value: Int8.max).stringValue, "127")
+ XCTAssertEqual(NSNumber(value: Int8.max - 1).stringValue, "126")
+
+ XCTAssertEqual(NSNumber(value: Int16.min).stringValue, "-32768")
+ XCTAssertEqual(NSNumber(value: Int16.min + 1).stringValue, "-32767")
+ XCTAssertEqual(NSNumber(value: Int16.max).stringValue, "32767")
+ XCTAssertEqual(NSNumber(value: Int16.max - 1).stringValue, "32766")
+
+ XCTAssertEqual(NSNumber(value: Int32.min).stringValue, "-2147483648")
+ XCTAssertEqual(NSNumber(value: Int32.min + 1).stringValue, "-2147483647")
+ XCTAssertEqual(NSNumber(value: Int32.max).stringValue, "2147483647")
+ XCTAssertEqual(NSNumber(value: Int32.max - 1).stringValue, "2147483646")
+
+ XCTAssertEqual(NSNumber(value: Int64.min).stringValue, "-9223372036854775808")
+ XCTAssertEqual(NSNumber(value: Int64.min + 1).stringValue, "-9223372036854775807")
+ XCTAssertEqual(NSNumber(value: Int64.max).stringValue, "9223372036854775807")
+ XCTAssertEqual(NSNumber(value: Int64.max - 1).stringValue, "9223372036854775806")
+ }
}
diff --git a/TestFoundation/TestOperationQueue.swift b/TestFoundation/TestOperationQueue.swift
index f60f17b..02f95c4 100644
--- a/TestFoundation/TestOperationQueue.swift
+++ b/TestFoundation/TestOperationQueue.swift
@@ -175,12 +175,17 @@
override internal(set) var isExecuting: Bool {
get {
- return _executing
+ lock.lock()
+ let wasExecuting = _executing
+ lock.unlock()
+ return wasExecuting
}
set {
- if _executing != newValue {
+ if isExecuting != newValue {
willChangeValue(forKey: "isExecuting")
+ lock.lock()
_executing = newValue
+ lock.unlock()
didChangeValue(forKey: "isExecuting")
}
}
@@ -188,12 +193,17 @@
override internal(set) var isFinished: Bool {
get {
- return _finished
+ lock.lock()
+ let wasFinished = _finished
+ lock.unlock()
+ return wasFinished
}
set {
- if _finished != newValue {
+ if isFinished != newValue {
willChangeValue(forKey: "isFinished")
+ lock.lock()
_finished = newValue
+ lock.unlock()
didChangeValue(forKey: "isFinished")
}
}
@@ -213,10 +223,8 @@
queue.async {
sleep(1)
- self.lock.lock()
self.isExecuting = false
self.isFinished = true
- self.lock.unlock()
}
}
diff --git a/TestFoundation/TestProcess.swift b/TestFoundation/TestProcess.swift
index 163dc31..922c607 100644
--- a/TestFoundation/TestProcess.swift
+++ b/TestFoundation/TestProcess.swift
@@ -334,7 +334,7 @@
guard let range = line.range(of: "=") else {
throw Error.InvalidEnvironmentVariable(line)
}
- result[line.substring(to: range.lowerBound)] = line.substring(from: range.upperBound)
+ result[String(line[..<range.lowerBound])] = String(line[range.upperBound...])
}
return result
}
diff --git a/TestFoundation/TestThread.swift b/TestFoundation/TestThread.swift
index 5630059..28478a4 100644
--- a/TestFoundation/TestThread.swift
+++ b/TestFoundation/TestThread.swift
@@ -24,6 +24,9 @@
("test_currentThread", test_currentThread ),
("test_threadStart", test_threadStart),
("test_threadName", test_threadName),
+ ("test_mainThread", test_mainThread),
+ ("test_callStackSymbols", test_callStackSymbols),
+ ("test_callStackReurnAddresses", test_callStackReturnAddresses),
]
}
@@ -33,25 +36,22 @@
XCTAssertNotNil(thread1)
XCTAssertNotNil(thread2)
XCTAssertEqual(thread1, thread2)
+ XCTAssertEqual(thread1, Thread.mainThread)
}
func test_threadStart() {
- var started = false
let condition = NSCondition()
let thread = Thread() {
condition.lock()
- started = true
condition.broadcast()
condition.unlock()
}
thread.start()
condition.lock()
- if !started {
- condition.wait()
- }
+ let ok = condition.wait(until: Date(timeIntervalSinceNow: 10))
condition.unlock()
- XCTAssertTrue(started)
+ XCTAssertTrue(ok, "NSCondition wait timed out")
}
func test_threadName() {
@@ -84,4 +84,41 @@
XCTAssertEqual(thread3.name, "Thread3")
XCTAssertNotEqual(thread3.name, getPThreadName())
}
+
+ func test_mainThread() {
+ XCTAssertTrue(Thread.isMainThread)
+ let t = Thread.mainThread
+ XCTAssertTrue(t.isMainThread)
+ let c = Thread.current
+ XCTAssertTrue(c.isMainThread)
+ XCTAssertTrue(c.isExecuting)
+ XCTAssertTrue(c.isEqual(t))
+
+ let condition = NSCondition()
+ let thread = Thread() {
+ condition.lock()
+ XCTAssertFalse(Thread.isMainThread)
+ XCTAssertFalse(Thread.mainThread == Thread.current)
+ condition.broadcast()
+ condition.unlock()
+ }
+ thread.start()
+
+ condition.lock()
+ let ok = condition.wait(until: Date(timeIntervalSinceNow: 10))
+ condition.unlock()
+ XCTAssertTrue(ok, "NSCondition wait timed out")
+ }
+
+ func test_callStackSymbols() {
+ let symbols = Thread.callStackSymbols
+ XCTAssertTrue(symbols.count > 0)
+ XCTAssertTrue(symbols.count <= 128)
+ }
+
+ func test_callStackReturnAddresses() {
+ let addresses = Thread.callStackReturnAddresses
+ XCTAssertTrue(addresses.count > 0)
+ XCTAssertTrue(addresses.count <= 128)
+ }
}
diff --git a/TestFoundation/TestURLSession.swift b/TestFoundation/TestURLSession.swift
index cf44ab6..c941f1c 100644
--- a/TestFoundation/TestURLSession.swift
+++ b/TestFoundation/TestURLSession.swift
@@ -36,13 +36,14 @@
("test_verifyHttpAdditionalHeaders", test_verifyHttpAdditionalHeaders),
("test_timeoutInterval", test_timeoutInterval),
("test_httpRedirection", test_httpRedirection),
- ("test_httpRedirectionTimeout", test_httpRedirectionTimeout),
+ //("test_httpRedirectionTimeout", test_httpRedirectionTimeout), /* temporarily disabled (https://bugs.swift.org/browse/SR-5751) */
("test_http0_9SimpleResponses", test_http0_9SimpleResponses),
("test_outOfRangeButCorrectlyFormattedHTTPCode", test_outOfRangeButCorrectlyFormattedHTTPCode),
("test_missingContentLengthButStillABody", test_missingContentLengthButStillABody),
("test_illegalHTTPServerResponses", test_illegalHTTPServerResponses),
("test_dataTaskWithSharedDelegate", test_dataTaskWithSharedDelegate),
("test_simpleUploadWithDelegate", test_simpleUploadWithDelegate),
+ ("test_concurrentRequests", test_concurrentRequests),
]
}
@@ -312,6 +313,8 @@
waitForExpectations(timeout: 12)
}
+ /*
+ // temporarily disabled (https://bugs.swift.org/browse/SR-5751)
func test_httpRedirectionTimeout() {
let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/UnitedStates"
var req = URLRequest(url: URL(string: urlString)!)
@@ -324,11 +327,14 @@
if let e = error as? URLError {
XCTAssertEqual(e.code, .timedOut, "Unexpected error code")
return
+ } else {
+ XCTFail("test unexpectedly succeeded (response=\(response.debugDescription))")
}
}
task.resume()
waitForExpectations(timeout: 12)
}
+ */
func test_http0_9SimpleResponses() {
for brokenCity in ["Pompeii", "Sodom"] {
@@ -459,6 +465,34 @@
task.resume()
waitForExpectations(timeout: 20)
}
+
+ func test_concurrentRequests() {
+ let syncQ = dispatchQueueMake("test_dataTaskWithURL.syncQ")
+ var dataTasks: [DataTask] = []
+ let g = dispatchGroupMake()
+ for f in 0..<640 {
+ g.enter()
+ let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/Nepal"
+ let expectation = self.expectation(description: "GET \(urlString) [\(f)]: with a delegate")
+ globalDispatchQueue.async {
+ let url = URL(string: urlString)!
+ let d = DataTask(with: expectation)
+ d.run(with: url)
+ syncQ.async {
+ dataTasks.append(d)
+ g.leave()
+ }
+ }
+ }
+ waitForExpectations(timeout: 12)
+ g.wait()
+ for d in syncQ.sync(execute: {dataTasks}) {
+ if !d.error {
+ XCTAssertEqual(d.capital, "Kathmandu", "test_dataTaskWithURLRequest returned an unexpected result")
+ }
+ }
+ }
+
}
class SharedDelegate: NSObject {
@@ -488,19 +522,73 @@
}
class DataTask : NSObject {
+ let syncQ = dispatchQueueMake("org.swift.TestFoundation.TestURLSession.DataTask.syncQ")
let dataTaskExpectation: XCTestExpectation!
- var capital = "unknown"
- var session: URLSession! = nil
- var task: URLSessionDataTask! = nil
- var cancelExpectation: XCTestExpectation?
- var responseReceivedExpectation: XCTestExpectation?
- var protocols: [AnyClass]?
+ let protocols: [AnyClass]?
+
+ /* all the following var _XYZ need to be synchronized on syncQ.
+ We can't just assert that we're on main thread here as we're modified in the URLSessionDataDelegate extension
+ for DataTask
+ */
+ var _capital = "unknown"
+ var capital: String {
+ get {
+ return self.syncQ.sync { self._capital }
+ }
+ set {
+ self.syncQ.sync { self._capital = newValue }
+ }
+ }
+ var _session: URLSession! = nil
+ var session: URLSession! {
+ get {
+ return self.syncQ.sync { self._session }
+ }
+ set {
+ self.syncQ.sync { self._session = newValue }
+ }
+ }
+ var _task: URLSessionDataTask! = nil
+ var task: URLSessionDataTask! {
+ get {
+ return self.syncQ.sync { self._task }
+ }
+ set {
+ self.syncQ.sync { self._task = newValue }
+ }
+ }
+ var _cancelExpectation: XCTestExpectation?
+ var cancelExpectation: XCTestExpectation? {
+ get {
+ return self.syncQ.sync { self._cancelExpectation }
+ }
+ set {
+ self.syncQ.sync { self._cancelExpectation = newValue }
+ }
+ }
+ var _responseReceivedExpectation: XCTestExpectation?
+ var responseReceivedExpectation: XCTestExpectation? {
+ get {
+ return self.syncQ.sync { self._responseReceivedExpectation }
+ }
+ set {
+ self.syncQ.sync { self._responseReceivedExpectation = newValue }
+ }
+ }
- public var error = false
+ private var _error = false
+ public var error: Bool {
+ get {
+ return self.syncQ.sync { self._error }
+ }
+ set {
+ self.syncQ.sync { self._error = newValue }
+ }
+ }
init(with expectation: XCTestExpectation, protocolClasses: [AnyClass]? = nil) {
dataTaskExpectation = expectation
- protocols = protocolClasses
+ protocols = protocolClasses
}
func run(with request: URLRequest) {
diff --git a/TestFoundation/TestUnit.swift b/TestFoundation/TestUnit.swift
new file mode 100644
index 0000000..c934965
--- /dev/null
+++ b/TestFoundation/TestUnit.swift
@@ -0,0 +1,103 @@
+// This source file is part of the Swift.org open source project
+//
+// Copyright (c) 2014 - 2017 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
+//
+
+#if DEPLOYMENT_RUNTIME_OBJC || os(Linux)
+ import Foundation
+ import XCTest
+#else
+ import SwiftFoundation
+ import SwiftXCTest
+#endif
+
+class TestUnit: XCTestCase {
+
+ static var allTests: [(String, (TestUnit) -> () throws -> Void)] {
+ return [
+ ("test_equality", test_equality),
+ ]
+ }
+
+ func test_equality() {
+ let s1 = "a"
+ let s2 = "ab"
+
+ let u1 = Unit(symbol: s1)
+ let u2 = Unit(symbol: s1)
+ let u3 = Unit(symbol: s2)
+
+ XCTAssertEqual(u1, u2)
+ XCTAssertEqual(u2, u1)
+ XCTAssertNotEqual(u1, u3)
+ XCTAssertNotEqual(u3, u1)
+
+ let uc1 = UnitConverterLinear(coefficient: 1, constant: 2)
+ let uc2 = UnitConverterLinear(coefficient: 1, constant: 3)
+
+ let d1 = Dimension(symbol: s1, converter: uc1)
+ let d2 = Dimension(symbol: s1, converter: uc1)
+ let d3 = Dimension(symbol: s2, converter: uc1)
+ let d4 = Dimension(symbol: s1, converter: uc2)
+
+ XCTAssertEqual(d1, d2)
+ XCTAssertEqual(d2, d1)
+ XCTAssertNotEqual(d1, d3)
+ XCTAssertNotEqual(d3, d1)
+ XCTAssertNotEqual(d1, d4)
+ XCTAssertNotEqual(d4, d1)
+
+ XCTAssertEqual(u1, d1)
+ XCTAssertNotEqual(d1, u1)
+
+ func testEquality<T: Dimension>(ofDimensionSubclass: T.Type) {
+ let u0 = Unit(symbol: s1)
+ let d1 = Dimension(symbol: s1, converter: uc1)
+
+ let u1 = T(symbol: s1, converter: uc1)
+ let u2 = T(symbol: s1, converter: uc1)
+ let u3 = T(symbol: s1, converter: uc2)
+ let u4 = T(symbol: s2, converter: uc1)
+
+ XCTAssertEqual(u1, u2)
+ XCTAssertEqual(u2, u1)
+ XCTAssertNotEqual(u1, u3)
+ XCTAssertNotEqual(u3, u1)
+ XCTAssertNotEqual(u1, u4)
+ XCTAssertNotEqual(u4, u1)
+
+ XCTAssertEqual(u0, u1)
+ XCTAssertNotEqual(u1, u0)
+
+ XCTAssertEqual(d1, u1)
+ XCTAssertNotEqual(u1, d1)
+ }
+
+ testEquality(ofDimensionSubclass: UnitAcceleration.self)
+ testEquality(ofDimensionSubclass: UnitAngle.self)
+ testEquality(ofDimensionSubclass: UnitArea.self)
+ testEquality(ofDimensionSubclass: UnitConcentrationMass.self)
+ testEquality(ofDimensionSubclass: UnitDispersion.self)
+ testEquality(ofDimensionSubclass: UnitDuration.self)
+ testEquality(ofDimensionSubclass: UnitElectricCharge.self)
+ testEquality(ofDimensionSubclass: UnitElectricCurrent.self)
+ testEquality(ofDimensionSubclass: UnitElectricPotentialDifference.self)
+ testEquality(ofDimensionSubclass: UnitElectricResistance.self)
+ testEquality(ofDimensionSubclass: UnitEnergy.self)
+ testEquality(ofDimensionSubclass: UnitFrequency.self)
+ testEquality(ofDimensionSubclass: UnitFuelEfficiency.self)
+ testEquality(ofDimensionSubclass: UnitIlluminance.self)
+ testEquality(ofDimensionSubclass: UnitLength.self)
+ testEquality(ofDimensionSubclass: UnitMass.self)
+ testEquality(ofDimensionSubclass: UnitPower.self)
+ testEquality(ofDimensionSubclass: UnitPressure.self)
+ testEquality(ofDimensionSubclass: UnitSpeed.self)
+ testEquality(ofDimensionSubclass: UnitTemperature.self)
+ testEquality(ofDimensionSubclass: UnitVolume.self)
+ }
+
+}
diff --git a/TestFoundation/TestUnitConverter.swift b/TestFoundation/TestUnitConverter.swift
index e03e6bb..ed1d374 100644
--- a/TestFoundation/TestUnitConverter.swift
+++ b/TestFoundation/TestUnitConverter.swift
@@ -24,6 +24,7 @@
("test_baseUnit", test_linearity),
("test_linearity", test_linearity),
("test_bijectivity", test_bijectivity),
+ ("test_equality", test_equality),
]
}
@@ -267,5 +268,22 @@
XCTAssertEqual(testIdentity(UnitVolume.imperialGallons), 1, accuracy: delta)
XCTAssertEqual(testIdentity(UnitVolume.metricCups), 1, accuracy: delta)
}
+
+ func test_equality() {
+ let u1 = UnitConverterLinear(coefficient: 1, constant: 2)
+ let u2 = UnitConverterLinear(coefficient: 1, constant: 2)
+ XCTAssertEqual(u1, u2)
+ XCTAssertEqual(u2, u1)
+
+ let u3 = UnitConverterLinear(coefficient: 1, constant: 3)
+ XCTAssertNotEqual(u1, u3)
+ XCTAssertNotEqual(u3, u1)
+
+ let u4 = UnitConverterLinear(coefficient: 2, constant: 2)
+ XCTAssertNotEqual(u1, u4)
+ XCTAssertNotEqual(u4, u1)
+
+ // Cannot test UnitConverterReciprocal due to no support for @testable import.
+ }
}
diff --git a/TestFoundation/TestXMLParser.swift b/TestFoundation/TestXMLParser.swift
index 84e5782..b08f0b3 100644
--- a/TestFoundation/TestXMLParser.swift
+++ b/TestFoundation/TestXMLParser.swift
@@ -86,10 +86,10 @@
return xmlUnderTest
}
if let open = encoding.range(of: "(") {
- encoding = encoding.substring(from: open.upperBound)
+ encoding = String(encoding[open.upperBound...])
}
if let close = encoding.range(of: ")") {
- encoding = encoding.substring(to: close.lowerBound)
+ encoding = String(encoding[..<close.lowerBound])
}
return "<?xml version='1.0' encoding='\(encoding.uppercased())' standalone='no'?>\n\(xmlUnderTest)\n"
}
diff --git a/TestFoundation/main.swift b/TestFoundation/main.swift
index 6e288ac..9ae6d0f 100644
--- a/TestFoundation/main.swift
+++ b/TestFoundation/main.swift
@@ -73,7 +73,7 @@
testCase(TestNSSet.allTests),
testCase(TestStream.allTests),
testCase(TestNSString.allTests),
-// testCase(TestThread.allTests),
+ testCase(TestThread.allTests),
testCase(TestProcess.allTests),
testCase(TestNSTextCheckingResult.allTests),
testCase(TestTimer.allTests),
@@ -104,4 +104,5 @@
testCase(TestMassFormatter.allTests),
testCase(TestJSONEncoder.allTests),
testCase(TestCodable.allTests),
+ testCase(TestUnit.allTests),
])