Skip to content

Commit 0fd0be8

Browse files
committed
[Foundation] add additional conformances and functionality to NSRange
1 parent 3e46ab6 commit 0fd0be8

File tree

3 files changed

+102
-21
lines changed

3 files changed

+102
-21
lines changed

stdlib/public/SDK/Foundation/NSRange.swift

Lines changed: 102 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,33 +12,117 @@
1212

1313
@_exported import Foundation // Clang module
1414

15+
extension NSRange : Hashable {
16+
public var hashValue: Int {
17+
return Int(bitPattern: UInt(location) ^ UInt(location))
18+
}
19+
20+
public static func==(_ lhs: NSRange, _ rhs: NSRange) -> Bool {
21+
return lhs.location == rhs.location && rhs.length == rhs.length
22+
}
23+
}
24+
25+
extension NSRange : CustomStringConvertible, CustomDebugStringConvertible {
26+
public var description: String { return "{\(location), \(length)}" }
27+
public var debugDescription: String { return "{\(location), \(length)}" }
28+
}
29+
30+
extension NSRange {
31+
public init?(_ string: String) {
32+
if string.isEmpty {
33+
// fail early if the string is empty
34+
return nil
35+
}
36+
let scanner = Scanner(string: string)
37+
let digitSet = CharacterSet.decimalDigits
38+
let _ = scanner.scanUpToCharacters(from: digitSet, into: nil)
39+
if scanner.isAtEnd {
40+
// fail early if there are no decimal digits
41+
return nil
42+
}
43+
var location = 0
44+
guard scanner.scanInt(&location) else {
45+
return nil
46+
}
47+
if scanner.isAtEnd {
48+
// return early if there are no more characters after the first int in the string
49+
return nil
50+
}
51+
let _ = scanner.scanUpToCharacters(from: digitSet, into: nil)
52+
if scanner.isAtEnd {
53+
// return early if there are no integer characters after the first int in the string
54+
return nil
55+
}
56+
var length = 0
57+
guard scanner.scanInt(&length) else {
58+
return nil
59+
}
60+
61+
self.location = location
62+
self.length = length
63+
}
64+
}
65+
66+
extension NSRange {
67+
public var lowerBound: Int { return location }
68+
69+
public var upperBound: Int { return location + length }
70+
71+
public func contains(_ index: Int) -> Bool { return (!(index < location) && (index - location) < length) }
72+
73+
public mutating func formUnion(_ other: NSRange) {
74+
self = union(other)
75+
}
76+
77+
public func union(_ other: NSRange) -> NSRange {
78+
let max1 = location + length
79+
let max2 = other.location + other.length
80+
let maxend = (max1 < max2) ? max2 : max1
81+
let minloc = location < other.location ? location : other.location
82+
return NSRange(location: minloc, length: maxend - minloc)
83+
}
84+
85+
public func intersection(_ other: NSRange) -> NSRange? {
86+
let max1 = location + length
87+
let max2 = other.location + other.length
88+
let minend = (max1 < max2) ? max1 : max2
89+
if other.location <= location && location < max2 {
90+
return NSRange(location: location, length: minend - location)
91+
} else if location <= other.location && other.location < max1 {
92+
return NSRange(location: other.location, length: minend - other.location);
93+
}
94+
return nil
95+
}
96+
}
97+
98+
1599
//===----------------------------------------------------------------------===//
16100
// Ranges
17101
//===----------------------------------------------------------------------===//
18102

19103
extension NSRange {
20-
public init(_ x: Range<Int>) {
21-
location = x.lowerBound
22-
length = x.count
23-
}
24-
25-
// FIXME(ABI)#75 (Conditional Conformance): this API should be an extension on Range.
26-
// Can't express it now because the compiler does not support conditional
27-
// extensions with type equality constraints.
28-
public func toRange() -> Range<Int>? {
29-
if location == NSNotFound { return nil }
30-
return location..<(location+length)
31-
}
104+
public init(_ x: Range<Int>) {
105+
location = x.lowerBound
106+
length = x.count
107+
}
108+
109+
// FIXME(ABI)#75 (Conditional Conformance): this API should be an extension on Range.
110+
// Can't express it now because the compiler does not support conditional
111+
// extensions with type equality constraints.
112+
public func toRange() -> Range<Int>? {
113+
if location == NSNotFound { return nil }
114+
return location..<(location+length)
115+
}
32116
}
33117

34118
extension NSRange : CustomReflectable {
35-
public var customMirror: Mirror {
36-
return Mirror(self, children: ["location": location, "length": length])
37-
}
119+
public var customMirror: Mirror {
120+
return Mirror(self, children: ["location": location, "length": length])
121+
}
38122
}
39123

40124
extension NSRange : CustomPlaygroundQuickLookable {
41-
public var customPlaygroundQuickLook: PlaygroundQuickLook {
42-
return .range(Int64(location), Int64(length))
43-
}
125+
public var customPlaygroundQuickLook: PlaygroundQuickLook {
126+
return .range(Int64(location), Int64(length))
127+
}
44128
}

test/Compatibility/bridging-nsnumber-and-nsvalue.swift.gyb

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ extension Equatable {
4545
}
4646
}
4747
extension Hashable { public var hashValue: Int { fatalError("trill hiphy") } }
48-
extension NSRange: Hashable {}
4948
extension CGSize: Hashable {}
5049
extension CGPoint: Hashable {}
5150
extension CGRect: Hashable {}

test/Constraints/bridging-nsnumber-and-nsvalue.swift.gyb

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ coercionTypes = {
2929
'CGRect',
3030
'CGPoint',
3131
'CGSize',
32-
'NSRange',
3332
],
3433
}
3534
}%
@@ -41,7 +40,6 @@ extension Equatable {
4140
}
4241
}
4342
extension Hashable { public var hashValue: Int { fatalError("trill hiphy") } }
44-
extension NSRange: Hashable {}
4543
extension CGSize: Hashable {}
4644
extension CGPoint: Hashable {}
4745
extension CGRect: Hashable {}

0 commit comments

Comments
 (0)