1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376
|
//===--- OptionSet.swift --------------------------------------------------===//
//
// 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 https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
/// A type that presents a mathematical set interface to a bit set.
///
/// You use the `OptionSet` protocol to represent bitset types, where
/// individual bits represent members of a set. Adopting this protocol in
/// your custom types lets you perform set-related operations such as
/// membership tests, unions, and intersections on those types. What's more,
/// when implemented using specific criteria, adoption of this protocol
/// requires no extra work on your part.
///
/// When creating an option set, include a `rawValue` property in your type
/// declaration. For your type to automatically receive default implementations
/// for set-related operations, the `rawValue` property must be of a type that
/// conforms to the `FixedWidthInteger` protocol, such as `Int` or `UInt8`.
/// Next, create unique options as static properties of your custom type using
/// unique powers of two (1, 2, 4, 8, 16, and so forth) for each individual
/// property's raw value so that each property can be represented by a single
/// bit of the type's raw value.
///
/// For example, consider a custom type called `ShippingOptions` that is an
/// option set of the possible ways to ship a customer's purchase.
/// `ShippingOptions` includes a `rawValue` property of type `Int` that stores
/// the bit mask of available shipping options. The static members `nextDay`,
/// `secondDay`, `priority`, and `standard` are unique, individual options.
///
/// struct ShippingOptions: OptionSet {
/// let rawValue: Int
///
/// static let nextDay = ShippingOptions(rawValue: 1 << 0)
/// static let secondDay = ShippingOptions(rawValue: 1 << 1)
/// static let priority = ShippingOptions(rawValue: 1 << 2)
/// static let standard = ShippingOptions(rawValue: 1 << 3)
///
/// static let express: ShippingOptions = [.nextDay, .secondDay]
/// static let all: ShippingOptions = [.express, .priority, .standard]
/// }
///
/// Declare additional preconfigured option set values as static properties
/// initialized with an array literal containing other option values. In the
/// example, because the `express` static property is assigned an array
/// literal with the `nextDay` and `secondDay` options, it will contain those
/// two elements.
///
/// Using an Option Set Type
/// ========================
///
/// When you need to create an instance of an option set, assign one of the
/// type's static members to your variable or constant. Alternatively, to
/// create an option set instance with multiple members, assign an array
/// literal with multiple static members of the option set. To create an empty
/// instance, assign an empty array literal to your variable.
///
/// let singleOption: ShippingOptions = .priority
/// let multipleOptions: ShippingOptions = [.nextDay, .secondDay, .priority]
/// let noOptions: ShippingOptions = []
///
/// Use set-related operations to check for membership and to add or remove
/// members from an instance of your custom option set type. The following
/// example shows how you can determine free shipping options based on a
/// customer's purchase price:
///
/// let purchasePrice = 87.55
///
/// var freeOptions: ShippingOptions = []
/// if purchasePrice > 50 {
/// freeOptions.insert(.priority)
/// }
///
/// if freeOptions.contains(.priority) {
/// print("You've earned free priority shipping!")
/// } else {
/// print("Add more to your cart for free priority shipping!")
/// }
/// // Prints "You've earned free priority shipping!"
public protocol OptionSet: SetAlgebra, RawRepresentable {
// We can't constrain the associated Element type to be the same as
// Self, but we can do almost as well with a default and a
// constrained extension
/// The element type of the option set.
///
/// To inherit all the default implementations from the `OptionSet` protocol,
/// the `Element` type must be `Self`, the default.
associatedtype Element = Self
// FIXME: This initializer should just be the failable init from
// RawRepresentable. Unfortunately, current language limitations
// that prevent non-failable initializers from forwarding to
// failable ones would prevent us from generating the non-failing
// default (zero-argument) initializer. Since OptionSet's main
// purpose is to create convenient conformances to SetAlgebra,
// we opt for a non-failable initializer.
/// Creates a new option set from the given raw value.
///
/// This initializer always succeeds, even if the value passed as `rawValue`
/// exceeds the static properties declared as part of the option set. This
/// example creates an instance of `ShippingOptions` with a raw value beyond
/// the highest element, with a bit mask that effectively contains all the
/// declared static members.
///
/// let extraOptions = ShippingOptions(rawValue: 255)
/// print(extraOptions.isStrictSuperset(of: .all))
/// // Prints "true"
///
/// - Parameter rawValue: The raw value of the option set to create. Each bit
/// of `rawValue` potentially represents an element of the option set,
/// though raw values may include bits that are not defined as distinct
/// values of the `OptionSet` type.
init(rawValue: RawValue)
}
/// `OptionSet` requirements for which default implementations
/// are supplied.
///
/// - Note: A type conforming to `OptionSet` can implement any of
/// these initializers or methods, and those implementations will be
/// used in lieu of these defaults.
extension OptionSet {
/// Returns a new option set of the elements contained in this set, in the
/// given set, or in both.
///
/// This example uses the `union(_:)` method to add two more shipping options
/// to the default set.
///
/// let defaultShipping = ShippingOptions.standard
/// let memberShipping = defaultShipping.union([.secondDay, .priority])
/// print(memberShipping.contains(.priority))
/// // Prints "true"
///
/// - Parameter other: An option set.
/// - Returns: A new option set made up of the elements contained in this
/// set, in `other`, or in both.
@inlinable // generic-performance
public func union(_ other: Self) -> Self {
var r: Self = Self(rawValue: self.rawValue)
r.formUnion(other)
return r
}
/// Returns a new option set with only the elements contained in both this
/// set and the given set.
///
/// This example uses the `intersection(_:)` method to limit the available
/// shipping options to what can be used with a PO Box destination.
///
/// // Can only ship standard or priority to PO Boxes
/// let poboxShipping: ShippingOptions = [.standard, .priority]
/// let memberShipping: ShippingOptions =
/// [.standard, .priority, .secondDay]
///
/// let availableOptions = memberShipping.intersection(poboxShipping)
/// print(availableOptions.contains(.priority))
/// // Prints "true"
/// print(availableOptions.contains(.secondDay))
/// // Prints "false"
///
/// - Parameter other: An option set.
/// - Returns: A new option set with only the elements contained in both this
/// set and `other`.
@inlinable // generic-performance
public func intersection(_ other: Self) -> Self {
var r = Self(rawValue: self.rawValue)
r.formIntersection(other)
return r
}
/// Returns a new option set with the elements contained in this set or in
/// the given set, but not in both.
///
/// - Parameter other: An option set.
/// - Returns: A new option set with only the elements contained in either
/// this set or `other`, but not in both.
@inlinable // generic-performance
public func symmetricDifference(_ other: Self) -> Self {
var r = Self(rawValue: self.rawValue)
r.formSymmetricDifference(other)
return r
}
}
/// `OptionSet` requirements for which default implementations are
/// supplied when `Element == Self`, which is the default.
///
/// - Note: A type conforming to `OptionSet` can implement any of
/// these initializers or methods, and those implementations will be
/// used in lieu of these defaults.
extension OptionSet where Element == Self {
/// Returns a Boolean value that indicates whether a given element is a
/// member of the option set.
///
/// This example uses the `contains(_:)` method to check whether next-day
/// shipping is in the `availableOptions` instance.
///
/// let availableOptions = ShippingOptions.express
/// if availableOptions.contains(.nextDay) {
/// print("Next day shipping available")
/// }
/// // Prints "Next day shipping available"
///
/// - Parameter member: The element to look for in the option set.
/// - Returns: `true` if the option set contains `member`; otherwise,
/// `false`.
@inlinable // generic-performance
public func contains(_ member: Self) -> Bool {
return self.isSuperset(of: member)
}
/// Adds the given element to the option set if it is not already a member.
///
/// In the following example, the `.secondDay` shipping option is added to
/// the `freeOptions` option set if `purchasePrice` is greater than 50.0. For
/// the `ShippingOptions` declaration, see the `OptionSet` protocol
/// discussion.
///
/// let purchasePrice = 87.55
///
/// var freeOptions: ShippingOptions = [.standard, .priority]
/// if purchasePrice > 50 {
/// freeOptions.insert(.secondDay)
/// }
/// print(freeOptions.contains(.secondDay))
/// // Prints "true"
///
/// - Parameter newMember: The element to insert.
/// - Returns: `(true, newMember)` if `newMember` was not contained in
/// `self`. Otherwise, returns `(false, oldMember)`, where `oldMember` is
/// the member of the set equal to `newMember`.
@inlinable // generic-performance
@discardableResult
public mutating func insert(
_ newMember: Element
) -> (inserted: Bool, memberAfterInsert: Element) {
let oldMember = self.intersection(newMember)
let shouldInsert = oldMember != newMember
let result = (
inserted: shouldInsert,
memberAfterInsert: shouldInsert ? newMember : oldMember)
if shouldInsert {
self.formUnion(newMember)
}
return result
}
/// Removes the given element and all elements subsumed by it.
///
/// In the following example, the `.priority` shipping option is removed from
/// the `options` option set. Attempting to remove the same shipping option
/// a second time results in `nil`, because `options` no longer contains
/// `.priority` as a member.
///
/// var options: ShippingOptions = [.secondDay, .priority]
/// let priorityOption = options.remove(.priority)
/// print(priorityOption == .priority)
/// // Prints "true"
///
/// print(options.remove(.priority))
/// // Prints "nil"
///
/// In the next example, the `.express` element is passed to `remove(_:)`.
/// Although `.express` is not a member of `options`, `.express` subsumes
/// the remaining `.secondDay` element of the option set. Therefore,
/// `options` is emptied and the intersection between `.express` and
/// `options` is returned.
///
/// let expressOption = options.remove(.express)
/// print(expressOption == .express)
/// // Prints "false"
/// print(expressOption == .secondDay)
/// // Prints "true"
///
/// - Parameter member: The element of the set to remove.
/// - Returns: The intersection of `[member]` and the set, if the
/// intersection was nonempty; otherwise, `nil`.
@inlinable // generic-performance
@discardableResult
public mutating func remove(_ member: Element) -> Element? {
let intersectionElements = intersection(member)
guard !intersectionElements.isEmpty else {
return nil
}
self.subtract(member)
return intersectionElements
}
/// Inserts the given element into the set.
///
/// If `newMember` is not contained in the set but subsumes current members
/// of the set, the subsumed members are returned.
///
/// var options: ShippingOptions = [.secondDay, .priority]
/// let replaced = options.update(with: .express)
/// print(replaced == .secondDay)
/// // Prints "true"
///
/// - Returns: The intersection of `[newMember]` and the set if the
/// intersection was nonempty; otherwise, `nil`.
@inlinable // generic-performance
@discardableResult
public mutating func update(with newMember: Element) -> Element? {
let r = self.intersection(newMember)
self.formUnion(newMember)
return r.isEmpty ? nil : r
}
}
/// `OptionSet` requirements for which default implementations are
/// supplied when `RawValue` conforms to `FixedWidthInteger`,
/// which is the usual case. Each distinct bit of an option set's
/// `.rawValue` corresponds to a disjoint value of the `OptionSet`.
///
/// - `union` is implemented as a bitwise "or" (`|`) of `rawValue`s
/// - `intersection` is implemented as a bitwise "and" (`&`) of
/// `rawValue`s
/// - `symmetricDifference` is implemented as a bitwise "exclusive or"
/// (`^`) of `rawValue`s
///
/// - Note: A type conforming to `OptionSet` can implement any of
/// these initializers or methods, and those implementations will be
/// used in lieu of these defaults.
extension OptionSet where RawValue: FixedWidthInteger {
/// Creates an empty option set.
///
/// This initializer creates an option set with a raw value of zero.
@inlinable // generic-performance
public init() {
self.init(rawValue: 0)
}
/// Inserts the elements of another set into this option set.
///
/// This method is implemented as a `|` (bitwise OR) operation on the
/// two sets' raw values.
///
/// - Parameter other: An option set.
@inlinable // generic-performance
public mutating func formUnion(_ other: Self) {
self = Self(rawValue: self.rawValue | other.rawValue)
}
/// Removes all elements of this option set that are not
/// also present in the given set.
///
/// This method is implemented as a `&` (bitwise AND) operation on the
/// two sets' raw values.
///
/// - Parameter other: An option set.
@inlinable // generic-performance
public mutating func formIntersection(_ other: Self) {
self = Self(rawValue: self.rawValue & other.rawValue)
}
/// Replaces this set with a new set containing all elements
/// contained in either this set or the given set, but not in both.
///
/// This method is implemented as a `^` (bitwise XOR) operation on the two
/// sets' raw values.
///
/// - Parameter other: An option set.
@inlinable // generic-performance
public mutating func formSymmetricDifference(_ other: Self) {
self = Self(rawValue: self.rawValue ^ other.rawValue)
}
}
|