指针、指针算法和Swift中的原始数据

时间:2021-10-01 21:19:59

My application uses a somewhat complex inmutable data structure that is encoded in a binary file. I need to have access to it at the byte level, avoiding any copying. Normally, I would use C or C++ pointer arithmetic and typecasts, to access and interpret the raw byte values. I would like to do the same with Swift.

我的应用程序使用了一个有点复杂的in可变数据结构,它被编码在一个二进制文件中。我需要在字节级别*问它,避免任何复制。通常,我将使用C或c++指针算法和类型转换来访问和解释原始字节值。我也想和斯威夫特一样。

I have found that the following works:

我发现以下作品:

class RawData {
    var data: NSData!

    init(rawData: NSData) {
      data = rawData
    }

    func read<T>(byteLocation: Int) -> T {
      let bytes = data.subdataWithRange(NSMakeRange(byteLocation, sizeof(T))).bytes
      return UnsafePointer<T>(bytes).memory
    }

    func example_ReadAnIntAtByteLocation5() -> Int {
      return read(5) as Int
    }
}

However, I am not sure how efficient it is. Do data.subdataWithRange and NSMakeRange allocate objects every time I call them, or are they just syntactic sugar for dealing with pointers?

然而,我不确定它有多高效。做数据。subdataWithRange和NSMakeRange每次调用它们时都分配对象,或者它们只是用于处理指针的语法糖?

Is there a better way to do this in Swift?

是否有更好的方法在Swift中实现这一点?

EDIT:

编辑:

I have created a small Objective-C class that just encapsulates a function to offset a pointer by a given number of bytes:

我创建了一个小的Objective-C类,它只是封装了一个函数,将指针偏移到给定的字节数:

@implementation RawDataOffsetPointer

inline void* offsetPointer(void* ptr, int bytes){
    return (char*)ptr + bytes;
}

@end

If I include this class in the bridging header, then I can change my read method to

如果我在桥接头中包含这个类,那么我可以将我的read方法更改为

func read<T>(byteLocation: Int) -> T {
  let ptr = offsetPointer(data.bytes, CInt(byteLocation))
  return UnsafePointer<T>(ptr).memory
}

which will not copy data from my buffer, or allocate other objects.

它不会从我的缓冲区复制数据,也不会分配其他对象。

However, it would still be nice to do some pointer arithmetic from Swift, if it were possible.

但是,如果可能的话,从Swift中做一些指针运算还是不错的。

3 个解决方案

#1


16  

If you just want to do it directly, UnsafePointer<T> can be manipulated arithmetically:

如果你只是想直接操作,UnsafePointer 可以用算术方法操作:

   let oldPointer = UnsafePointer<()>
   let newPointer = oldPointer + 10

You can also cast a pointer like so (UnsafePointer<()> is equivalent to void *)

您还可以释放一个指针,如so (UnsafePointer<()>等价于void *)

   let castPointer = UnsafePointer<MyStruct>(oldPointer)

#2


15  

I would recommend looking into NSInputStream, which allows you to read NSData as a series of bytes (UInt8 in Swift).

我建议查看NSInputStream,它允许您以一系列字节(Swift中的UInt8)读取NSData。

Here is a little sample I put together in the playground:

这是我在操场上收集的一个小样本:

func generateRandomData(count:Int) -> NSData
{
    var array = Array<UInt8>(count: count, repeatedValue: 0)

    arc4random_buf(&array, UInt(count))
    return NSData(bytes: array, length: count)
}

let randomData = generateRandomData(256 * 1024)

let stream = NSInputStream(data: randomData)
stream.open() // IMPORTANT

var readBuffer = Array<UInt8>(count: 16 * 1024, repeatedValue: 0)

var totalBytesRead = 0

while (totalBytesRead < randomData.length)
{
    let numberOfBytesRead = stream.read(&readBuffer, maxLength: readBuffer.count)

    // Do something with the data

    totalBytesRead += numberOfBytesRead
}

You can create an extension to read primitive types like so:

您可以创建一个扩展来读取原始类型,如下所示:

extension NSInputStream
{
    func readInt32() -> Int
    {
        var readBuffer = Array<UInt8>(count:sizeof(Int32), repeatedValue: 0)

        var numberOfBytesRead = self.read(&readBuffer, maxLength: readBuffer.count)

        return Int(readBuffer[0]) << 24 |
            Int(readBuffer[1]) << 16 |
            Int(readBuffer[2]) << 8 |
            Int(readBuffer[3])
    }
}

#3


11  

I would recommend the simple way to use UnsafeArray.

我建议使用UnsafeArray的简单方法。

let data = NSData(contentsOfFile: filename)
let ptr = UnsafePointer<UInt8>(data.bytes)
let bytes = UnsafeBufferPointer<UInt8>(start:ptr, count:data.length)

#1


16  

If you just want to do it directly, UnsafePointer<T> can be manipulated arithmetically:

如果你只是想直接操作,UnsafePointer 可以用算术方法操作:

   let oldPointer = UnsafePointer<()>
   let newPointer = oldPointer + 10

You can also cast a pointer like so (UnsafePointer<()> is equivalent to void *)

您还可以释放一个指针,如so (UnsafePointer<()>等价于void *)

   let castPointer = UnsafePointer<MyStruct>(oldPointer)

#2


15  

I would recommend looking into NSInputStream, which allows you to read NSData as a series of bytes (UInt8 in Swift).

我建议查看NSInputStream,它允许您以一系列字节(Swift中的UInt8)读取NSData。

Here is a little sample I put together in the playground:

这是我在操场上收集的一个小样本:

func generateRandomData(count:Int) -> NSData
{
    var array = Array<UInt8>(count: count, repeatedValue: 0)

    arc4random_buf(&array, UInt(count))
    return NSData(bytes: array, length: count)
}

let randomData = generateRandomData(256 * 1024)

let stream = NSInputStream(data: randomData)
stream.open() // IMPORTANT

var readBuffer = Array<UInt8>(count: 16 * 1024, repeatedValue: 0)

var totalBytesRead = 0

while (totalBytesRead < randomData.length)
{
    let numberOfBytesRead = stream.read(&readBuffer, maxLength: readBuffer.count)

    // Do something with the data

    totalBytesRead += numberOfBytesRead
}

You can create an extension to read primitive types like so:

您可以创建一个扩展来读取原始类型,如下所示:

extension NSInputStream
{
    func readInt32() -> Int
    {
        var readBuffer = Array<UInt8>(count:sizeof(Int32), repeatedValue: 0)

        var numberOfBytesRead = self.read(&readBuffer, maxLength: readBuffer.count)

        return Int(readBuffer[0]) << 24 |
            Int(readBuffer[1]) << 16 |
            Int(readBuffer[2]) << 8 |
            Int(readBuffer[3])
    }
}

#3


11  

I would recommend the simple way to use UnsafeArray.

我建议使用UnsafeArray的简单方法。

let data = NSData(contentsOfFile: filename)
let ptr = UnsafePointer<UInt8>(data.bytes)
let bytes = UnsafeBufferPointer<UInt8>(start:ptr, count:data.length)