Note that the most common use cases for bit masks include packing a collection of specific, meaningful Boolean flags into a single word-sized value, and performing tests against those flags. Swift provides facilities for this in the OptionSet
type.
struct Bits: OptionSet {
let rawValue: UInt // unsigned is usually best for bitfield math
init(rawValue: UInt) { self.rawValue = rawValue }
static let one = Bits(rawValue: 0b1)
static let two = Bits(rawValue: 0b10)
static let four = Bits(rawValue: 0b100)
static let eight = Bits(rawValue: 0b1000)
}
let someBits = Bits(rawValue: 13)
// the following all return true:
someBits.contains(.four)
someBits.isDisjoint(with: .two)
someBits == [.one, .four, .eight]
someBits == [.four, .four, .eight, .one] // set algebra: order/duplicates moot
someBits == Bits(rawValue: 0b1011)
(In real-world use, of course, you'd give each of the "element" values in your OptionSet
type some value that's meaningful to your use case.)
An OptionSet
is actually a single value (that supports set algebra in terms of itself, instead of in terms of an element type), so it's not a collection — that is, it doesn't provide a way to enumerate its elements. But if the way you intend to use a bitmask only requires setting and testing specific flags (or combinations of flags), maybe you don't need a way to enumerate elements.
And if you do need to enumerate elements, but also want all the set algebra features of OptionSet
, you can combine OptionSet
with bit-splitting math such as that found in @rmaddy's answer:
extension OptionSet where RawValue == UInt { // try being more generic?
var discreteElements: [Self] {
var result = [Self]()
var bitmask = self.rawValue
var element = RawValue(1)
while bitmask > 0 && element < ~RawValue.allZeros {
if bitmask & 0b1 == 1 {
result.append(Self(rawValue: element))
}
bitmask >>= 1
element <<= 1
}
return result
}
}
someBits.discreteElements.map({$0.rawValue}) // => [1, 4, 8]