2014-06-05 9 views
18

La mia applicazione utilizza una struttura di dati inmutabili un po 'complessa che è codificata in un file binario. Ho bisogno di avere accesso ad esso a livello di byte, evitando qualsiasi copia. Normalmente, userei l'aritmetica e la tipizzazione del puntatore C o C++, per accedere e interpretare i valori dei byte grezzi. Mi piacerebbe fare lo stesso con Swift.Puntatori, aritmetica del puntatore e dati grezzi in Swift

Ho scoperto che le seguenti opere:

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 
    } 
} 

Tuttavia, io non sono sicuro di come sia efficiente. data.subdataWithRange e NSMakeRange allocano oggetti ogni volta che li chiamo, o sono solo zucchero sintattico per gestire i puntatori?

C'è un modo migliore per farlo in Swift?

EDIT:

Ho creato una piccola classe Objective-C che incapsula solo una funzione di compensare un puntatore da un determinato numero di byte:

@implementation RawDataOffsetPointer 

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

@end 

Se includere questa classe in l'intestazione bridging, allora posso cambiare il mio metodo read a

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

che non copiare i dati dal mio b uffer, o allocare altri oggetti.

Tuttavia, sarebbe comunque opportuno eseguire qualche calcolo aritmetico da Swift, se fosse possibile.

+0

Il mio istinto dice che se il sottodata è immutabile, swift non si preoccuperebbe di copiarlo. –

+1

Proprio su questo punto c'è un modo in Swift per leggere un flusso di 'UInt8's da un NSData in un modo completamente sicuro? O da un file anche? Sto usando questo dsdata NSData/subdata in un dingus di file binario che sto scrivendo ora, odio davvero guardare quelle sciocchezze "non sicure" ma sembra l'unico modo per ottenere i byte grezzi dal file. – iluvcapra

risposta

16

Se si desidera solo per farlo direttamente, UnsafePointer<T> possono essere manipolati aritmeticamente:

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

Si può anche lanciare un puntatore in questo modo (UnsafePointer<()> è equivalente a void *)

let castPointer = UnsafePointer<MyStruct>(oldPointer) 
+0

'let oldPointer = UnsafePointer <()>' mi dà un errore ''>' non è un suffisso operatore unario'. Mi sto perdendo qualcosa? – Robert

15

Si consiglia di esaminare NSInputStream, che consente di leggere NSData come una serie di byte (UInt8 in Swift).

Ecco un piccolo esempio che ho messo insieme nel parco giochi:

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 
} 

È possibile creare un extension di leggere tipi primitivi in ​​questo modo:

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]) 
    } 
} 
+0

Questo è esattamente il tipo di cosa che stavo cercando! –

11

lo consiglio il modo più semplice da usare UnsafeArray.

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

Quando provo questo, ottengo un EXC_BAD_ACCESS quando arriva alla riga dei byte. – Brian

+0

Con la versione corrente di Swift, l'ultima riga deve essere 'lasciare byte = UnsafeBufferPointer (inizio: ptr, conteggio: data.length)' –