2

When you open IOKit/usb/USB.h, you see constants like:

#define err_system(x)    (signed((unsigned(x) & 0x3f) << 26))
#define err_sub(x)       ((x & 0xfff) << 14)

#define sys_iokit         err_system(0x38)
#define sub_iokit_usb     err_sub(1)

#define iokit_usb_err(return)   (IOReturn)(sys_iokit|sub_iokit_usb|return)


//Then all errors are defined like:

#define kIOUSBUnknownPipeErr   iokit_usb_err(0x61)   //0xe0004061
kIOUSBTooManyPipesErr          iokit_usb_err(0x60)   //0xe0004060
.
.   

So I decided to try this in swift:

private enum IOUSBError: IOReturn {
    case unknownPipeErr = 0xe0004061
    case tooManyPipesErr = 0xe0004060
    .
    .
}

But it gives the error: "Integer literal '3758112865' overflows when stored into 'IOReturn' (aka 'Int32')"

For every single enum case..

Any idea how to deliberately allow this?

Brandon
  • 22,723
  • 11
  • 93
  • 186
  • The C code is taking an unsigned 32-bit number and casting it to a signed 32-bit number. That works in C. In Swift you get the overflow. You need to perform a few tricks in Swift to stick a 32-bit unsigned value in a 32-bit signed variable (case). – rmaddy Jul 27 '19 at 05:33
  • See https://stackoverflow.com/questions/27127124/int-to-uint-and-vice-versa-bit-casting-in-swift – rmaddy Jul 27 '19 at 05:35
  • @rmaddy; That works for constants but not for enums in Swift :( – Brandon Jul 27 '19 at 05:38
  • I just realized that. – rmaddy Jul 27 '19 at 05:38
  • 1
    `0xe0004061` is `-0x1fffbf9f` and `0xe0004060` is `-0x1fffbfa0` when converted from unsigned to signed. – rmaddy Jul 27 '19 at 05:44
  • 1
    Use `print(String(Int32(bitPattern: 0xe0004060), radix: 16))` to convert. – rmaddy Jul 27 '19 at 05:46

1 Answers1

3

You cannot assign an unsigned integer literal to an enum with a signed integer raw type if the value exceeds the range of the signed type, as far as I know.

Precomputing the equivalent signed value would be one option, as @rmaddy suggested.

Another option is to use a struct with static properties instead:

struct IOUSBErr {

    static let kIOUSBUnknownPipeErr  = IOReturn(bitPattern: 0xe0004061)
    static let kIOUSBTooManyPipesErr = IOReturn(bitPattern: 0xe0004060)
    // ...
}

Or replace the C macros (which are not imported into Swift) by Swift functions:

struct IOUSBErr {

    static func err_system(_ x: UInt32) -> IOReturn {
        return IOReturn(bitPattern: (x & 0x3f) << 26)
    }

    static func err_sub(_ x: UInt32) -> IOReturn {
        return IOReturn(bitPattern: (x & 0xfff) << 14)
    }

    static let sys_iokit = err_system(0x38)
    static let sub_iokit_usb = err_sub(1)

    static func iokit_usb_err(_ ret: UInt32) -> IOReturn {
        return sys_iokit|sub_iokit_usb|IOReturn(bitPattern: ret)
    }

    static let kIOUSBUnknownPipeErr  = iokit_usb_err(0x61)
    static let kIOUSBTooManyPipesErr = iokit_usb_err(0x60)

}

(Static properties are initialized once, on the first access.)

Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • I ended up making the enum `Int64, CaseIterable`, looping all the cases and comparing to Int32 via bit-pattern. This way I didn't change to change all my code. But your solution of using Struct is nice. – Brandon Jul 29 '19 at 18:25