blob: 9f5dc5acf220d0408fb533db6b6cd5155cfc5f24 [file] [log] [blame]
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2016, 2018 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
open class NSIndexPath : NSObject, NSCopying, NSSecureCoding {
private var _indexes : [Int]
override public init() {
_indexes = []
}
public init(indexes: UnsafePointer<Int>?, length: Int) {
if length == 0 {
_indexes = []
} else {
_indexes = Array(UnsafeBufferPointer(start: indexes!, count: length))
}
}
private init(indexes: [Int]) {
_indexes = indexes
}
open override func copy() -> Any {
return copy(with: nil)
}
open func copy(with zone: NSZone? = nil) -> Any {
return self
}
public convenience init(index: Int) {
self.init(indexes: [index])
}
fileprivate enum NSCodingKeys {
static let lengthKey = "NSIndexPathLength"
static let singleValueKey = "NSIndexPathValue"
static let dataKey = "NSIndexPathData"
}
open func encode(with aCoder: NSCoder) {
guard aCoder.allowsKeyedCoding else {
aCoder.failWithError(NSError(domain: NSCocoaErrorDomain, code: NSCoderReadCorruptError, userInfo: [NSLocalizedDescriptionKey: "Cannot be serialized with a coder that does not support keyed archives"]))
return
}
let length = self.length
aCoder.encode(length, forKey: NSCodingKeys.lengthKey)
switch length {
case 0:
break
case 1:
aCoder.encode(index(atPosition: 0), forKey: NSCodingKeys.singleValueKey)
default:
var sequence = PackedUIntSequence(data: Data(capacity: length * 2 + 16))
for position in 0 ..< length {
sequence.append(UInt(index(atPosition: position)))
}
aCoder.encode(sequence.data, forKey: NSCodingKeys.dataKey)
}
}
public required convenience init?(coder aDecoder: NSCoder) {
guard aDecoder.allowsKeyedCoding else {
aDecoder.failWithError(NSError(domain: NSCocoaErrorDomain, code: NSCoderReadCorruptError, userInfo: [NSLocalizedDescriptionKey: "Cannot be deserialized with a coder that does not support keyed archives"]))
return nil
}
guard aDecoder.containsValue(forKey: NSCodingKeys.lengthKey) else {
aDecoder.failWithError(NSError(domain: NSCocoaErrorDomain, code: NSCoderReadCorruptError, userInfo: [NSLocalizedDescriptionKey: "Decoder did not provide a length value for the indexPath."]))
return nil
}
let len = aDecoder.decodeInteger(forKey: NSCodingKeys.lengthKey)
guard len > 0 else {
self.init()
return
}
switch len {
case 0:
self.init()
return
case 1:
guard aDecoder.containsValue(forKey: NSCodingKeys.singleValueKey) else {
aDecoder.failWithError(NSError(domain: NSCocoaErrorDomain, code: NSCoderReadCorruptError, userInfo: [NSLocalizedDescriptionKey: "Decoder did not provide indexPath data."]))
return nil
}
let index = aDecoder.decodeInteger(forKey: NSCodingKeys.singleValueKey)
self.init(index: index)
return
default:
guard let bytes = aDecoder.decodeObject(of: NSData.self, forKey: NSCodingKeys.dataKey) else {
aDecoder.failWithError(NSError(domain: NSCocoaErrorDomain, code: NSCoderReadCorruptError, userInfo: [NSLocalizedDescriptionKey: "Range data missing."]))
return nil
}
let sequence = PackedUIntSequence(data: bytes._swiftObject)
guard sequence.count == len else {
aDecoder.failWithError(NSError(domain: NSCocoaErrorDomain, code: NSCoderReadCorruptError, userInfo: [NSLocalizedDescriptionKey: "Range data did not match expected length."]))
return nil
}
self.init(indexes: sequence.integers)
}
}
public static var supportsSecureCoding: Bool { return true }
open func adding(_ index: Int) -> IndexPath {
return IndexPath(indexes: _indexes + [index])
}
open func removingLastIndex() -> IndexPath {
if _indexes.count <= 1 {
return IndexPath(indexes: [])
} else {
return IndexPath(indexes: [Int](_indexes[0..<_indexes.count - 1]))
}
}
open func index(atPosition position: Int) -> Int {
return _indexes[position]
}
open var length: Int {
return _indexes.count
}
/*!
@abstract Copies the indexes stored in this index path from the positions specified by positionRange into indexes.
@param indexes Buffer of at least as many NSUIntegers as specified by the length of positionRange. On return, this memory will hold the index path's indexes.
@param positionRange A range of valid positions within this index path. If the location plus the length of positionRange is greater than the length of this index path, this method raises an NSRangeException.
@discussion
It is the developer’s responsibility to allocate the memory for the C array.
*/
open func getIndexes(_ indexes: UnsafeMutablePointer<Int>, range positionRange: NSRange) {
for (pos, idx) in _indexes[positionRange.location ..< NSMaxRange(positionRange)].enumerated() {
indexes.advanced(by: pos).pointee = idx
}
}
@available(*, unavailable, renamed: "getIndex(_:range:)")
open func getIndexes(_ indexes: UnsafeMutablePointer<Int>) {
NSUnsupported()
}
// comparison support
// sorting an array of indexPaths using this comparison results in an array representing nodes in depth-first traversal order
open func compare(_ otherObject: IndexPath) -> ComparisonResult {
let thisLength = length
let otherLength = otherObject.count
let minLength = thisLength >= otherLength ? otherLength : thisLength
for pos in 0..<minLength {
let otherValue = otherObject[pos]
let thisValue = index(atPosition: pos)
if thisValue < otherValue {
return .orderedAscending
} else if thisValue > otherValue {
return .orderedDescending
}
}
if thisLength > otherLength {
return .orderedDescending
} else if thisLength < otherLength {
return .orderedAscending
}
return .orderedSame
}
open override var hash: Int {
var hasher = Hasher()
for i in 0 ..< length {
hasher.combine(index(atPosition: i))
}
return hasher.finalize()
}
open override func isEqual(_ object: Any?) -> Bool {
guard let indexPath = object as? NSIndexPath,
indexPath.length == self.length else { return false }
let length = self.length
for i in 0 ..< length {
if index(atPosition: i) != indexPath.index(atPosition: i) {
return false
}
}
return true
}
}
extension NSIndexPath : _StructTypeBridgeable {
public typealias _StructType = IndexPath
public func _bridgeToSwift() -> IndexPath {
return IndexPath._unconditionallyBridgeFromObjectiveC(self)
}
}
extension NSIndexPath : _SwiftBridgeable {
var _swiftObject: IndexPath {
return _bridgeToSwift()
}
}