2016-02-19 18 views
5

Ich versuche, einige Binärdatei Parsing in Swift zu tun, und obwohl ich Dinge arbeiten, habe ich eine Situation, wo ich variable Felder haben.Ansatz zum Lesen einer beliebigen Anzahl von Bits in Swift

Ich habe alle meine Parsing im Standardfall arbeitet

Ich packe

1-bit field 
1-bit field 
1-bit field 
11-bits field 
1-bit field 
(optional) 4-bit field 
(optional) 4-bit field 
1-bit field 
2-bit field 
(optional) 4-bit field 
5-bit field 
6-bit field 
(optional) 6-bit field 
(optional) 24-bit field 
(junk data - up until byte buffer 0 - 7 bits as needed) 

meisten Daten verwendet nur einen bestimmten Satz von optionals so habe ich weitergemacht und begann Klassen schreiben zu handhaben diese Daten. Mein allgemeiner Ansatz ist es, eine Zeigerstruktur zu erstellen und dann einen Byte-Array aus, dass konstruieren:

let rawData: NSMutableData = NSMutableData(data: input_nsdata) 
var ptr: UnsafeMutablePointer<UInt8> = UnsafeMutablePointer<UInt8(rawData.mutableBytes) 
bytes = UnsafeMutableBufferPointer<UInt8>(start: ptr, count: rawData.length - offset) 

Also hast ich mit einer Reihe von [UInt8] am Ende arbeiten und ich kann meine Analyse in ähnlicher Weise wie tun:

let b1 = (bytes[3] & 0x01) << 5 
let b2 = (bytes[4] & 0xF8) >> 3 
    return Int(b1 | b2) 

Also wo ich Probleme habe ist mit den optionalen Feldern, da meine Daten nicht speziell auf Bytegrenzen liegen wird alles kompliziert. In der idealen Welt würde ich wahrscheinlich nur direkt mit dem Zeiger arbeiten und ihn nach Bedarf um Bytes vorrücken, aber ich bin mir nicht bewusst, dass ich einen Zeiger um 3 Bits vorrücken kann - was mich zu meiner Frage

bringt

Was ist der beste Ansatz, um mit meiner Situation umzugehen?

Eine Idee, die ich dachte, war mit verschiedenen Strukturen zu kommen, die die optionalen Felder widerspiegeln, außer dass ich nicht sicher bin, wie man Bit-ausgerichtete gepackte Strukturen erzeugt.

Was ist meine beste Vorgehensweise hier? Zur Klarstellung - die ersten 1-bit Felder bestimmen, welche der optionalen Felder gesetzt sind.

+1

Sofern Sie nicht ausdrücklich suchen Ihre eigene Implementierung zu entwerfen, würde ich einfach nutzen [CFBitVector] (https://developer.apple. com/bibliothek/mac/documentation/CoreFoundation/Referenz/CFBitVectorRef /). –

+0

@SamR .: CFBitVector ist praktisch, um eine große Menge von Bits zu verwalten, aber - es sei denn, ich übersehen etwas - hilft nicht beim Abrufen einer Zahl, die in mehreren Bits gespeichert ist, vielleicht über Byte Grenzen. Es ist wahrscheinlich nicht sehr effektiv, alle Bits für ein Feld getrennt abzurufen und die Anzahl daraus zu bilden. –

Antwort

2

Wenn die Felder nicht auf Bytegrenzen liegen, müssen Sie die Spur des aktuellen Bytes und der aktuellen Bitposition innerhalb eines Bytes behalten.

Hier ist eine mögliche Lösung, die es erlaubt, eine beliebige Nummer von Bits aus einem Datenarray zu lesen und führt die gesamte Buchhaltung. Die einzige Einschränkung ist, dass das Ergebnis nextBits() in eine UInt passen muss (32 oder 64 Bit, je nach Plattform).

struct BitReader { 

    private let data : [UInt8] 
    private var byteOffset : Int 
    private var bitOffset : Int 

    init(data : [UInt8]) { 
     self.data = data 
     self.byteOffset = 0 
     self.bitOffset = 0 
    } 

    func remainingBits() -> Int { 
     return 8 * (data.count - byteOffset) - bitOffset 
    } 

    mutating func nextBits(numBits : Int) -> UInt { 
     precondition(numBits <= remainingBits(), "attempt to read more bits than available") 

     var bits = numBits  // remaining bits to read 
     var result : UInt = 0 // result accumulator 

     // Read remaining bits from current byte: 
     if bitOffset > 0 { 
      if bitOffset + bits < 8 { 
       result = (UInt(data[byteOffset]) & UInt(0xFF >> bitOffset)) >> UInt(8 - bitOffset - bits) 
       bitOffset += bits 
       return result 
      } else { 
       result = UInt(data[byteOffset]) & UInt(0xFF >> bitOffset) 
       bits = bits - (8 - bitOffset) 
       bitOffset = 0 
       byteOffset = byteOffset + 1 
      } 
     } 

     // Read entire bytes: 
     while bits >= 8 { 
      result = (result << UInt(8)) + UInt(data[byteOffset]) 
      byteOffset = byteOffset + 1 
      bits = bits - 8 
     } 

     // Read remaining bits: 
     if bits > 0 { 
      result = (result << UInt(bits)) + (UInt(data[byteOffset]) >> UInt(8 - bits)) 
      bitOffset = bits 
     } 

     return result 
    } 
} 

Beispiel Nutzung:

let data : [UInt8] = ... your data ... 
var bitReader = BitReader(data: data) 

let b1 = bitReader.nextBits(1) 
let b2 = bitReader.nextBits(1) 
let b3 = bitReader.nextBits(1) 
let b4 = bitReader.nextBits(11) 
let b5 = bitReader.nextBits(1) 
if b1 > 0 { 
    let b6 = bitReader.nextBits(4) 
    let b7 = bitReader.nextBits(4) 
} 
// ... and so on ... 

Und hier ist eine weitere mögliche UMSETZUNG, die ein bisschen einfacher ist und vielleicht effektiver. Es sammelt Bytes in eine UInt und extrahiert dann das Ergebnis in einem einzigen Schritt. Hier ist die Einschränkung, dass numBits + 7 kleiner oder gleich zu der Anzahl der Bits in einem UInt (32 oder 64) sein muss. (Natürlich UInt kann durch UInt64 ersetzen werden es machen plattformunabhängig.)

struct BitReader { 
    private let data : [UInt8] 
    private var byteOffset = 0 
    private var currentValue : UInt = 0 // Bits which still have to be consumed 
    private var currentBits = 0   // Number of valid bits in `currentValue` 

    init(data : [UInt8]) { 
     self.data = data 
    } 

    func remainingBits() -> Int { 
     return 8 * (data.count - byteOffset) + currentBits 
    } 

    mutating func nextBits(numBits : Int) -> UInt { 
     precondition(numBits <= remainingBits(), "attempt to read more bits than available") 

     // Collect bytes until we have enough bits: 
     while currentBits < numBits { 
      currentValue = (currentValue << 8) + UInt(data[byteOffset]) 
      currentBits = currentBits + 8 
      byteOffset = byteOffset + 1 
     } 

     // Extract result: 
     let remaining = currentBits - numBits 
     let result = currentValue >> UInt(remaining) 

     // Update remaining bits: 
     currentValue = currentValue & UInt(1 << remaining - 1) 
     currentBits = remaining 
     return result 
    } 

} 
Verwandte Themen