swift 3:如何扩展[UInt8]以添加getUInt32BE函数

时间:2016-04-26 08:06:08

标签: swift bit-manipulation swift3

我正在尝试使用[UInt8]函数扩展getUInt32BE(),如下面的代码。

这是我收到的错误:

  

“无法使用索引类型下标”Self“类型的值   '范围'“

有人可以帮我纠正这个错误吗?

extension Collection where Iterator.Element == UInt8 {
    public func getUInt32BE(at: Int = 0) -> UInt32 {
        return self[at..<at+4].reduce(0) {
          $0 << 8 + UInt32($1)
        }
    }
}

提前致谢:)

1 个答案:

答案 0 :(得分:1)

备选方案#1:使用.pointee

UnsafePointer属性

如以下Q&amp; A

所述

你可以在Swift 2.2中使用,例如

UnsafePointer<UInt16>(bytes).memory

将2字节UInt8数组转换为UInt16值(主机字节顺序)。

现在,在Swift 3.0中,dev .memory已被.pointee取代。应用上述以及此更改,我们同样可以使用UnsafePointer<UInt32>(bytes).pointee来访问4字节UInt32数组的UInt8表示,但请注意表示使用主机字节顺序进行转换。为了可能(如果需要)将其转换为整数的大端表示,我们可以使用每个整数类型可用的.bigEndian属性,如以下Q&amp; A中所述:

这在Swift 3.0-dev中仍然有效,因此,我们可以按如下方式构建您的getUInt32BE(...)方法

extension Collection where Iterator.Element == UInt8 {
    public func getUInt32BE(at: Index.Distance) -> UInt32? {
        let from = startIndex.advanced(by: at, limit: endIndex)
        let to = from.advanced(by: 4, limit: endIndex)

        guard case let bytes = Array(self[from..<to]) 
            where bytes.count == 4 else { return nil }

        return UnsafePointer<UInt32>(bytes).pointee.bigEndian
    }
}

备选方案#2:使用位移

或者,使用上述的修改版本进行位移(类似于您的问题的尝试)而不是使用UnsafePointer<..>

extension Collection where Iterator.Element == UInt8 {

    public func getUInt32BE(at: Index.Distance) -> UInt32? {
        let from = startIndex.advanced(by: at, limit: endIndex)
        let to = from.advanced(by: 4, limit: endIndex)

        guard case let bytesSlice = self[from..<to] 
            where from.distance(to: to) == 4 else { return nil }

        return bytesSlice.reduce(0) { (tot, val) -> UInt32 in
            tot << 8 + UInt32(val as! UInt8)
        }
    }
}

请注意,我们需要通过强制valIterator.Element强制转换为UInt8来帮助编译器(由于扩展名的where子句,保证会成功; Iterator.Element == UInt8)。

使用示例

上述两种替代方案中的任何一种的示例用法:

/* example usage */
let bytes: [UInt8] = [
    0,   // 0b 0000 0000
    255, // 0b 1111 1111 
    0,   // 0b 0000 0000
    104, // 0b 0110 1000
    76]  // 0b 0100 1100

/*  byteArr[1..<5], big-endian:
        0b 1111 1111 0000 0000 0110 1000 0100 1100 */
let u32verify = 0b11111111000000000110100001001100 
print(u32verify) // 4278216780

if let u32val = bytes.getUInt32BE(1) {
    print(u32val) // 4278216780, OK
}

有关Collection协议的详细信息(上述错误的原因不是使用此协议的关联类型Index.Distance来构建子阵列),请参阅例如。