有没有办法在不复制的情况下将 "cast" 数据转换为 [UInt8](一个字节数组),或者是不安全的方法?
Is there a way to "cast" Data to [UInt8] (a byte array) without copying, or is Unsafe the way to go?
我知道我能做到
//Setup
let originalBytes: [UInt8] = [0x0, 0x1, 0x2]
let originalData = Data(originalBytes)
//This
let getByteArFromData: [UInt8] = [UInt8](originalData)
现在,我漂亮确定getByteArFromData
将字节复制到新分配的内存中(我在这里错了吗?)。
随着数据规模的扩大(例如图像、编解码器操作等),这会变得 昂贵。
我有动力问这个问题,因为我想通过类似数组的接口来操作数据,但据我所知,Unsafe API 是可行的方法。所以...
使用不安全 API 是唯一的方法吗?这是推荐的方法吗?
我也玩过一些指针运算(刚刚进入不安全 API,不确定如果我可以使用类似数组的接口是否应该投入时间)。
这是一些 playground 代码(如果您愿意,请评论并回答评论中的问题):
import Foundation
//MARK:- Setup
final class PlaygroundSwingSet
{
//Squish unimportant setup code
private let byteAr: [UInt8]
private lazy var data: Data = {Data(byteAr)}()
init(_ bytes: [UInt8]) {self.byteAr = bytes}
func swing() -> Int
{
//Unsafe API stuff
return data.withUnsafeBytes {(dataBufferPtr) -> (Int) in //No need for [weak self], right?
//I don't like how the pointers are only valid in this closure.
//It adds an indent to my code to which my "code OCD" forces me to abstract out
self.middleOut(dataBufferPtr)
}
}
private func middleOut(_ dataBufferPtr: UnsafeRawBufferPointer) -> Int
{
//Yuck, I have to make sure count isn't 0 AND safely unwrap.
//Why is .baseAddress even optional then?
guard let dataPtr = dataBufferPtr.baseAddress?.assumingMemoryBound(to: UInt8.self), dataBufferPtr.count > 0 else {
print("data is empty")
return 0
}
let middishIndex = dataBufferPtr.count / 2 - 1
//More yuck.
print("truncated middle element's value is \(dataPtr.advanced(by: middishIndex).pointee)")
print("this should yield the same thing: \((dataPtr + middishIndex).pointee)")
return middishIndex
}
}
//MARK:- Code Execution
let a = PlaygroundSwingSet([0x1, 0x2, 0x3, 0x4]).swing() //a = 1
//Console prints
/*
truncated middle element's value is 3
this should yield the same thing: 2
*/
let b = PlaygroundSwingSet([]).swing() //b = 0
//Console prints
/*
data is empty
*/
无需转换数据,因为它支持 subscript operator 访问给定索引处的 UInt8
。
顺便说一句,它也支持isEmpty
,所以你不需要检查count是否为0。
var data = Data(repeating: 0, count: 5)
data.indices.forEach { data[[=10=]] = UInt8([=10=]) }
print(data[2])
我知道我能做到
//Setup
let originalBytes: [UInt8] = [0x0, 0x1, 0x2]
let originalData = Data(originalBytes)
//This
let getByteArFromData: [UInt8] = [UInt8](originalData)
现在,我漂亮确定getByteArFromData
将字节复制到新分配的内存中(我在这里错了吗?)。
随着数据规模的扩大(例如图像、编解码器操作等),这会变得 昂贵。
我有动力问这个问题,因为我想通过类似数组的接口来操作数据,但据我所知,Unsafe API 是可行的方法。所以...
使用不安全 API 是唯一的方法吗?这是推荐的方法吗?
我也玩过一些指针运算(刚刚进入不安全 API,不确定如果我可以使用类似数组的接口是否应该投入时间)。
这是一些 playground 代码(如果您愿意,请评论并回答评论中的问题):
import Foundation
//MARK:- Setup
final class PlaygroundSwingSet
{
//Squish unimportant setup code
private let byteAr: [UInt8]
private lazy var data: Data = {Data(byteAr)}()
init(_ bytes: [UInt8]) {self.byteAr = bytes}
func swing() -> Int
{
//Unsafe API stuff
return data.withUnsafeBytes {(dataBufferPtr) -> (Int) in //No need for [weak self], right?
//I don't like how the pointers are only valid in this closure.
//It adds an indent to my code to which my "code OCD" forces me to abstract out
self.middleOut(dataBufferPtr)
}
}
private func middleOut(_ dataBufferPtr: UnsafeRawBufferPointer) -> Int
{
//Yuck, I have to make sure count isn't 0 AND safely unwrap.
//Why is .baseAddress even optional then?
guard let dataPtr = dataBufferPtr.baseAddress?.assumingMemoryBound(to: UInt8.self), dataBufferPtr.count > 0 else {
print("data is empty")
return 0
}
let middishIndex = dataBufferPtr.count / 2 - 1
//More yuck.
print("truncated middle element's value is \(dataPtr.advanced(by: middishIndex).pointee)")
print("this should yield the same thing: \((dataPtr + middishIndex).pointee)")
return middishIndex
}
}
//MARK:- Code Execution
let a = PlaygroundSwingSet([0x1, 0x2, 0x3, 0x4]).swing() //a = 1
//Console prints
/*
truncated middle element's value is 3
this should yield the same thing: 2
*/
let b = PlaygroundSwingSet([]).swing() //b = 0
//Console prints
/*
data is empty
*/
无需转换数据,因为它支持 subscript operator 访问给定索引处的 UInt8
。
顺便说一句,它也支持isEmpty
,所以你不需要检查count是否为0。
var data = Data(repeating: 0, count: 5)
data.indices.forEach { data[[=10=]] = UInt8([=10=]) }
print(data[2])