Indexing flexible arrays in imported C-structs

Yes, flexible array members work extremely badly in Swift. If you want them to work you need to hold them extremely carefully. You can do it, but I'd encourage you to wrap the type in a class that holds a pointer whose size you manage yourself, and then use that as your abstraction type in Swift. In your case, rather than using Packet directly, you can use:

struct SwiftPacket {
    private var storage: Storage

    init(count: UInt) {
        self.storage = Storage(count: count)
    }
}

extension SwiftPacket {
    fileprivate final class Storage {
        private let ptr: UnsafeMutablePointer<Packet>

        init(count: UInt) {
            self.ptr = Packet_allocate(count)
        }

        init(copying original: Storage) {
            self.ptr = Packet_allocate(original.size)
            for i in 0..<Int(original.size) {
                self[i] = original[i]
            }
        }

        var size: CUnsignedLong {
            ptr.pointee.size
        }

        subscript(index: Int) -> UInt8 {
            get {
                (UnsafeRawPointer(ptr) + MemoryLayout<Packet>.size + index).load(as: UInt8.self)
            }
            set {
                (UnsafeMutableRawPointer(ptr) + MemoryLayout<Packet>.size + index).storeBytes(of: newValue, as: UInt8.self)
            }
        }

        deinit {
            Packet_free(ptr)
        }
    }
}

extension SwiftPacket: RandomAccessCollection, MutableCollection {
    var count: Int {
        Int(self.storage.size)
    }

    var startIndex: Int { 0 }
    var endIndex: Int { count }

    subscript(index: Int) -> UInt8 {
        get {
            precondition(indices.contains(index))
            return storage[index]
        }
        set {
            precondition(indices.contains(index))
            if !isKnownUniquelyReferenced(&storage) {
                storage = Storage(copying: storage)
            }
            storage[index] = newValue
        }
    }
}

While you'll have to take care with the pointer sizing dance, this gives you a Swift-native CoW equivalent of the original pointer type, with an entirely safe user interface. I think this kind of wrapper is pretty neat!

3 Likes