How to capture invalid c enum in swift - c

I use C to cast my over-the-wire payloads, so int to enum conversion in c:
typedef CF_ENUM( uint8_t, MyEnum_t ) {
a = 0
b = 1
}
...
value = ( MyEnum_t * ) rawValue;
Because the value is over-the-wire, the actual raw value can be any uint8_t. It is possible that the rawValue is actually not 0 or 1.
Below I have a swift code sample showing setting an enum to an impossible/invalid value and a switch statement handling that value.
import Foundation
enum MyEnum: UInt8 {
case a = 0
case b = 1
}
let raw: UInt8 = 0b100 // 4
let value: MyEnum = unsafeBitCast(raw, to: MyEnum.self)
switch value {
case .a:
print("a:", value) // * not reached *
case .b:
print("b:", value) // "b: \n"
print("value == .a => \(value == .a)") // "x == .a => true\n"
print("value == .b => \(value == .b)") // "x == .b => false\n"
}
print(value.rawValue) // "0"
With a C raw value of 4, swift sample shows crazy results:
4 force casted prints nothing for its value (most likely char 4 or 0, both unprintable)
4 switches as MyEnum.b (1)
4 compares equally to MyEnum.a (0)
4 as MyEnum raw value prints 0 (MyEnum.a)
Is there any way to capture this error in swift? I prefer to not use these odd results to detect the anomaly.

Related

Swift vs C Bridge Enumeration Internals and Metadata

Question:
Why aren't the outputs the same for what are basically the same enumeration? Does Swift strip metadata from C (I assume it is stripped by the Compiler, then provided again by the Swift Interface File as though it is a normal Swift enum)?
Details:
I have two similar enumerations, one defined in C and one defined in Swift.
However I'm getting different print results, specifically the Swift Enum is able to print the key as a representational String (in this case first), but the C Enum is only able to print the Enumeration name (in this case CNames).
This is the output of the program,
Enumerations in Swift and C
Running on Swift 5
CNames is 1
first is 1
Program ended with exit code: 0
I assume the Swift main file is using the Swift Generated Interface.
This is what my project looks like:
Here's my native Swift enum, `SNames:
//
// SNames.swift
// TestAppMain
//
import Foundation
public enum SNames : UInt {
case unknown = 0
case first = 1
case middle = 2
case last = 3
}
Here is my C enum, CNames:
#ifndef Names_h
#define Names_h
// NSUInteger type definition
#import <Foundation/NSObjCRuntime.h>
typedef NS_ENUM(NSUInteger, CNames) {
NameUnknown = 0,
NameFirst = 1,
NameMiddle = 2,
NameLast = 3,
};
#endif /* Names_h */
Here's the generated Swift 5 Interface:
// NSUInteger type definition
import Foundation.NSObjCRuntime
public enum CNames : UInt {
case unknown = 0
case first = 1
case middle = 2
case last = 3
}
Here is my bridging header:
#ifndef TestAppMain_Bridging_Header_h
#define TestAppMain_Bridging_Header_h
#import "CNames.h"
#endif /* TestAppMain_Bridging_Header_h */
Finally my main.swift is:
print("Enumerations in Swift and C")
#if swift(>=5)
print("Running on Swift 5")
#endif
let cFirst = CNames.first
let sFirst = SNames.first
// Swift Enum is able to output the specific
// case (i.e first) whereas the C Enum
// doesn't seem to have that info
// CNames is 1
print("\(cFirst) is \(cFirst.rawValue)")
// first is 1
print("\(sFirst) is \(sFirst.rawValue)")
This is just a limitation of C. C enums are nothing more than a visually grouped set of integer constants.
You'll notice that you get the same limitations when applying #objc to Swift enums:
enum NativeEnum: Int32 { case i = 123 }
print(NativeEnum.i) // => "i"
#objc enum ExportedEnum: Int32 { case i = 123 }
print(ExportedEnum.i) // => "ExportedEnum"

Kotlin foreach object

I have a object class,
object Examples {
const val E = 1
const val X = 2
const val A = 3
const val M = 4
const val P = 5
const val L = 6
}
if I use
Examples.E
it will reference the relevant const val, but how can I iterate over each val without calling them individually?
It looks like you want an enum:
enum class Example(val intVal: Int) {
E(1), X(2), A(3), M(4), P(5), L(6);
}
You can access specific values like Example.E.intVal. To get a collection of all the values, you can do Example.values() or enumValues<Example>()
Well, best way to do this is use Enum class.
But you also could implement Iterable interface on yours object. And after this you will be able to use forEach/map/fold/filter and other useful Iterable extension functions
object Examples : Iterable<Int> {
const val E = 1
const val X = 2
const val A = 3
const val M = 4
const val P = 5
const val L = 6
override fun iterator(): Iterator<Int> {
return object : Iterator<Int> {
private var i = 1
override fun hasNext(): Boolean = i <= 6
override fun next(): Int {
return when (i++) {
1 -> E
2 -> X
3 -> A
4 -> M
5 -> P
6 -> L
else -> error("No such element exeption")
}
}
}
}
}
Examples.forEach {
println(it)
}
With Kotlin, you could use reflection to get all values inside an object.
However, did you consider using an Enum class? This seems like it would fit your use case better:
enum class Letter {
A,
B,
C
}
You can get all possible values for Letter using
Letter.values()
Or if what you want is to be able to reference repeated elements in order, you could use an array, or just a string, if the elements are always letters.
Also keep in mind you have two values called "E" defined, which wouldn't work.

Casting between Swift array types without copy or allocations

I want to access an existing array of UInt64 as if it is an array of Int8. Key requirement is efficiency - I don't want to copy or reallocate the data, just have direct access. I don't want side effects (for example I want to be able to continue to use the uint64Array after this block of code has executed, was reading about rebinding having undefined side effects.)
I tried doing this with Swift 4.2:
var uint64Array = [UInt64](repeating: 0, count: 100)
uint64Array.withUnsafeMutableBufferPointer() {
uint64Pointer in
uint64Pointer.withMemoryRebound(to: Int8.self) { // <- Error occurs here.
int8Pointer in
int8Pointer[0] = 1
int8Pointer[1] = 2
int8Pointer[2] = 3
int8Pointer[3] = 4
}
}
However I get a Fatal Error at runtime on the following line:
uint64Pointer.withMemoryRebound(to: Int8.self) {
Is this the right approach? If so, why am I getting the Fatal Error?
I think the problem is that you can't bind to a different type directly as per this note in the docs:
Only use this method to rebind the buffer’s memory to a type with the same size and stride as the currently bound Element type. To bind a region of memory to a type that is a different size, convert the buffer to a raw buffer and use the bindMemory(to:) method.
If bytes is what you're after then the quickest route is:
var uint64Array = [UInt64](repeating: 0, count: 100)
uint64Array.withUnsafeMutableBytes { x in
x[0] = 1
x[1] = 2
x[3] = 3
x[4] = 4
}
If you have another type you'd like to use you can do it like this:
var uint64Array = [UInt64](repeating: 0, count: 100)
uint64Array.withUnsafeMutableBufferPointer() {
uint64Pointer in
let x = UnsafeMutableRawBufferPointer(uint64Pointer).bindMemory(to: Int32.self)
x[0] = 1
x[1] = 2
x[3] = 3
x[4] = 4
}
Thanks to #brindy for solving this one. Here is an extension implementation that is as clean as I could get it.
The extension:
extension Array {
mutating func bindMutableMemoryTo<T,R>(_ type: T.Type, _ closure: (UnsafeMutableBufferPointer<T>) throws -> R) rethrows -> R {
return try self.withUnsafeMutableBytes() {
return try closure($0.bindMemory(to: type))
}
}
}
Usage:
var uint64Array = [UInt64](repeating: 0, count: 100)
uint64Array.bindMutableMemoryTo(Int8.self) {
int8Pointer in
int8Pointer[0] = 1 // LSB of uint64Array[0]
int8Pointer[1] = 2
int8Pointer[2] = 3
int8Pointer[3] = 4 // MSB of uint64Array[0]
}

Dereference UnsafeMutablePointer<UnsafeMutableRawPointer>

I have a block that is passing data in that I'd like to convert to an array of array of floats -- e.g. [[0.1,0.2,0.3, 1.0], [0.3, 0.4, 0.5, 1.0], [0.5, 0.6, 0.7, 1.0]]. This data is passed to me in the form of data:UnsafeMutablePointer<UnsafeMutableRawPointer> (The inner arrays are RGBA values)
fwiw -- the block parameters are from SCNParticleEventBlock
How can I dereference data into a [[Float]]? Once I have the array containing the inner arrays, I can reference the inner array (colorArray) data with:
let rgba: UnsafeMutablePointer<Float> = UnsafeMutablePointer(mutating: colorArray)
let count = 4
for i in 0..<count {
print((rgba+i).pointee)
}
fwiw -- this is Apple's example Objective-C code for referencing the data (from SCNParticleSystem handle(_:forProperties:handler:) )
[system handleEvent:SCNParticleEventBirth
forProperties:#[SCNParticlePropertyColor]
withBlock:^(void **data, size_t *dataStride, uint32_t *indices , NSInteger count) {
for (NSInteger i = 0; i < count; ++i) {
float *color = (float *)((char *)data[0] + dataStride[0] * i);
if (rand() & 0x1) { // Switch the green and red color components.
color[0] = color[1];
color[1] = 0;
}
}
}];
You can actually subscript the typed UnsafeMutablePointer without having to create an UnsafeMutableBufferPointer, as in:
let colorsPointer:UnsafeMutableRawPointer = data[0] + dataStride[0] * i
let rgbaBuffer = colorsPointer.bindMemory(to: Float.self, capacity: dataStride[0])
if(arc4random_uniform(2) == 1) {
rgbaBuffer[0] = rgbaBuffer[1]
rgbaBuffer[1] = 0
}
Were you ever able to get your solution to work? It appears only a handful of SCNParticleProperties can be used within an SCNParticleEventBlock block.
Based on this answer, I've written the particle system handler function in swift as:
ps.handle(SCNParticleEvent.birth, forProperties [SCNParticleSystem.ParticleProperty.color]) {
(data:UnsafeMutablePointer<UnsafeMutableRawPointer>, dataStride:UnsafeMutablePointer<Int>, indicies:UnsafeMutablePointer<UInt32>?, count:Int) in
for i in 0..<count {
// get an UnsafeMutableRawPointer to the i-th rgba element in the data
let colorsPointer:UnsafeMutableRawPointer = data[0] + dataStride[0] * i
// convert the UnsafeMutableRawPointer to a typed pointer by binding it to a type:
let floatPtr = colorsPointer.bindMemory(to: Float.self, capacity: dataStride[0])
// convert that to a an UnsafeMutableBufferPointer
var rgbaBuffer = UnsafeMutableBufferPointer(start: floatPtr, count: dataStride[0])
// At this point, I could convert the buffer to an Array, but doing so copies the data into the array and any changes made in the array are not reflected in the original data. UnsafeMutableBufferPointer are subscriptable, nice.
//var rgbaArray = Array(rgbaBuffer)
// about half the time, mess with the red and green components
if(arc4random_uniform(2) == 1) {
rgbaBuffer[0] = rgbaBuffer[1]
rgbaBuffer[1] = 0
}
}
}
I'm really not certain if this is the most direct way to go about this and seems rather cumbersome compared to the objective-C code (see above question). I'm certainly open to other solutions and/or comments on this solution.

How should i get the last index of an object that passes a test as an Int?

I'm trying to get the index of the last object in my array of messages which passes a test.
Here's what I'm doing so far. I have an array of message Dictionary objects: [[String:Any]]
I'm getting the index of the last object:
let lastStatusUpdateIndex = messages.reversed().index { message in
guard let type = message["type"] as? String else { return false }
guard type == "Status Change" else { return false }
return true
}
To work get the integer of the index, I was expecting to do either
let position = messages.startIndex.distance(to: lastStatusUpdateIndex)
or
let position = messages.distance(from: messages.startIndex, to: lastStatusUpdateIndex)
But these methods take an Int or two Ints (respectively)
What's the correct way to do this? (I understand that the index would be coming from the end of the array rather than the start, but I can deal with that)
I'm using Swift 3.1.
Many thanks.
An index can only be used with the collection that it belongs to.
In your case, lastStatusUpdateIndex (which is a ReversedRandomAccessIndex) can only be used with the collection
returned by messages.reversed() (which is a ReversedRandomAccessCollection).
So you can compute the distance of lastStatusUpdateIndex to
the startIndex of the reverse collection, and that is an Int:
let messages = Array(0...10) // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let revMessages = messages.reversed() // a ReversedRandomAccessCollection
if let lastStatusUpdateIndex = (revMessages.index { message in
message % 3 == 0 } ) {
let pos = revMessages.distance(from: revMessages.startIndex,
to: lastStatusUpdateIndex)
print(pos) // 1
}
Alternatively, use that ReversedRandomAccessIndex
has a base property which is the position after
its corresponding position in the underlying collection. (This was wrongly
documented in Swift 3, but has been fixed for Swift 4,
compare SR-3650 - Transforming an index from a reversed() array is off by one.)
In your case the underlying collection is an Array, so that
lastStatusUpdateIndex.base is an Int:
if let lastStatusUpdateIndex = (messages.reversed().index { message in
message % 3 == 0 } ) {
let pos = lastStatusUpdateIndex.base - 1
print(pos) // 9
}

Resources