StringLengthPrefixEncoding.swift 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
  1. //
  2. // StringLengthPrefixEncoding.swift
  3. // OmniBLE
  4. //
  5. // Created by Randall Knutson on 8/5/21.
  6. // Copyright © 2021 LoopKit Authors. All rights reserved.
  7. //
  8. import Foundation
  9. final class StringLengthPrefixEncoding {
  10. static private let LENGTH_BYTES = 2
  11. static func parseKeys(_ keys: Array<String>, _ payload: Data) throws -> [Data] {
  12. var ret = Array<Data>(repeating: Data(capacity: 0), count: keys.count)
  13. var remaining = payload
  14. for (index, key) in keys.enumerated() {
  15. guard remaining.count >= key.count else {
  16. throw PodProtocolError.messageIOException("Payload too short: \(payload)")
  17. }
  18. if (String(decoding: remaining.subdata(in: 0..<key.count), as: UTF8.self) != key) {
  19. throw PodProtocolError.messageIOException("Key not found: \(key) in \(payload.hexadecimalString)")
  20. }
  21. // last key can be empty, no length
  22. else if index == keys.count - 1 && remaining.count == key.count {
  23. return ret
  24. }
  25. guard remaining.count >= (key.count + LENGTH_BYTES) else {
  26. throw PodProtocolError.messageIOException("Payload too short: \(payload)")
  27. }
  28. remaining = remaining.subdata(in: key.count..<remaining.count)
  29. let length = Int(remaining[0...].toBigEndian(UInt16.self))
  30. guard remaining.count >= length else {
  31. throw PodProtocolError.messageIOException("Payload too short: \(payload)")
  32. }
  33. ret[index] = remaining.subdata(in: LENGTH_BYTES..<LENGTH_BYTES + length)
  34. remaining = remaining.subdata(in: LENGTH_BYTES + length..<remaining.count)
  35. }
  36. return ret
  37. }
  38. static func formatKeys(keys: Array<String>, payloads: Array<Data>) -> Data {
  39. let payloadTotalSize = payloads.reduce(0, { acc, i in acc + i.count })
  40. let keyTotalSize = keys.reduce(0, { acc, i in acc + i.count })
  41. let zeros = payloads.reduce(0, { acc, i in acc + (i.count == 0 ? 1 : 0) })
  42. var bb = Data(capacity: 2 * (keys.count - zeros) + keyTotalSize + payloadTotalSize)
  43. for (idx, key) in keys.enumerated() {
  44. let payload = payloads[idx]
  45. bb.append(key.data(using: .utf8)!)
  46. if (payload.count != 0) {
  47. bb.append(withUnsafeBytes(of: Int16(payload.count).bigEndian) { Data($0) })
  48. bb.append(payload)
  49. }
  50. }
  51. return bb
  52. }
  53. }