Idiomatic way to model opcodes with embedded data in Swift - swift

Swift enums seem like a great fit for modeling byte opcodes in Swift3. For example:
enum MathOpCode:UInt8 {
case Add = 0
case Subtract = 1
case Multiply = 2
case Divide = 3
}
let a = 42
let b = 13
let someByte = 2
if let opcode = OpCode(rawValue: someByte) {
switch opcode {
case .Add:
return a + b
case .Subtract:
return a - b
case .Multiply:
return a * b
case .Divide:
return a / b
}
}
This can be really expressive for writing binary protocols. The enum nicely captures the logical opcodes, and the switches read nicely then. Where it's breaking down for me, is where OpCodes include small amounts of data. IOW, let's say I add an OpCode called AddSmallConstant which is meant to represent all opcodes matching 0b01nnnnnn where the top two bits must be 01, but the bottom 6 bits are an embedded constant ranging 0-63. I could add 64 cases to my enum...
enum MathOpCode:UInt8 {
...
case AddConstant0 = 0b01000000
case AddConstant1 = 0b01000001
...
case AddConstant63 = 0b01111111
}
This doesn't really scale well. And to get the embedded value, I have to use rawValue and masking operations to get it anyway. And I can't have a switch statement that looks like
case MathOpCode.AddConstant0...MathOpCode.AddConstant63
to match the whole range, because enum cases can't be turned into ranges. The alternate is to use rawValue all over the place:
switch opCode.rawValue {
case MathOpCode.Add.rawValue:
...
case MathOpCode.Subtract.rawValue:
...
case (MathOpCode.AddConstant0.rawValue)...(MathOpCode.AddConstant63.rawValue):
...
}
Now the enum just seems like baggage, better to just define a bunch of constant let's up top of my my file. Am I missing a better pattern I can use in Swift to express these types of relationships and patterns?

As you know, Swift has a fancy feature called associated value for enum, but unfortunately, you cannot control raw bit representation of enum with associated value.
If you want to control raw bit representation of your MathOpCode type, you may need to create a RawRepresentable type.
For example, you can write something like this:
struct MathOpCode: RawRepresentable {
var rawValue: UInt8
init(rawValue: UInt8) {self.rawValue = rawValue}
static let add = MathOpCode(rawValue: 0)
static let subtract = MathOpCode(rawValue: 1)
static let multiply = MathOpCode(rawValue: 2)
static let divide = MathOpCode(rawValue: 3)
static let addConstant = MathOpCode(rawValue: 0b0100_0000)
static func add(constant value: UInt8) -> MathOpCode {
guard value < 64 else {fatalError("constant out of bounds")}
return MathOpCode(rawValue: self.addConstant.rawValue + value)
}
var isAddConstant: Bool {return self.rawValue & 0b1100_0000 == MathOpCode.addConstant.rawValue}
var constant: UInt8 {return self.rawValue & 0b0011_1111}
}
//Prepare `matches` operator for `switch`.
func ~= (lhs: MathOpCode, rhs: MathOpCode) -> Bool {
return lhs == MathOpCode.addConstant && rhs.isAddConstant
|| lhs == rhs
}
You can use it as:
let opCode = MathOpCode.add(constant: 22)
switch opCode {
case MathOpCode.add:
print("add")
case MathOpCode.subtract:
print("subtract")
case MathOpCode.multiply:
print("multiply")
case MathOpCode.divide:
print("divide")
case MathOpCode.addConstant:
print("addConstant", opCode.constant)
default:
print("invalid opCode")
}
//->addConstant 22

Related

Perform OR operation to all elements in array

I want to extend Arrays of Enums: Numeric with a function OR() that ORs all element in the array.
This is what I came up:
extension Array where Element: RawRepresentable, Element.RawValue: Numeric {
func OR(_: Array) -> Element.RawValue {
return self.map{ $0.rawValue }.reduce(0x00000000){ $0|$1 }
}
}
and this is the error thrown by the compiler:
Cannot invoke 'reduce' with an argument list of type '(Int, (_, _) ->
_)'
I want use it in situation like:
enum RendererFlags: CUnsignedInt {
case software = 0x00000001 // The renderer is a software fallback
case accelerated = 0x00000002 // The renderer uses hardware acceleration
case presentVSync = 0x00000004 // Present is synchronized with the refresh rate
case targetTexture = 0x00000008 // The renderer supports rendering to texture
}
or enums with other numeric rawValues, and then
let flags = [.software, .accelerated]OR()
Where is my error? Why the compiler is not happy with this?
Numeric is not enough for your needs, you need BinaryInteger:
extension Array where Element: RawRepresentable, Element.RawValue: BinaryInteger {
func OR() -> Element.RawValue {
return self.map{ $0.rawValue }.reduce(0) { $0 | $1 }
}
}
Also OR should be without parameters and you need to specify the type somewhere:
let flags = ([.software, .accelerated] as [RendererFlags]).OR()
However, this can be implemented easier using OptionSet:
struct RendererFlags: OptionSet {
let rawValue: CUnsignedInt
static let software = RendererFlags(rawValue: 1 << 0)
static let accelerated = RendererFlags(rawValue: 1 << 1)
static let presentVSync = RendererFlags(rawValue: 1 << 2)
static let targetTexture = RendererFlags(rawValue: 1 << 3)
}
let flags: RendererFlags = [.software, .accelerated]
The OR operation is already implemented for you and the options behave like an array therefore you don't have to worry about mask operations.

swift range greater than lower bound

I need to implement experience filter like this
0 to 2 years
2+ to 4 years
How to express it in swift range?
Problem is I can't express more than 2 to 4 years. While I can do less than upper bounds. e.g. like this
let underTen = 0.0..<10.0
I need something like this (greater than lower bound)
let uptoTwo = 0.0...2.0
let twoPlus = 2.0>..4.0 // compiler error
Currently I am doing
let twoPlus = 2.1...4.0
But this is not perfect.
nextUp from the FloatingPoint protocol
You can make use of the nextUp property of Double, as blueprinted in the FloatingPoint protocol to which Double conforms
nextUp
The least representable value that compares greater than this value.
For any finite value x, x.nextUp is greater than x. ...
I.e.:
let uptoTwo = 0.0...2.0
let twoPlus = 2.0.nextUp...4.0
The property ulp, also blueprinted in the FloatingPoint protocol, has been mentioned in the comments to your question. For most numbers, this is the difference between self and the next greater representable number:
ulp
The unit in the last place of self.
This is the unit of the least significant digit in the significand of
self. For most numbers x, this is the difference between x and
the next greater (in magnitude) representable number. ...
nextUp does, in essence, return the value of self with the addition of ulp. So for your example above, the following is equivalent (whereas, imo, nextup should be preferred in this use case).
let uptoTwo = 0.0...2.0
let twoPlus = (2.0+2.0.ulp)...4.0
You might also want to consider replacing the lower bound literal in twoPlus with the upperBound property of the preceding uptoTwo range:
let uptoTwo = 0.0...2.0 // [0, 2] closed-closed
let twoPlus = uptoTwo.upperBound.nextUp...4.0 // (2, 4] open-closed
if uptoTwo.overlaps(twoPlus) {
print("the two ranges overlap ...")
}
else {
print("ranges are non-overlapping, ok!")
}
// ranges are non-overlapping, ok!
Rather than create a new type of range you can instead create a method that will identify values above the lower bound:
extension ClosedRange {
func containsAboveLowerBound(value:Bound) -> Bool {
if value > self.lowerBound {
return self.contains(value)
}
else {
return false
}
}
}
implementing it like so:
let r = 2.0...3.0
r.containsAboveLowerBound(value: 2.0) // false
r.containsAboveLowerBound(value: 2.01) // true
If your actual purpose is to use ranges for filtering, how about making them as closures?
let underTen = {0.0 <= $0 && $0 < 10.0}
let upToTwo = {0.0 <= $0 && $0 <= 2.0}
let twoPlus = {2.0 < $0 && $0 <= 4.0}
You can use such filtering closures like this:
class Client: CustomStringConvertible {
var experience: Double
init(experience: Double) {self.experience = experience}
var description: String {return "Client(\(experience))"}
}
let clients = [Client(experience: 1.0),Client(experience: 2.0),Client(experience: 3.0)]
let filteredUnderTen = clients.filter {underTen($0.experience)}
print(filteredUnderTen) //->[Client(1.0), Client(2.0), Client(3.0)]
let filteredUpToTwo = clients.filter {upToTwo($0.experience)}
print(filteredUpToTwo) //->[Client(1.0), Client(2.0)]
let filteredTwoPlus = clients.filter {twoPlus($0.experience)}
print(filteredTwoPlus) //->[Client(3.0)]
I think this does it,
extension ClosedRange where Bound == Int {
func containsExclusiveOfBounds(_ bound: Bound) -> Bool {
return !(bound == lowerBound || bound == upperBound)
}
}

How do I convert a bitmask Int into a set of Ints?

I want a function that takes in a bitmask Int, and returns its masked values as a set of Ints. Something like this:
func split(bitmask: Int) -> Set<Int> {
// Do magic
}
such that
split(bitmask: 0b01001110) == [0b1000000, 0b1000, 0b100, 0b10]
One solution is to check each bit and add the corresponding mask if the bit is set.
func split(bitmask: Int) -> Set<Int> {
var results = Set<Int>()
// Change 31 to 63 or some other appropriate number based on how big your numbers can be
for shift in 0...31 {
let mask = 1 << shift
if bitmask & mask != 0 {
results.insert(mask)
}
}
return results
}
print(split(bitmask: 0b01001110))
For the binary number 0b01001110 the results will be:
[64, 2, 4, 8]
which are the decimal equivalent of the results in your question.
For the hex number 0x01001110 (which is 1000100010000 in binary) the results will be:
[16, 256, 4096, 16777216]
Here's another solution that doesn't need to know the size of the value and it's slightly more efficient for smaller numbers:
func split(bitmask: Int) -> Set<Int> {
var results = Set<Int>()
var value = bitmask
var mask = 1
while value > 0 {
if value % 2 == 1 {
results.insert(mask)
}
value /= 2
mask = mask &* 2
}
return results
}
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]
Here's my "1 line" version:
let values = Set(Array(String(0x01001110, radix: 2).characters).reversed().enumerated().map { (offset, element) -> Int in
Int(String(element))! << offset
}.filter { $0 != 0 })
Not super efficient, but fun!
Edit: wrapped in split function...
func split(bitmask: Int) -> Set<Int> {
return Set(Array(String(bitmask, radix: 2).characters).reversed().enumerated().map { (offset, element) -> Int in
Int(String(element))! << offset
}.filter { $0 != 0 })
}
Edit: a bit shorter
let values = Set(String(0x01001110, radix: 2).utf8.reversed().enumerated().map { (offset, element) -> Int in
Int(element-48) << offset
}.filter { $0 != 0 })

How to change the value of a child from a Mirror introspection

I'm doing a bunch of BLE in iOS, which means lots of tight packed C structures being encoded/decoded as byte packets. The following playground snippets illustrate what I'm trying to do generically.
import Foundation
// THE PROBLEM
struct Thing {
var a:UInt8 = 0
var b:UInt32 = 0
var c:UInt8 = 0
}
sizeof(Thing) // --> 9 :(
var thing = Thing(a: 0x42, b: 0xDEADBEAF, c: 0x13)
var data = NSData(bytes: &thing, length: sizeof(Thing)) // --> <42000000 afbeadde 13> :(
So given a series of fields of varying size, we don't get the "tightest" packing of bytes. Pretty well known and accepted. Given my simple structs, I'd like to be able to arbitrarily encode the fields back to back with no padding or alignment stuff. Relatively easy actually:
// ARBITRARY PACKING
var mirror = Mirror(reflecting: thing)
var output:[UInt8] = []
mirror.children.forEach { (label, child) in
switch child {
case let value as UInt32:
(0...3).forEach { output.append(UInt8((value >> ($0 * 8)) & 0xFF)) }
case let value as UInt8:
output.append(value)
default:
print("Don't know how to serialize \(child.dynamicType) (field \(label))")
}
}
output.count // --> 6 :)
data = NSData(bytes: &output, length: output.count) // --> <42afbead de13> :)
Huzzah! Works as expected. Could probably add a Class around it, or maybe a Protocol extension and have a nice utility. The problem I'm up against is the reverse process:
// ARBITRARY DEPACKING
var input = output.generate()
var thing2 = Thing()
"\(thing2.a), \(thing2.b), \(thing2.c)" // --> "0, 0, 0"
mirror = Mirror(reflecting:thing2)
mirror.children.forEach { (label, child) in
switch child {
case let oldValue as UInt8:
let newValue = input.next()!
print("new value for \(label!) would be \(newValue)")
// *(&child) = newValue // HOW TO DO THIS IN SWIFT??
case let oldValue as UInt32: // do little endian
var newValue:UInt32 = 0
(0...3).forEach {
newValue |= UInt32(input.next()!) << UInt32($0 * 8)
}
print("new value for \(label!) would be \(newValue)")
// *(&child) = newValue // HOW TO DO THIS IN SWIFT??
default:
print("skipping field \(label) of type \(child.dynamicType)")
}
}
Given an unpopulated struct value, I can decode the byte stream appropriately, figure out what the new value would be for each field. What I don't know how to do is to actually update the target struct with the new value. In my example above, I show how I might do it with C, get the pointer to the original child, and then update its value with the new value. I could do it easily in Python/Smalltalk/Ruby. But I don't know how one can do that in Swift.
UPDATE
As suggested in comments, I could do something like the following:
// SPECIFIC DEPACKING
extension GeneratorType where Element == UInt8 {
mutating func _UInt8() -> UInt8 {
return self.next()!
}
mutating func _UInt32() -> UInt32 {
var result:UInt32 = 0
(0...3).forEach {
result |= UInt32(self.next()!) << UInt32($0 * 8)
}
return result
}
}
extension Thing {
init(inout input:IndexingGenerator<[UInt8]>) {
self.init(a: input._UInt8(), b: input._UInt32(), c: input._UInt8())
}
}
input = output.generate()
let thing3 = Thing(input: &input)
"\(thing3.a), \(thing3.b), \(thing3.c)" // --> "66, 3735928495, 19"
Basically, I move the various stream decoding methods to byte stream (i.e. GeneratorType where Element == UInt8), and then I just have to write an initializer that strings those off in the same order and type the struct is defined as. I guess that part, which is essentially "copying" the structure definition itself (and therefore error prone), is what I had hoped to use some sort of introspection to handle. Mirrors are the only real Swift introspection I'm aware of, and it seems pretty limited.
As discussed in the comments, I suspect this is over-clever. Swift includes a lot of types not friendly to this approach. I would focus instead on how to make the boilerplate as easy as possible, without worrying about eliminating it. For example, this is very sloppy, but is in the direction I would probably go:
Start with some helper packer/unpacker functions:
func pack(values: Any...) -> [UInt8]{
var output:[UInt8] = []
for value in values {
switch value {
case let i as UInt32:
(0...3).forEach { output.append(UInt8((i >> ($0 * 8)) & 0xFF)) }
case let i as UInt8:
output.append(i)
default:
assertionFailure("Don't know how to serialize \(value.dynamicType)")
}
}
return output
}
func unpack<T>(bytes: AnyGenerator<UInt8>, inout target: T) throws {
switch target {
case is UInt32:
var newValue: UInt32 = 0
(0...3).forEach {
newValue |= UInt32(bytes.next()!) << UInt32($0 * 8)
}
target = newValue as! T
case is UInt8:
target = bytes.next()! as! T
default:
// Should throw an error here probably
assertionFailure("Don't know how to deserialize \(target.dynamicType)")
}
}
Then just call them:
struct Thing {
var a:UInt8 = 0
var b:UInt32 = 0
var c:UInt8 = 0
func encode() -> [UInt8] {
return pack(a, b, c)
}
static func decode(bytes: [UInt8]) throws -> Thing {
var thing = Thing()
let g = anyGenerator(bytes.generate())
try unpack(g, target: &thing.a)
try unpack(g, target: &thing.b)
try unpack(g, target: &thing.c)
return thing
}
}
A little more thought might be able to make the decode method a little less repetitive, but this is still probably the way I would go, explicitly listing the fields you want to encode rather than trying to introspect them. As you note, Swift introspection is very limited, and it may be that way for a long time. It's mostly used for debugging and logging, not logic.
I have tagged Rob's answer is the official answer. But I'd thought I'd share what I ended up doing as well, inspired by the comments and answers.
First, I fleshed out my "Problem" a little to include a nested structure:
struct Inner {
var ai:UInt16 = 0
var bi:UInt8 = 0
}
struct Thing {
var a:UInt8 = 0
var b:UInt32 = 0
var inner = Inner()
var c:UInt8 = 0
}
sizeof(Thing) // --> 12 :(
var thing = Thing(a: 0x42, b: 0xDEADBEAF, inner: Inner(ai: 0x1122, bi: 0xDD), c: 0x13)
var data = NSData(bytes: &thing, length: sizeof(Thing)) // --> <42000000 afbeadde 2211dd13> :(
For Arbitrary Packing, I stuck with the same generic approach:
protocol Packable {
func packed() -> [UInt8]
}
extension UInt8:Packable {
func packed() -> [UInt8] {
return [self]
}
}
extension UInt16:Packable {
func packed() -> [UInt8] {
return [(UInt8((self >> 0) & 0xFF)), (UInt8((self >> 8) & 0xFF))]
}
}
extension UInt32:Packable {
func packed() -> [UInt8] {
return [(UInt8((self >> 0) & 0xFF)), (UInt8((self >> 8) & 0xFF)), (UInt8((self >> 16) & 0xFF)), (UInt8((self >> 24) & 0xFF))]
}
}
extension Packable {
func packed() -> [UInt8] {
let mirror = Mirror(reflecting:self)
var bytes:[UInt8] = []
mirror.children.forEach { (label, child) in
switch child {
case let value as Packable:
bytes += value.packed()
default:
print("Don't know how to serialize \(child.dynamicType) (field \(label))")
}
}
return bytes
}
}
Being able to "pack" things is as easy adding them to the Packable protocol and telling them to pack themselves. For my cases above, I only need 3 different types of signed integers, but one could add lots more. For example, in my own code, I have some Enums derived from UInt8 which I added the packed method to.
extension Thing:Packable { }
extension Inner:Packable { }
var output = thing.packed()
output.count // --> 9 :)
data = NSData(bytes: &output, length: output.count) // --> <42afbead de2211dd 13> :)
To be able to unpack stuff, I came up with a little bit of support:
protocol UnpackablePrimitive {
static func unpack(inout input:IndexingGenerator<[UInt8]>) -> Self
}
extension UInt8:UnpackablePrimitive {
static func unpack(inout input:IndexingGenerator<[UInt8]>) -> UInt8 {
return input.next()!
}
}
extension UInt16:UnpackablePrimitive {
static func unpack(inout input:IndexingGenerator<[UInt8]>) -> UInt16 {
return UInt16(input.next()!) | (UInt16(input.next()!) << 8)
}
}
extension UInt32:UnpackablePrimitive {
static func unpack(inout input:IndexingGenerator<[UInt8]>) -> UInt32 {
return UInt32(input.next()!) | (UInt32(input.next()!) << 8) | (UInt32(input.next()!) << 16) | (UInt32(input.next()!) << 24)
}
}
With this, I can then add initializers to my high level structures, e.g.
extension Inner:Unpackable {
init(inout packed bytes:IndexingGenerator<[UInt8]>) {
self.init(ai: UInt16.unpack(&bytes), bi: UInt8.unpack(&bytes))
}
}
extension Thing:Unpackable {
init(inout packed bytes:IndexingGenerator<[UInt8]>) {
self.init(a: UInt8.unpack(&bytes), b: UInt32.unpack(&bytes), inner: Inner(packed:&bytes), c: UInt8.unpack(&bytes))
}
}
What I liked about this is that these initializers call the default initializer in the same order and types as the structure is defined. So if the structure changes in type or order, I have to revisit the (packed:) initializer. The kids a bit long, but not too.
What I didn't like about this, was having to pass the inout everywhere. I'm honestly not sure what the value is of value based generators, since passing them around you almost always want to share state. Kind of the whole point of reifying an object that captures the position of a stream of data, is to be able to share it. I also don't like having to specify IndexingGenerator directly, but I imagine there's some fu magic that would make that less specific and still work, but I'm not there yet.
I did play with something more pythonic, where I return a tuple of the type and the remainder of a passed array (rather than a stream/generator), but that wasn't nearly as easy to use at the top level init level.
I also tried putting the static methods as extensions on byte based generators, but you have to use a function (would rather have used a computed var with side effects) there whose name doesn't match a type, so you end up with something like
self.init(a: bytes._UInt8(), b: bytes._UInt32(), inner: Inner(packed:&bytes), c: bytes._UInt8())
This is shorter, but doesn't put the type like functions next to the argument names. And would require all kinds of application specific method names to be added as well as one extended the set of UnpackablePrimitives.

How to generate a random variable from an enum that has cases with arguments in Swift?

Given the following enum:
enum GameLevel {
case Level(Int)
case TutorialLevel, BossLevel
}
How to generate a random variable of type GameLevel in Swift?
I updated your enum as per Apple standards (Capital letter to start a Type, and no abbreviations.
enum GameLevel {
case Level(Int)
case TutorialLevel, BossLevel
}
First, how to create a constant or variable with a value for level.
let level = GameLevel.Level(1)
Next, for a random value to level use arc4random_uniform:
let maxGameLevel: UInt32 = 10
let randomGameLevel: Int = Int(arc4random_uniform(maxGameLevel))
let level = GameLevel.Level(randomGameLevel)
Of course, this can be put into a function:
func RandomGameLevel() -> GameLevel {
let maxGameLevel: UInt32 = 10
return .Level(Int(arc4random_uniform(maxGameLevel)))
}
let level = RandomGameLevel()
Finally, here is how you would use it in a case statement:
switch level {
case .Level(let levelValue):
println("Level \(levelValue)")
case .TutorialLevel:
println("Tutorial Level")
case .BossLevel:
println("Boss Level")
}
Update
OK, it's not too hard to include the other values. I'll also put all of this into GameLevel to package it up better.
enum GameLevel {
case Level(Int)
case TutorialLevel, BossLevel
static func Random() -> GameLevel {
let maxGameLevel: UInt32 = 10 /* levels will be 0 through 9 */
let otherGameLevels: UInt32 = 2 /* TutorialLevel and BossLevel */
let levelValue = Int(arc4random_uniform(maxGameLevel + otherGameLevels))
switch levelValue {
case 10: return .TutorialLevel
case 11: return .BossLevel
default: return .Level(levelValue)
}
}
}
Then
let level = GameLevel.Random()
Not the cleanest, but it's a start.
enum GameLevel: CaseIterable {
case Level(Int)
case TutorialLevel, BossLevel
}
let level:GameLevel = GameLevel.allCases.randomElement()!
Why would you need it that way? :(
Assign numbers to your start and final levels and implement a function, which will return random in that range as Lvl(int)