Skip to content

Instantly share code, notes, and snippets.

@mika76
Last active September 22, 2015 16:17
Show Gist options
  • Save mika76/a65dc0a7bd41e23bb247 to your computer and use it in GitHub Desktop.
Save mika76/a65dc0a7bd41e23bb247 to your computer and use it in GitHub Desktop.
//
// BitwiseOptions.swift
//
// Created by Gregory Higley on 11/24/14.
// Copyright (c) 2014 Prosumma LLC. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
/*
BitwiseOptions is a simple protocol (BitwiseOptionsType) and class (BitwiseOptions<T>)
that allow bitmask-like operations on native Swift enumerations:
enum Animal: BitwiseOptionsType {
case Chicken
case Cow
case Goat
static let allOptions = [.Chicken, .Cow, .Goat]
}
var animals = Animal.Chicken | Animal.Goat // Automatically creates an instance of BitwiseOptions<Animal>
animals ^= .Chicken // Toggle .Chicken so we only have .Goat
Since Swift enumerations won't work with Objective-C, neither will BitwiseOptions. This
is for native Swift code only.
No actual bitmasking of any kind is going on here. There is no math. These are effectively
operations on sets.
All operations produce an instance of BitwiseOptions<T>. If you wish to allow bitmasks
to be passed to functions, you should use this type:
func putIntoBarn(animals: BitwiseOptions<Animal>)
Then you can say:
putIntoBarn(.Chicken | .Goat)
putIntoBarn(nil)
BitwiseOptions<Animal> implements NilLiteralConvertible, so nil gets you an empty set of options.
This allows us to use a little trick to say we want all options:
putIntoBarn(~nil)
What we are saying here is that we want the complement (~) of the empty set of options, which is the
set of all options.
This brings us to the only real problem with BitwiseOptions: single enumeration values. Because
Swift does not support implicit conversions (except of various kinds of literals), there are
four ways to pass a single enumeration value:
putIntoBarn(BitwiseOptions<Animal>(.Chicken))
putIntoBarn([.Chicken])
putIntoBarn(|.Chicken)
putIntoBarn(nil | .Chicken)
The second one works because BitwiseOptions<T> implements ArrayLiteralConvertible. The third one
works because I have defined | as a prefix operator to handle this situation. The fourth one
works for obvious reasons. I recommend the third or fourth versions, which also work with assignments:
var animal = |.Chicken
var animal = nil | .Chicken
Using BitwiseOptions is pretty easy. Just create an enumeration and make it conform to the
BitwiseOptionsType protocol:
enum Animal: BitwiseOptionsType {
case Chicken
case Cow
case Goat
static let allOptions: [Animal] = [.Chicken, .Cow, .Goat]
}
The only work you have to do is to add allOptions, whose implementation should be obvious.
The rest you get automatically.
I wrote most of this code in a single furious sitting. It may have some bugs. It may omit some things
that are important for bitmasking and would work with this paradigm. (I implemented what I
tended to use.) Some of the algorithms could be more efficient. So let me know what I can
do to improve this, or just copy it and do it yourself. (Though I'd still love some feedback.)
*/
prefix operator | {}
func intersect<S: SequenceType where S.Generator.Element: Equatable>(sequences: [S]) -> [S.Generator.Element] {
var candidates: [S.Generator.Element]!
if sequences.count > 0 {
candidates = Array<S.Generator.Element>(sequences[0])
for sequence in sequences {
candidates = filter(candidates) { contains(sequence, $0) }
}
} else {
candidates = Array<S.Generator.Element>()
}
return candidates
}
func intersect<S: SequenceType where S.Generator.Element: Equatable>(sequences: S...) -> [S.Generator.Element] {
return intersect(sequences)
}
public struct BitwiseOptions<T: BitwiseOptionsType>: ArrayLiteralConvertible, NilLiteralConvertible, SequenceType, Equatable {
typealias Generator = GeneratorOf<T>
internal let bits: [T: Bool]
init() {
bits = [T: Bool]()
}
init(_ options: [T]) {
bits = BitwiseOptions.merge(options)
}
init(_ options: T...) {
self.init(options)
}
init(_ bitwiseOptions: BitwiseOptions<T>, _ options: T...) {
self.bits = BitwiseOptions.merge(bitwiseOptions.options, options)
}
init(_ bitwiseOptions: BitwiseOptions<T>...) {
self.bits = BitwiseOptions.merge(bitwiseOptions)
}
public init(arrayLiteral elements: T...) {
self.init(elements)
}
public init(nilLiteral: ()) {
self.init()
}
var options: [T] {
return bits.keys.array
}
public func generate() -> Generator {
return GeneratorOf(bits.keys.generate())
}
internal static func merge<S: SequenceType, K: BitwiseOptionsType where S.Generator.Element == K>(sequences: [S]) -> Dictionary<K, Bool> {
var result = Dictionary<K, Bool>()
for sequence in sequences {
for elem in sequence {
result[elem] = true
}
}
return result
}
internal static func merge<S: SequenceType, K: BitwiseOptionsType where S.Generator.Element == K>(sequence: S...) -> Dictionary<K, Bool> {
return merge(sequence)
}
}
public protocol BitwiseOptionsType: Hashable {
static var allOptions: [Self] { get }
func |(lhs: Self, rhs: Self) -> BitwiseOptions<Self>
func |(lhs: BitwiseOptions<Self>, rhs: Self) -> BitwiseOptions<Self>
func |(lhs: Self, rhs: BitwiseOptions<Self>) -> BitwiseOptions<Self>
func |=(inout lhs: BitwiseOptions<Self>, rhs: Self)
func &(lhs: Self, rhs: Self) -> BitwiseOptions<Self>
func &(lhs: BitwiseOptions<Self>, rhs: Self) -> BitwiseOptions<Self>
func &(lhs: Self, rhs: BitwiseOptions<Self>) -> BitwiseOptions<Self>
func &=(inout lhs: BitwiseOptions<Self>, rhs: Self)
func ^(lhs: Self, rhs: Self) -> BitwiseOptions<Self>
func ^(lhs: BitwiseOptions<Self>, rhs: Self) -> BitwiseOptions<Self>
func ^(lhs: Self, BitwiseOptions<Self>) -> BitwiseOptions<Self>
func ^=(inout lhs: BitwiseOptions<Self>, rhs: Self)
func ==(lhs: BitwiseOptions<Self>, rhs: Self) -> Bool
func ==(lhs: Self, rhs: BitwiseOptions<Self>) -> Bool
func !=(lhs: BitwiseOptions<Self>, rhs: Self) -> Bool
func !=(lhs: Self, rhs: BitwiseOptions<Self>) -> Bool
prefix func ~(option: Self) -> BitwiseOptions<Self>
prefix func |(option: Self) -> BitwiseOptions<Self>
}
public func |<T: BitwiseOptionsType>(lhs: T, rhs: T) -> BitwiseOptions<T> {
return BitwiseOptions<T>(lhs, rhs)
}
public func |<T: BitwiseOptionsType>(lhs: BitwiseOptions<T>, rhs: T) -> BitwiseOptions<T> {
return BitwiseOptions<T>(lhs, rhs)
}
public func |<T: BitwiseOptionsType>(lhs: T, rhs: BitwiseOptions<T>) -> BitwiseOptions<T> {
return BitwiseOptions<T>(rhs, lhs)
}
public func |<T: BitwiseOptionsType>(lhs: BitwiseOptions<T>, rhs: BitwiseOptions<T>) -> BitwiseOptions<T> {
return BitwiseOptions<T>(lhs, rhs)
}
public func |=<T: BitwiseOptionsType>(inout lhs: BitwiseOptions<T>, rhs: T) {
lhs = lhs | rhs
}
public func |=<T: BitwiseOptionsType>(inout lhs: BitwiseOptions<T>, rhs: BitwiseOptions<T>) {
lhs = lhs | rhs
}
public func &<T: BitwiseOptionsType>(lhs: T, rhs: T) -> BitwiseOptions<T> {
return lhs == rhs ? BitwiseOptions<T>(lhs) : BitwiseOptions<T>()
}
public func &<T: BitwiseOptionsType>(lhs: BitwiseOptions<T>, rhs: T) -> BitwiseOptions<T> {
return (lhs.bits[rhs] ?? false) ? BitwiseOptions<T>(rhs) : BitwiseOptions<T>()
}
public func &<T: BitwiseOptionsType>(lhs: T, rhs: BitwiseOptions<T>) -> BitwiseOptions<T> {
return rhs & lhs
}
public func &<T: BitwiseOptionsType>(lhs: BitwiseOptions<T>, rhs: BitwiseOptions<T>) -> BitwiseOptions<T> {
return BitwiseOptions<T>(intersect(lhs.options, rhs.options))
}
public func &=<T: BitwiseOptionsType>(inout lhs: BitwiseOptions<T>, rhs: T) {
lhs = lhs & rhs
}
func &=<T: BitwiseOptionsType>(inout lhs: BitwiseOptions<T>, rhs: BitwiseOptions<T>) {
lhs = lhs & rhs
}
public func ^<T: BitwiseOptionsType>(lhs: T, rhs: T) -> BitwiseOptions<T> {
return BitwiseOptions<T>(lhs) ^ BitwiseOptions<T>(rhs)
}
public func ^<T: BitwiseOptionsType>(lhs: BitwiseOptions<T>, rhs: T) -> BitwiseOptions<T> {
return lhs ^ BitwiseOptions<T>(rhs)
}
public func ^<T: BitwiseOptionsType>(lhs: T, rhs: BitwiseOptions<T>) -> BitwiseOptions<T> {
return BitwiseOptions<T>(lhs) ^ rhs
}
public func ^<T: BitwiseOptionsType>(lhs: BitwiseOptions<T>, rhs: BitwiseOptions<T>) -> BitwiseOptions<T> {
let intersection = intersect(lhs.options, rhs.options)
let lhsOptions = filter(lhs.options) { !contains(intersection, $0) }
let rhsOptions = filter(rhs.options) { !contains(intersection, $0) }
return BitwiseOptions<T>(lhsOptions) | BitwiseOptions<T>(rhsOptions)
}
public func ^=<T: BitwiseOptionsType>(inout lhs: BitwiseOptions<T>, rhs: T) {
lhs = lhs ^ rhs
}
public func ^=<T: BitwiseOptionsType>(inout lhs: BitwiseOptions<T>, rhs: BitwiseOptions<T>) {
lhs = lhs ^ rhs
}
public func ==<T: BitwiseOptionsType>(lhs: BitwiseOptions<T>, rhs: T) -> Bool {
return lhs.bits.count == 1 && (lhs.bits[rhs] ?? false)
}
public func ==<T: BitwiseOptionsType>(lhs: T, rhs: BitwiseOptions<T>) -> Bool {
return rhs == lhs
}
public func ==<T: BitwiseOptionsType>(lhs: BitwiseOptions<T>, rhs: BitwiseOptions<T>) -> Bool {
return lhs.bits == rhs.bits
}
public func !=<T: BitwiseOptionsType>(lhs: BitwiseOptions<T>, rhs: T) -> Bool {
return !(lhs == rhs)
}
public func !=<T: BitwiseOptionsType>(lhs: T, rhs: BitwiseOptions<T>) -> Bool {
return !(lhs == rhs)
}
public prefix func ~<T: BitwiseOptionsType>(option: T) -> BitwiseOptions<T> {
return ~BitwiseOptions<T>(option)
}
public prefix func ~<T: BitwiseOptionsType>(bitwiseOptions: BitwiseOptions<T>) -> BitwiseOptions<T> {
return BitwiseOptions<T>(filter(T.allOptions, { !contains(bitwiseOptions.options, $0) }))
}
public prefix func |<T: BitwiseOptionsType>(option: T) -> BitwiseOptions<T> {
return BitwiseOptions<T>(option)
}
@lifely
Copy link

lifely commented Sep 22, 2015

Hey, i've updated this to be swift 2.0 compatible. Check-out my fork if you want to update it.

I know Swift 2.0 solve the problems this used to solve but i was using this in a project and didn't had time to update my base code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment