blob: 88c048764027004a3dfca853c3b773d334f22e7e [file] [log] [blame]
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
import CoreFoundation
open class NSSet : NSObject, NSCopying, NSMutableCopying, NSSecureCoding, NSCoding {
private let _cfinfo = _CFInfo(typeID: CFSetGetTypeID())
internal var _storage: Set<NSObject>
open var count: Int {
guard type(of: self) === NSSet.self || type(of: self) === NSMutableSet.self || type(of: self) === NSCountedSet.self else {
NSRequiresConcreteImplementation()
}
return _storage.count
}
open func member(_ object: Any) -> Any? {
guard type(of: self) === NSSet.self || type(of: self) === NSMutableSet.self || type(of: self) === NSCountedSet.self else {
NSRequiresConcreteImplementation()
}
let value = __SwiftValue.store(object)
guard let idx = _storage.firstIndex(of: value) else { return nil }
return _storage[idx]
}
open func objectEnumerator() -> NSEnumerator {
guard type(of: self) === NSSet.self || type(of: self) === NSMutableSet.self || type(of: self) === NSCountedSet.self else {
NSRequiresConcreteImplementation()
}
return NSGeneratorEnumerator(_storage.map { __SwiftValue.fetch(nonOptional: $0) }.makeIterator())
}
public convenience override init() {
self.init(objects: [], count: 0)
}
public init(objects: UnsafePointer<AnyObject>!, count cnt: Int) {
_storage = Set(minimumCapacity: cnt)
super.init()
let buffer = UnsafeBufferPointer(start: objects, count: cnt)
for obj in buffer {
_storage.insert(__SwiftValue.store(obj))
}
}
public convenience init(array: [Any]) {
let buffer = UnsafeMutablePointer<AnyObject>.allocate(capacity: array.count)
for (idx, element) in array.enumerated() {
buffer.advanced(by: idx).initialize(to: __SwiftValue.store(element))
}
self.init(objects: buffer, count: array.count)
buffer.deinitialize(count: array.count)
buffer.deallocate()
}
public convenience init(set: Set<AnyHashable>) {
self.init(set: set, copyItems: false)
}
public convenience init(set anSet: NSSet) {
self.init(array: anSet.allObjects)
}
public convenience init(set: Set<AnyHashable>, copyItems flag: Bool) {
if flag {
self.init(array: set.map {
if let item = $0 as? NSObject {
return item.copy()
} else {
return $0
}
})
} else {
self.init(array: Array(set))
}
}
public convenience init(object: Any) {
self.init(array: [object])
}
internal class func _objects(from aDecoder: NSCoder, allowDecodingNonindexedArrayKey: Bool = true) -> [NSObject] {
guard aDecoder.allowsKeyedCoding else {
preconditionFailure("Unkeyed coding is unsupported.")
}
if (allowDecodingNonindexedArrayKey && type(of: aDecoder) == NSKeyedUnarchiver.self) || aDecoder.containsValue(forKey: "NS.objects") {
let objects = aDecoder._decodeArrayOfObjectsForKey("NS.objects")
return objects as! [NSObject]
} else {
var objects: [NSObject] = []
var count = 0
var key: String { return "NS.object.\(count)" }
while aDecoder.containsValue(forKey: key) {
let object = aDecoder.decodeObject(forKey: key)
objects.append(object as! NSObject)
count += 1
}
return objects
}
}
public required convenience init?(coder aDecoder: NSCoder) {
self.init(array: NSSet._objects(from: aDecoder))
}
open func encode(with aCoder: NSCoder) {
// The encoding of a NSSet is identical to the encoding of an NSArray of its contents
self.allObjects._nsObject.encode(with: aCoder)
}
open override func copy() -> Any {
return copy(with: nil)
}
open func copy(with zone: NSZone? = nil) -> Any {
if type(of: self) === NSSet.self {
// return self for immutable type
return self
} else if type(of: self) === NSMutableSet.self {
let set = NSSet()
set._storage = self._storage
return set
}
return NSSet(array: self.allObjects)
}
open override func mutableCopy() -> Any {
return mutableCopy(with: nil)
}
open func mutableCopy(with zone: NSZone? = nil) -> Any {
if type(of: self) === NSSet.self || type(of: self) === NSMutableSet.self {
// always create and return an NSMutableSet
let mutableSet = NSMutableSet()
mutableSet._storage = self._storage
return mutableSet
}
return NSMutableSet(array: self.allObjects)
}
public static var supportsSecureCoding: Bool {
return true
}
override open var description: String {
return description(withLocale: nil)
}
open func description(withLocale locale: Locale?) -> String {
return description(withLocale: locale, indent: 0)
}
private func description(withLocale locale: Locale?, indent level: Int) -> String {
var descriptions = [String]()
for obj in self._storage {
if let string = obj as? String {
descriptions.append(string)
} else if let array = obj as? [Any] {
descriptions.append(NSArray(array: array).description(withLocale: locale, indent: level + 1))
} else if let dict = obj as? [AnyHashable : Any] {
descriptions.append(dict._bridgeToObjectiveC().description(withLocale: locale, indent: level + 1))
} else if let set = obj as? Set<AnyHashable> {
descriptions.append(set._bridgeToObjectiveC().description(withLocale: locale, indent: level + 1))
} else {
descriptions.append("\(obj)")
}
}
var indent = ""
for _ in 0..<level {
indent += " "
}
var result = indent + "{(\n"
for idx in 0..<self.count {
result += indent + " " + descriptions[idx]
if idx + 1 < self.count {
result += ",\n"
} else {
result += "\n"
}
}
result += indent + ")}"
return result
}
override open var _cfTypeID: CFTypeID {
return CFSetGetTypeID()
}
open override func isEqual(_ value: Any?) -> Bool {
switch value {
case let other as NSSet:
// Check that this isn't a subclass — if both self and other are subclasses, this would otherwise turn into an infinite loop if other.isEqual(_:) calls super.
if (type(of: self) == NSSet.self || type(of: self) == NSMutableSet.self) &&
(type(of: other) != NSSet.self && type(of: other) != NSMutableSet.self) {
return other.isEqual(self) // This ensures NSCountedSet overriding this method is respected no matter which side of the equality it appears on.
} else {
return isEqual(to: Set._unconditionallyBridgeFromObjectiveC(other))
}
case let other as Set<AnyHashable>:
return isEqual(to: other)
default:
return false
}
}
open override var hash: Int {
return self.count
}
open var allObjects: [Any] {
if type(of: self) === NSSet.self || type(of: self) === NSMutableSet.self {
return _storage.map { __SwiftValue.fetch(nonOptional: $0) }
} else {
let enumerator = objectEnumerator()
var items = [Any]()
while let val = enumerator.nextObject() {
items.append(val)
}
return items
}
}
open func anyObject() -> Any? {
return objectEnumerator().nextObject()
}
open func contains(_ anObject: Any) -> Bool {
return member(anObject) != nil
}
open func intersects(_ otherSet: Set<AnyHashable>) -> Bool {
if count < otherSet.count {
for item in self {
if otherSet.contains(item as! AnyHashable) {
return true
}
}
return false
} else {
return otherSet.contains { obj in contains(obj) }
}
}
open func isEqual(to otherSet: Set<AnyHashable>) -> Bool {
return count == otherSet.count && isSubset(of: otherSet)
}
open func isSubset(of otherSet: Set<AnyHashable>) -> Bool {
// If self is larger then self cannot be a subset of otherSet
if count > otherSet.count {
return false
}
// `true` if we don't contain any object that `otherSet` doesn't contain.
for item in self {
if !otherSet.contains(item as! AnyHashable) {
return false
}
}
return true
}
open func adding(_ anObject: Any) -> Set<AnyHashable> {
return self.addingObjects(from: [anObject])
}
open func addingObjects(from other: Set<AnyHashable>) -> Set<AnyHashable> {
var result = Set<AnyHashable>(minimumCapacity: Swift.max(count, other.count))
if type(of: self) === NSSet.self || type(of: self) === NSMutableSet.self {
result.formUnion(_storage.map { __SwiftValue.fetch(nonOptional: $0) as! AnyHashable })
} else {
for case let obj as NSObject in self {
_ = result.insert(obj)
}
}
return result.union(other)
}
open func addingObjects(from other: [Any]) -> Set<AnyHashable> {
var result = Set<AnyHashable>(minimumCapacity: count)
if type(of: self) === NSSet.self || type(of: self) === NSMutableSet.self {
result.formUnion(_storage.map { __SwiftValue.fetch(nonOptional: $0) as! AnyHashable })
} else {
for case let obj as AnyHashable in self {
result.insert(obj)
}
}
for case let obj as AnyHashable in other {
result.insert(obj)
}
return result
}
open func enumerateObjects(_ block: (Any, UnsafeMutablePointer<ObjCBool>) -> Swift.Void) {
enumerateObjects(options: [], using: block)
}
open func enumerateObjects(options opts: NSEnumerationOptions = [], using block: (Any, UnsafeMutablePointer<ObjCBool>) -> Swift.Void) {
var stop : ObjCBool = false
for obj in self {
withUnsafeMutablePointer(to: &stop) { stop in
block(obj, stop)
}
if stop.boolValue {
break
}
}
}
open func objects(passingTest predicate: (Any, UnsafeMutablePointer<ObjCBool>) -> Bool) -> Set<AnyHashable> {
return objects(options: [], passingTest: predicate)
}
open func objects(options opts: NSEnumerationOptions = [], passingTest predicate: (Any, UnsafeMutablePointer<ObjCBool>) -> Bool) -> Set<AnyHashable> {
var result = Set<AnyHashable>()
enumerateObjects(options: opts) { obj, stopp in
if predicate(obj, stopp) {
result.insert(obj as! AnyHashable)
}
}
return result
}
open func sortedArray(using sortDescriptors: [NSSortDescriptor]) -> [Any] {
return allObjects._nsObject.sortedArray(using: sortDescriptors)
}
}
extension NSSet : _CFBridgeable, _SwiftBridgeable {
internal var _cfObject: CFSet { return unsafeBitCast(self, to: CFSet.self) }
internal var _swiftObject: Set<NSObject> { return Set._unconditionallyBridgeFromObjectiveC(self) }
}
extension CFSet : _NSBridgeable, _SwiftBridgeable {
internal var _nsObject: NSSet { return unsafeBitCast(self, to: NSSet.self) }
internal var _swiftObject: Set<NSObject> { return _nsObject._swiftObject }
}
extension NSMutableSet {
internal var _cfMutableObject: CFMutableSet { return unsafeBitCast(self, to: CFMutableSet.self) }
}
extension Set : _NSBridgeable, _CFBridgeable {
internal var _nsObject: NSSet { return _bridgeToObjectiveC() }
internal var _cfObject: CFSet { return _nsObject._cfObject }
}
extension NSSet : Sequence {
public typealias Iterator = NSEnumerator.Iterator
public func makeIterator() -> Iterator {
return self.objectEnumerator().makeIterator()
}
}
extension NSSet: CustomReflectable {
public var customMirror: Mirror {
return Mirror(reflecting: self._storage)
}
}
open class NSMutableSet : NSSet {
open func add(_ object: Any) {
guard type(of: self) === NSMutableSet.self else {
NSRequiresConcreteImplementation()
}
_storage.insert(__SwiftValue.store(object))
}
open func remove(_ object: Any) {
guard type(of: self) === NSMutableSet.self else {
NSRequiresConcreteImplementation()
}
_storage.remove(__SwiftValue.store(object))
}
override public init(objects: UnsafePointer<AnyObject>!, count cnt: Int) {
super.init(objects: objects, count: cnt)
}
public convenience init() {
self.init(capacity: 0)
}
public required init(capacity numItems: Int) {
super.init(objects: [], count: 0)
}
public required convenience init?(coder aDecoder: NSCoder) {
self.init(array: NSSet._objects(from: aDecoder))
}
open func addObjects(from array: [Any]) {
if type(of: self) === NSMutableSet.self {
for case let obj in array {
_storage.insert(__SwiftValue.store(obj))
}
} else {
array.forEach(add)
}
}
open func intersect(_ otherSet: Set<AnyHashable>) {
if type(of: self) === NSMutableSet.self {
_storage.formIntersection(otherSet.map { __SwiftValue.store($0) })
} else {
for obj in self {
if !otherSet.contains(obj as! AnyHashable) {
remove(obj)
}
}
}
}
open func minus(_ otherSet: Set<AnyHashable>) {
if type(of: self) === NSMutableSet.self {
_storage.subtract(otherSet.map { __SwiftValue.store($0) })
} else {
otherSet.forEach(remove)
}
}
open func removeAllObjects() {
if type(of: self) === NSMutableSet.self {
_storage.removeAll()
} else {
forEach(remove)
}
}
open func union(_ otherSet: Set<AnyHashable>) {
if type(of: self) === NSMutableSet.self {
_storage.formUnion(otherSet.map { __SwiftValue.store($0) })
} else {
otherSet.forEach(add)
}
}
open func setSet(_ otherSet: Set<AnyHashable>) {
if type(of: self) === NSMutableSet.self {
_storage = Set(otherSet.map { __SwiftValue.store($0) })
} else {
removeAllObjects()
union(otherSet)
}
}
}
/**************** Counted Set ****************/
open class NSCountedSet : NSMutableSet {
// Note: in 5.0 and earlier, _table contained the object's exact count.
// In 5.1 and earlier, it contains the count minus one. This allows us to have a quick 'is this set just like a regular NSSet' flag (if this table is empty, then all objects in it exist at most once in it.)
internal var _table: [NSObject: Int] = [:]
public required init(capacity numItems: Int) {
_table = Dictionary<NSObject, Int>()
super.init(capacity: numItems)
}
public convenience init() {
self.init(capacity: 0)
}
public convenience init(array: [Any]) {
self.init(capacity: array.count)
for object in array {
add(__SwiftValue.store(object))
}
}
public convenience init(set: Set<AnyHashable>) {
self.init(array: Array(set))
}
private enum NSCodingKeys {
static let maximumAllowedCount = UInt.max >> 4
static let countKey = "NS.count"
static func objectKey(atIndex index: Int64) -> String { return "NS.object\(index)" }
static func objectCountKey(atIndex index: Int64) -> String { return "NS.count\(index)" }
}
public required convenience init?(coder: NSCoder) {
func fail(_ message: String) {
coder.failWithError(NSError(domain: NSCocoaErrorDomain, code: NSCoderReadCorruptError, userInfo: [NSLocalizedDescriptionKey: message]))
}
guard coder.allowsKeyedCoding else {
fail("NSCountedSet requires keyed coding to be archived.")
return nil
}
let count = coder.decodeInt64(forKey: NSCodingKeys.countKey)
guard count >= 0, UInt(count) <= NSCodingKeys.maximumAllowedCount else {
fail("cannot decode set with \(count) elements in this version")
return nil
}
var objects: [(object: Any, count: Int64)] = []
for i in 0 ..< count {
let objectKey = NSCodingKeys.objectKey(atIndex: i)
let countKey = NSCodingKeys.objectCountKey(atIndex: i)
guard coder.containsValue(forKey: objectKey) && coder.containsValue(forKey: countKey) else {
fail("Mismatch in count stored (\(count)) vs. count present (\(i))")
return nil
}
guard let object = coder.decodeObject(forKey: objectKey) else {
fail("Decode failure at index \(i) - item nil")
return nil
}
let itemCount = coder.decodeInt64(forKey: countKey)
guard itemCount > 0 else {
fail("Decode failure at index \(i) - itemCount zero")
return nil
}
guard UInt(itemCount) <= NSCodingKeys.maximumAllowedCount else {
fail("Cannot store \(itemCount) instances of item \(object) in this version")
return nil
}
objects.append((object, itemCount))
}
self.init()
for value in objects {
for _ in 0 ..< value.count {
add(value.object)
}
}
}
open override func encode(with coder: NSCoder) {
func fail(_ message: String) {
coder.failWithError(NSError(domain: NSCocoaErrorDomain, code: NSCoderReadCorruptError, userInfo: [NSLocalizedDescriptionKey: message]))
}
guard coder.allowsKeyedCoding else {
fail("NSCountedSet requires keyed coding to be archived.")
return
}
coder.encode(Int64(self.count), forKey: NSCodingKeys.countKey)
var index: Int64 = 0
for object in self {
coder.encode(object, forKey: NSCodingKeys.objectKey(atIndex: index))
coder.encode(Int64(count(for: object)), forKey: NSCodingKeys.objectCountKey(atIndex: index))
index += 1
}
}
open override func copy(with zone: NSZone? = nil) -> Any {
if type(of: self) === NSCountedSet.self {
let countedSet = NSCountedSet()
countedSet._storage = self._storage
countedSet._table = self._table
return countedSet
}
return NSCountedSet(array: self.allObjects)
}
open override func mutableCopy(with zone: NSZone? = nil) -> Any {
if type(of: self) === NSCountedSet.self {
let countedSet = NSCountedSet()
countedSet._storage = self._storage
countedSet._table = self._table
return countedSet
}
return NSCountedSet(array: self.allObjects)
}
open func count(for object: Any) -> Int {
guard type(of: self) === NSCountedSet.self else {
NSRequiresConcreteImplementation()
}
let value = __SwiftValue.store(object)
if let count = _table[value] {
return count + 1
} else if _storage.contains(value) {
return 1
} else {
return 0
}
}
open override func add(_ object: Any) {
guard type(of: self) === NSCountedSet.self else {
NSRequiresConcreteImplementation()
}
let value = __SwiftValue.store(object)
if _storage.contains(value) {
_table[value, default: 0] += 1
} else {
_storage.insert(value)
}
}
open override func remove(_ object: Any) {
guard type(of: self) === NSCountedSet.self else {
NSRequiresConcreteImplementation()
}
let value = __SwiftValue.store(object)
if let count = _table[value] {
precondition(count > 0)
_table[value] = count == 1 ? nil : count - 1
} else if _storage.contains(value) {
_table.removeValue(forKey: value)
_storage.remove(value)
}
}
open override func removeAllObjects() {
if type(of: self) === NSCountedSet.self {
_storage.removeAll()
_table.removeAll()
} else {
forEach(remove)
}
}
open override func isEqual(_ value: Any?) -> Bool {
if let countedSet = value as? NSCountedSet {
guard count == countedSet.count else { return false }
for object in self {
if !countedSet.contains(object) || count(for: object) != countedSet.count(for: object) {
return false
}
}
return true
}
if _table.isEmpty {
return super.isEqual(value)
} else {
return false
}
}
// The hash of a NSSet in s-c-f is its count, which is the same among equal NSCountedSets as well,
// so just using the superclass's implementation works fine.
}
extension NSSet : _StructTypeBridgeable {
public typealias _StructType = Set<AnyHashable>
public func _bridgeToSwift() -> _StructType {
return _StructType._unconditionallyBridgeFromObjectiveC(self)
}
}