Swift 3.0 OptionSet Bit Test - swift

With the following OptionSet:
struct StatusOptions : OptionSet {
let rawValue: Int
static let CountdownDuration0 = StatusOptions(rawValue: 1 << 0)
static let CountdownDuration1 = StatusOptions(rawValue: 1 << 1)
static let CountdownDuration2 = StatusOptions(rawValue: 1 << 2)
static let CountdownDuration3 = StatusOptions(rawValue: 1 << 3)
static let CountdownDuration4 = StatusOptions(rawValue: 1 << 4)
static let CountdownDuration5 = StatusOptions(rawValue: 1 << 5)
static let HomeMode = StatusOptions(rawValue: 1 << 6)
static let AwayMode = StatusOptions(rawValue: 1 << 7)
static let Disarmed: StatusOptions = []
static let ArmedHome: StatusOptions = .HomeMode
static let ArmedAway: StatusOptions = .AwayMode
static let ArmedBoth: StatusOptions = [.HomeMode, .AwayMode]
static let ArmingCountdown: StatusOptions = [.CountdownDuration0, .CountdownDuration1, .CountdownDuration2, .CountdownDuration3, .CountdownDuration4, .CountdownDuration5]
}
How do I compare a byte
var statusFlags: UInt8 = 0b00000000
To the Options?
The following is what I'd think would work, but causes a playground error:
if (statusFlags & .Disarmed) {
print("Disarmed")
}
Thanks

Since .Disarmed is zero, you say:
if statusFlags == .Disarmed
or even more simply:
if statusFlags.isEmpty
For other values, you say, e.g.:
if statusFlags.contains(.CountdownDuration3)
That is the whole point of an OptionSet. You use set operations, not arithmetic bit operations.
But for this to work, so that you can take advantage of OptionSet's yummy goodness, statusFlags needs to be a StatusOptions. You can do that by way of the StatusOptions rawValue: initializer:
let statusFlags = StatusOptions(rawValue:0b00000000)
statusFlags.isEmpty // true
(Note that in Swift 3, a zero bitmask like .Disarmed probably wouldn't even be used. Zero is just an empty set.)

Related

What is a convenient way to work with OptionSet?

I work on a project with many bitwise option sets and each of them contains many options with all option e.g:
struct MyOption: OptionSet {
let rawValue: Int
static let a = Self(rawValue: 1 << 0)
static let b = Self(rawValue: 1 << 1)
static let c = Self(rawValue: 1 << 2)
...
static let last = Self(rawValue: 1 << N)
static let all: Self = [.a, .b, .c, ..., .last]
}
It requires to maintain much of similar code so is it any way to eliminate hardcoded bitwise shift operations and the all option?
You can use next OptionSet's extension which implements all option and a convenient initializer:
extension OptionSet where RawValue == Int {
static var all: Self {
Self.init(rawValue: Int.max)
}
init(_ shift: Int) {
self.init(rawValue: 1 << shift)
}
}
Then you can re-write your option set:
struct Option: OptionSet {
let rawValue: Int
static let a = Self(0)
static let b = Self(1)
static let c = Self(2)
}
Option.a.rawValue // 1
Option.b.rawValue // 2
Option.c.rawValue // 4
let options: Option = [.a, .b]
Option.all.contains(options) // true

Find the name that was associated to a value of an OptionSet

So I have an OptionSet:
struct Ability: OptionSet {
let rawValue: Int
static let create = Ability(rawValue: 1 << 0)
static let read = Ability(rawValue: 1 << 1)
static let update = Ability(rawValue: 1 << 2)
static let delete = Ability(rawValue: 1 << 3)
init(rawValue: Int) {
self.rawValue = rawValue
}
}
I'm just writing one as an example here. In reality my code doesn't know what OptionSet it has exactly, it only knows it gets an OptionSet. What I want to do is be able to get the name of the option from its rawValue as a String.
So basically I want to write a function with the following signature:
func convertOptionToString<OS: OptionSet>(optionSet: OS) -> String
Where convertOptionToString(Ability.read) would return "read"
Of course I know that OptionSets can also hold multiple values, but I already know how I would deal with that situation, so that's not a problem I need help with.
I am hoping that this problem can be solved using some kind of smart Reflection, but I haven't been able to find out how yet. Anybody want to give it a go?
Update your struct to conform to CustomStringConvertible and implement the description property:
struct Ability: OptionSet, CustomStringConvertible {
let rawValue: Int
static let create = Ability(rawValue: 1 << 0)
static let read = Ability(rawValue: 1 << 1)
static let update = Ability(rawValue: 1 << 2)
static let delete = Ability(rawValue: 1 << 3)
init(rawValue: Int) {
self.rawValue = rawValue
}
var description: String {
var vals = [String]()
if self.contains(.create) {
vals.append("create")
}
if self.contains(.read) {
vals.append("read")
}
if self.contains(.update) {
vals.append("update")
}
if self.contains(.delete) {
vals.append("delete")
}
return vals.joined(separator: ",")
}
}
print(Ability.read)
let opts: Ability = [ .read, .delete ]
print(opts)
Output:
read
read,delete

Map OptionSetType to Array

Given the following:
struct Weekdays: OptionSetType {
let rawValue: Int
init(rawValue: Int) { self.rawValue = rawValue }
static let Monday = Weekdays(rawValue: 1)
static let Tuesday = Weekdays(rawValue: 2)
static let Wednesday = Weekdays(rawValue: 4)
static let Thursday = Weekdays(rawValue: 8)
static let allOptions: [Weekdays] = [.Monday, .Tuesday, .Wednesday, .Thursday]
}
I can convert an array of Ints into a Weekdays object by doing this:
let arr = [1, 4]
let weekdays = arr.reduce(Weekdays()) { $0.union(Weekdays(rawValue: $1)) }
My question is, how do I take a Weekdays object and convert it into an array of Ints?
(Not necessarily better, but a different way to look at it and slightly
more general).
OptionSetType inherits from RawRepresentable and therefore can be
converted from and to the associated raw type, which in your case is
Int.
So the "missing link" is a conversion between the raw value (e.g. 5)
and an integer array of the bitwise components (e.g. [1, 4]).
This can be done with an Int extension method:
extension Int {
init(bitComponents : [Int]) {
self = bitComponents.reduce(0, combine: (+))
}
func bitComponents() -> [Int] {
return (0 ..< 8*sizeof(Int)).map( { 1 << $0 }).filter( { self & $0 != 0 } )
}
}
Then your conversion from an array to a Weekdays object becomes
let arr : [Int] = [1, 4]
let weekdays = Weekdays(rawValue: Int(bitComponents: arr))
print(weekdays)
// app.Weekdays(rawValue: 5)
and the reverse conversion
let array = weekdays.rawValue.bitComponents()
print(array)
// [1, 4]
Advantages:
The explicit definition of allOptions: is not needed.
It can be applied to other option set types (which have Int
as a raw value).
One could also try to define the conversions as a protocol extension,
e.g. of IntegerType, so that the same works with other integer raw types as well. However, this seems to be a bit complicated/ugly
because the left shift operator << is not part of the
IntegerType (or any) protocol.
Update for Swift 3:
extension Int {
init(bitComponents : [Int]) {
self = bitComponents.reduce(0, +)
}
func bitComponents() -> [Int] {
return (0 ..< 8*MemoryLayout<Int>.size).map( { 1 << $0 }).filter( { self & $0 != 0 } )
}
}
Not exactly answering the question, but might be useful to others. Based on Martin's answer I extract back the component objects:
extension FixedWidthInteger {
init(bitComponents : [Self]) {
self = bitComponents.reduce(0, +)
}
var bitComponents : [Self] {
(0 ..< Self.bitWidth).map { 1 << $0 } .filter { self & $0 != 0 }
}
}
extension OptionSet where RawValue: FixedWidthInteger, Self == Self.Element {
var components : [Self] { rawValue.bitComponents.map(Self.init) }
}
As I was writing the question, I figured it out:
let array = Weekdays.allOptions.filter { weekdays.contains($0) }.map { $0.rawValue }
Is there a better way?
You can improve the context of your extension by defining it conditionally on OptionSet.
extension OptionSet where RawValue: UnsignedInteger {
var individualCases: [Self] {
return (0..<(8 * MemoryLayout<RawValue>.size))
.map { bitsToShift in RawValue(1 << bitsToShift) } // Get every possible single-bit flag
.filter { (powerOfTwo: RawValue) -> Bool in rawValue & powerOfTwo != 0 } // filter out only the cases the receiver contains
.map { Self(rawValue: $0) } // create the `OptionSet` (single bit) type
}
}
let weekdays = Weekdays(rawValue: 0b11111)
weekdays.individualCases.map { $0.rawValue } // [1, 2, 4, 8, 16]
A warning: On my 13" 2019 MacBookPro, I had to provide all of the explicit types above to keep the methods type checking under 1500ms in Swift 5.0.
Thanks to MartinR for the inspiration to loop of the memory layout size.
To complete the example, I've updated the weekday type to Swift 5 below, and explicitly used the UInt8 type to make the individualCases more efficient. With UInt, it would loop over the first map and filter 64 times each, with UInt8 it only loops 8 times.
struct Weekdays: OptionSet {
let rawValue: UInt8
static let Monday = Weekdays(rawValue: 1)
static let Tuesday = Weekdays(rawValue: 2)
static let Wednesday = Weekdays(rawValue: 4)
static let Thursday = Weekdays(rawValue: 8)
static let Friday = Weekdays(rawValue: 16)
}

Given a hexadecimal string in Swift, convert to hex value

Suppose I am given a string like this:
D7C17A4F
How do I convert each individual character to a hex value?
So D should be 0xD, 7 should be 0x7…
Right now, I have each individual character represented as it's ASCII value. D is 68, 7 is 55. I'm trying to pack those two values into one byte. For example: D7 becomes 0xD7 and C1 becomes 0xC1. I can't do that using the ASCII decimal values though.
A possible solution:
let string = "D7C17A4F"
let chars = Array(string)
let numbers = map (stride(from: 0, to: chars.count, by: 2)) {
strtoul(String(chars[$0 ..< $0+2]), nil, 16)
}
Using the approach from https://stackoverflow.com/a/29306523/1187415,
the string is split into substrings of two characters.
Each substring is interpreted as a sequence of digits
in base 16, and converted to a number with strtoul().
Verify the result:
println(numbers)
// [215, 193, 122, 79]
println(map(numbers, { String(format: "%02X", $0) } ))
// [D7, C1, 7A, 4F]
Update for Swift 2 (Xcode 7):
let string = "D7C17A4F"
let chars = Array(string.characters)
let numbers = 0.stride(to: chars.count, by: 2).map {
UInt8(String(chars[$0 ..< $0+2]), radix: 16) ?? 0
}
print(numbers)
or
let string = "D7C17A4F"
var numbers = [UInt8]()
var from = string.startIndex
while from != string.endIndex {
let to = from.advancedBy(2, limit: string.endIndex)
numbers.append(UInt8(string[from ..< to], radix: 16) ?? 0)
from = to
}
print(numbers)
The second solution looks a bit more complicated but has the small
advantage that no additional chars array is needed.
Swift 3 version, modified from #Martin R's answer. This variant also accepts incoming string with odd length.
let string = "D7C17A4F"
let chars = Array(string.characters)
let numbers = stride(from: 0, to: chars.count, by: 2).map() {
strtoul(String(chars[$0 ..< min($0 + 2, chars.count)]), nil, 16)
}
Use chunks!
"D7C17A4F"
.chunks(ofCount: 2)
.map { UInt8($0, radix: 0x10)! }
My variation of #martin-r answer:
extension String {
func hexToByteArray() -> [UInt8] {
let byteCount = self.utf8.count / 2
var array = [UInt8](count: byteCount, repeatedValue: 0)
var from = self.startIndex
for i in 0..<byteCount {
let to = from.successor()
let sub = self.substringWithRange(from...to)
array[i] = UInt8(sub, radix: 16) ?? 0
from = to.successor()
}
return array
}
}
here is the more generic, "pure swift" approach (no Foundation required :-))
extension UnsignedInteger {
var hex: String {
var str = String(self, radix: 16, uppercase: true)
while str.characters.count < 2 * MemoryLayout<Self>.size {
str.insert("0", at: str.startIndex)
}
return str
}
}
extension Array where Element: UnsignedInteger {
var hex: String {
var str = ""
self.forEach { (u) in
str.append(u.hex)
}
return str
}
}
let str = [UInt8(1),22,63,41].hex // "01163F29"
let str2 = [UInt(1),22,63,41].hex // "00000000000000010000000000000016000000000000003F0000000000000029"
extension String {
func toUnsignedInteger<T:UnsignedInteger>()->[T]? {
var ret = [T]()
let nibles = MemoryLayout<T>.size * 2
for i in stride(from: 0, to: characters.count, by: nibles) {
let start = self.index(startIndex, offsetBy: i)
guard let end = self.index(start, offsetBy: nibles, limitedBy: endIndex),
let ui = UIntMax(self[start..<end], radix: 16) else { return nil }
ret.append(T(ui))
}
return ret
}
}
let u0:[UInt8]? = str.toUnsignedInteger() // [1, 22, 63, 41]
let u1 = "F2345f".toUnsignedInteger() as [UInt8]? // [18, 52, 95]
let u2 = "12345f".toUnsignedInteger() as [UInt16]? // nil
let u3 = "12345g".toUnsignedInteger() as [UInt8]? // nil
let u4 = "12345f".toUnsignedInteger() as [UInt]? // nil
let u5 = "12345678".toUnsignedInteger() as [UInt8]? // [18, 52, 86, 120]
let u6 = "12345678".toUnsignedInteger() as [UInt16]? // [4660, 22136]
let u7 = "1234567812345678".toUnsignedInteger() as [UInt]? // [1311768465173141112]
It is very easily to do the same for SignedInteger as well, but better approach will be to map results to signed type
let u8 = u1?.map { Int8(bitPattern: $0) } // [-14, 52, 95]

How to convert hex number to bin in Swift?

I have string variable:
var str = "239A23F"
How do I convert this string to a binary number?
str.toInt() does not work.
You can use NSScanner() from the Foundation framework:
let scanner = NSScanner(string: str)
var result : UInt32 = 0
if scanner.scanHexInt(&result) {
println(result) // 37331519
}
Or the BSD library function strtoul()
let num = strtoul(str, nil, 16)
println(num) // 37331519
As of Swift 2 (Xcode 7), all integer types have an
public init?(_ text: String, radix: Int = default)
initializer, so that a pure Swift solution is available:
let str = "239A23F"
let num = Int(str, radix: 16)