Skip to content

Instantly share code, notes, and snippets.

@ChrisLawther
Created August 21, 2018 13:09
Show Gist options
  • Save ChrisLawther/58bf45ffc32fedb8ea47306df948fc5e to your computer and use it in GitHub Desktop.
Save ChrisLawther/58bf45ffc32fedb8ea47306df948fc5e to your computer and use it in GitHub Desktop.
// Allows reading bytes, 16-bit words, 32-bit words etc. out of the supplied buffer.
// Often useful when decoding data from, for example, a Bluetooth device which produces
// data containing flags to indicate the presence/absence of optional fields.
// See https://codereview.stackexchange.com/q/196429/171827 for it's genesis/evolution
public struct ConsumableByteArray {
private let bytes: [UInt8]
private var idx = 0
enum ReadError: Error, LocalizedError {
typealias RawValue = Int
case notEnoughBytes(available: Int, requested: Int)
public var errorDescription: String? {
switch self {
case .notEnoughBytes(let available, let requested):
return "Not enough bytes remaining in buffer (available: \(available), requested: \(requested))"
}
}
}
init(data: Data) {
bytes = [UInt8](data)
}
init(bytes: [UInt8]) {
self.bytes = bytes
}
mutating func read<T: FixedWidthInteger>(_: T.Type) throws -> T {
let size = MemoryLayout<T>.size
guard idx + size <= bytes.count else {
throw ReadError.notEnoughBytes(available: bytes.count - idx, requested: size)
}
let offset = idx
idx += size
return bytes[offset..<offset + size].enumerated().reduce(0) {
$0 + T($1.element) << (8 * $1.offset)
}
}
}
// Usage:
let bytes = [1,2,3,4]
var consumable = ConsumableByteArray(bytes: bytes)
let firstByte = try! cba.consume(UInt8.self)
let middle16 = try! cba.consume(Int16.self)
XCTAssertEqual(256*bytes[2] + bytes[1], middle16)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment