Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pointers, Pointer Arithmetic, and Raw Data in Swift

Tags:

swift

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.

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?

Is there a better way to do this in 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:

@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

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.

like image 260
Eduardo Avatar asked Jun 05 '14 18:06

Eduardo


People also ask

What is pointers in Swift?

A pointer is an object that stores a memory address. In Swift, there are 8 types of pointers. In Swift, pointers are known as UnsafePointer because they support direct operations on memory and these operations are unsafe in nature that's why a Unsafe prefix is used with Pointers in swift.

What is pointer pointer arithmetic with example?

When a pointer is incremented, it actually increments by the number equal to the size of the data type for which it is a pointer. For Example: If an integer pointer that stores address 1000 is incremented, then it will increment by 2(size of an int) and the new address it will points to 1002.

What is UnsafePointer Swift?

Using Pointers in Unsafe Swift A pointer encapsulates a memory address. Types that involve direct memory access get an unsafe prefix, so the pointer type name is UnsafePointer . The extra typing may seem annoying, but it reminds you that you're accessing memory that the compiler isn't checking.

What is an unsafe pointer?

The type of data that a pointer can access is the pointer's Pointee type. UnsafePointer provides no automated memory management or alignment guarantees. You are responsible for handling the life cycle of any memory you work with through unsafe pointers to avoid leaks or undefined behavior.


2 Answers

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

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

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

   let castPointer = UnsafePointer<MyStruct>(oldPointer)
like image 146
russbishop Avatar answered Sep 16 '22 14:09

russbishop


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

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])
    }
}
like image 37
Erik Avatar answered Sep 18 '22 14:09

Erik