Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Extracting segments of Data object in Swift 3

Tags:

ios

swift

swift3

I have a 60 byte Data stream as follows

let myDataStream = Data(bytes: [24, 163, 209, 194, 255, 1, 184, 230, 37, 208, 140, 201, 6, 0, 64, 0, 7, 98, 108, 117, ...])

Byte #1 (index 0) represents represent the length of the first object, so in this case the first object is comprised of the first 24 objects including byte #1

I have tried numerous methods but I have been unable to figure out

  1. How to extract the first 24 bytes into a new Data object

  2. How to remove the first 24 bytes from myDataStream once #1 has been accomplished. As byte #25 will again have size of next object

The closest I have come to solving #1 is

let streamLength = Int(myDataStream[0])
let newStream = Data(bytes: myDataStream[0...streamLength])

but I get an error stating cannot invoke initializer for Data with Data.subsequence

like image 224
ChicagoSky Avatar asked Dec 11 '16 03:12

ChicagoSky


2 Answers

You can utilize a combination of the subdata and removeSubRange methods to achieve your desired results.

subData(in:) is an instance method on a Data struct.

removeSubRange() is also an instance method on a Data struct.

You can read more about these methods here: https://developer.apple.com/reference/foundation/data

Example Method:

func extract(from data: inout Data) -> Data? {
    guard data.count > 0 else {
        return nil
    }

    // Define the length of data to return
    let length = Int.init(data[0])

    // Create a range based on the length of data to return
    let range = Range(0..<length)

    // Get a new copy of data
    let subData = data.subdata(in: range)

    // Mutate data
    data.removeSubrange(range)

    // Return the new copy of data
    return subData
}

Usage:

// Data (27 bytes)
var data = Data(bytes: [24, 163, 209, 194, 255, 1, 184, 230, 37, 208, 140, 201, 6, 0, 64, 0, 7, 98, 108, 117, 42, 63, 78, 200, 3, 34, 36])

// First extraction
let first = extract(from: &data)
print(first!) // Prints 24 bytes
print(data) // Prints 3 bytes

// Second extraction
let second = extract(from: &data)
print(second!) // Prints 3 bytes
print(data) // Prints 0 bytes

// Third extraction
let third = extract(from: &data)
print(third ?? "No Data") // Prints "No Data"

Making an Extension

You could also wrap the extract method above in an extension of Data as follows:

extension Data {
    mutating func extract() -> Data? {
        guard self.count > 0 else {
            return nil
        }

        // Define the length of data to return
        let length = Int.init(self[0])

        // Create a range based on the length of data to return
        let range = Range(0..<length)

        // Get a new copy of data
        let subData = self.subdata(in: range)

        // Mutate data
        self.removeSubrange(range)

        // Return the new copy of data
        return subData
    }
}

Then you could you use the method like this:

// Data (27 bytes)
var data = Data(bytes: [24, 163, 209, 194, 255, 1, 184, 230, 37, 208, 140, 201, 6, 0, 64, 0, 7, 98, 108, 117, 42, 63, 78, 200, 3, 34, 36])

// First extraction
let first = data.extract()
print(first!) // Prints 24 bytes
print(data) // Prints 3 bytes

// Second extraction
let second = data.extract()
print(second!) // Prints 3 bytes
print(data) // Prints 0 bytes

// Third extraction
let third = data.extract()
print(third ?? "No Data") // Prints "No Data"
like image 172
Ryan H. Avatar answered Sep 30 '22 17:09

Ryan H.


Data has dedicated functions to handle subdata:

let streamLength = Int(myDataStream[0])
let newStream = myDataStream.subdata(in: 1..<streamLength + 1)
let nextStream = myDataStream.subdata(in: streamLength + 1..<myDataStream.count)
like image 21
vadian Avatar answered Sep 30 '22 17:09

vadian