swift 3:如何扩展[UInt8]以添加getUInt32BE函数
swift 3: How to extend [UInt8] to add getUInt32BE function
我正在尝试使用 getUInt32BE()
函数扩展 [UInt8]
,如下面的代码。
这是我收到的错误:
"Cannot subscript a value of type 'Self' with an index of type
'Range'"
谁能帮我改正这个错误?
extension Collection where Iterator.Element == UInt8 {
public func getUInt32BE(at: Int = 0) -> UInt32 {
return self[at..<at+4].reduce(0) {
[=11=] << 8 + UInt32()
}
}
}
提前致谢:)
备选方案 #1:使用 .pointee
属性 或 UnsafePointer
如以下问答所述
- Convert a two byte UInt8 array to a UInt16 in Swift
您可以在 Swift 2.2 中使用,例如
UnsafePointer<UInt16>(bytes).memory
将 2 字节 UInt8
数组转换为 UInt16
值(主机字节顺序)。
现在,在 Swift 3.0-dev 中 .memory
已被 .pointee
取代。应用上述以及此更改,我们同样可以使用 UnsafePointer<UInt32>(bytes).pointee
访问 4 字节 UInt8
数组的 UInt32
表示,但请注意该表示使用主机字节顺序进行转换.为了可能(如有必要)将其转换为整数的大端表示,我们可以使用每个整数类型可用的 .bigEndian
属性,如以下问答所述:
- How do I set integer endianness using htonl in Swift?
这在 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)
}
}
}
请注意,我们需要通过强制将 val
从 Iterator.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
来构造您的子数组),请参见
我正在尝试使用 getUInt32BE()
函数扩展 [UInt8]
,如下面的代码。
这是我收到的错误:
"Cannot subscript a value of type 'Self' with an index of type 'Range'"
谁能帮我改正这个错误?
extension Collection where Iterator.Element == UInt8 {
public func getUInt32BE(at: Int = 0) -> UInt32 {
return self[at..<at+4].reduce(0) {
[=11=] << 8 + UInt32()
}
}
}
提前致谢:)
备选方案 #1:使用 .pointee
属性 或 UnsafePointer
如以下问答所述
- Convert a two byte UInt8 array to a UInt16 in Swift
您可以在 Swift 2.2 中使用,例如
UnsafePointer<UInt16>(bytes).memory
将 2 字节 UInt8
数组转换为 UInt16
值(主机字节顺序)。
现在,在 Swift 3.0-dev 中 .memory
已被 .pointee
取代。应用上述以及此更改,我们同样可以使用 UnsafePointer<UInt32>(bytes).pointee
访问 4 字节 UInt8
数组的 UInt32
表示,但请注意该表示使用主机字节顺序进行转换.为了可能(如有必要)将其转换为整数的大端表示,我们可以使用每个整数类型可用的 .bigEndian
属性,如以下问答所述:
- How do I set integer endianness using htonl in Swift?
这在 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)
}
}
}
请注意,我们需要通过强制将 val
从 Iterator.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
来构造您的子数组),请参见