swiftgrand-central-dispatchlibdispatch

Type mismatch in Swift GCD overlay


I'd like to use a dispatch IO channel to read some data from a file descriptor. After creating the channel, the next step is to call read, whose declaration is as follows:

func read(offset: off_t, 
   length: Int, 
    queue: DispatchQueue, 
ioHandler: @escaping (Bool, DispatchData?, Int32) -> Void)

The documentation for the length parameter says:

The number of bytes to read from the channel. Specify SIZE_MAX to continue reading data until an EOF is reached.

Seems easy enough. In my case, I'd like do just that — read until EOF. So I'll pass SIZE_MAX:

// `queue` and `handler` defined elsewhere
channel.read(offset: 0, length: SIZE_MAX, queue: queue, ioHandler: handler)

The astute reader has guessed that the compiler doesn't like this:

Cannot convert value of type 'UInt' to expected argument type 'Int'

SIZE_MAX is of type UInt, but length is of type Int. The compiler offers to fix it:

channel.read(offset: 0, length: Int(SIZE_MAX), queue: queue, ioHandler: handler)

But of course, at runtime, this doesn't work out so well:

fatal error: Not enough bits to represent a signed value

Naturally if SIZE_MAX is the largest value representable by UInt, then Int cannot represent it. After some quick searching, I found this exact issue on the Swift bug tracker. Since it doesn't seem to yet be addressed — and I'm unsure of my ability to address it myself with a pull request — how can I work around this issue? Or am I missing a way to do what I want?


The Swift Stdlib rationales document covers the explicit decision to import size_t as Int rather than UInt. It boils down to "fewer type conversions, and who needs to specify numbers above 2^63 anyways (sorry, 32-bit platforms)." Fair enough, but that doesn't cover issues like mine, where the use of SIZE_MAX is part of an API.


Solution

  • Just use Int.max If you are on a 64 bit platform, you still almost guaranteed to reach end of file before you read Int.max bytes. On a 32 bit platform, if your file is very large, you might have to issue more than one read.

    Then you should report the issue to Apple. I'm not sure if the Dispatch IO library belongs to Apple or to the Swift Open Source project, or if it is simply a documentation error.

    Update

    The source code is open source, and the read operation is just a simple wrapper to a C function that takes a size_t for length.

    https://github.com/apple/swift-corelibs-libdispatch/blob/master/src/swift/IO.swift

    I haven't tried it, but you can almost certainly use the bit pattern or probably even -1. I think I would still go with Int.max though.