I'm trying to figure out how to "chunk" a NSData
on a specific value.
7E 55 33 22 7E 7E 22 AE BC 7E 7E AA AA 00 20 00 22 53 25 A3 4E 7E
Returns an array of 3 elements of type [NSData]
where the elements are:
55 33 22
22 AE BC
AA AA 00 20 00 22 53 25 A3 4E
I know I can do something like:
var ptr = UnsafePointer<UInt8>(data.bytes)
var bytes = UnsafeBufferPointer<UInt8>(start: ptr, count: data.length)
And I guess iterate through doing a comparison similar to:
bytes[1] == UInt8(0x7E)
And I guess build up "ranges" but I was wondering if there is a better way to go about this?
private fund chunkMessage(data: NSData) -> [NSData] {
var ptr = UnsafePointer<UInt8>(data.bytes)
var bytes = UnsafeBufferPointer<UInt8>(start: ptr, count: data.length)
var ret = []
// DO SOME STUFF
return ret as! [NSData];
}
There are probably many possible solutions. A straight-forward way, using
NSData
methods, would be
func chunkMessage(data: NSData, var separator : UInt8) -> [NSData] {
let sepdata = NSData(bytes: &separator, length: 1)
var chunks : [NSData] = []
// Find first occurrence of separator:
var searchRange = NSMakeRange(0, data.length)
var foundRange = data.rangeOfData(sepdata, options: nil, range: searchRange)
while foundRange.location != NSNotFound {
// Append chunk (if not empty):
if foundRange.location > searchRange.location {
chunks.append(data.subdataWithRange(NSMakeRange(searchRange.location, foundRange.location - searchRange.location)))
}
// Search next occurrence of separator:
searchRange.location = foundRange.location + foundRange.length
searchRange.length = data.length - searchRange.location
foundRange = data.rangeOfData(sepdata, options: nil, range: searchRange)
}
// Check for final chunk:
if searchRange.length > 0 {
chunks.append(data.subdataWithRange(searchRange))
}
return chunks
}
As already suggested in the comments, you could work with Swift arrays instead. Here is a possible implementation:
func chunkMessage(data: NSData, separator : UInt8) -> [[UInt8]] {
let bytes = UnsafeBufferPointer<UInt8>(start: UnsafePointer(data.bytes), count: data.length)
// Positions of separator bytes:
let positions = filter(enumerate(bytes), { $1 == separator } ).map( { $0.0 } )
// Non-empty ranges between the separator bytes:
let ranges = map(Zip2([-1] + positions, positions + [bytes.count])) {
(from : Int, to : Int) -> (Int, Int) in
(from + 1, to - from - 1)
}.filter( { $1 > 0 } )
// Byte chunks between the separator bytes:
let chunks = map(ranges) {
(start: Int, count : Int) -> [UInt8] in
Array(UnsafeBufferPointer(start: bytes.baseAddress + start, count: count))
}
return chunks
}
I leave it to you to test which one performs better :)