From a2825fbc199c1e4da7fc6cbad73c739d892e72e9 Mon Sep 17 00:00:00 2001 From: Alsey Coleman Miller Date: Mon, 4 Nov 2024 17:53:20 -0500 Subject: [PATCH] #161 Add `DataContainer` --- Sources/Bluetooth/Data.swift | 80 +++++++++++++++++++ .../Extensions/DataConvertible.swift | 51 +++--------- .../Bluetooth/LowEnergyAdvertisingData.swift | 7 +- 3 files changed, 92 insertions(+), 46 deletions(-) create mode 100644 Sources/Bluetooth/Data.swift diff --git a/Sources/Bluetooth/Data.swift b/Sources/Bluetooth/Data.swift new file mode 100644 index 000000000..12cf03db2 --- /dev/null +++ b/Sources/Bluetooth/Data.swift @@ -0,0 +1,80 @@ +// +// DataContainer.swift +// Bluetooth +// +// Created by Alsey Coleman Miller on 11/4/24. +// + +#if canImport(Foundation) +import Foundation +#endif + +/// Data container type. +public protocol DataContainer: RandomAccessCollection where Self.Index == Int, Self.Element == UInt8, Self: Hashable { + + init() + + init (_ collection: C) where C.Element == UInt8 + + mutating func reserveCapacity(_ capacity: Int) + + subscript(index: Int) -> UInt8 { get } + + mutating func append(_ newElement: UInt8) + + mutating func append(_ pointer: UnsafePointer, count: Int) + + mutating func append (contentsOf bytes: C) where C.Element == UInt8 + + static func += (lhs: inout Self, rhs: UInt8) + + static func += (lhs: inout Self, rhs: C) where C.Element == UInt8 + + /// Return a new copy of the data in a specified range. + /// + /// - parameter range: The range to copy. + func subdata(in range: Range) -> Self +} + +#if canImport(Foundation) +extension Data: DataContainer { + + public static func += (lhs: inout Data, rhs: UInt8) { + lhs.append(rhs) + } +} +#endif + +extension LowEnergyAdvertisingData: DataContainer { + + public mutating func reserveCapacity(_ capacity: Int) { + // does nothing + } + + public func subdata(in range: Range) -> LowEnergyAdvertisingData { + var data = LowEnergyAdvertisingData() + for (newIndex, oldIndex) in range.enumerated() { + data[newIndex] = self[oldIndex] + } + return data + } +} + +extension Array: DataContainer where Self.Element == UInt8 { + + public static func += (lhs: inout Array, rhs: UInt8) { + lhs.append(rhs) + } + + public mutating func append(_ pointer: UnsafePointer, count: Int) { + let newCapacity = self.count + count + self.reserveCapacity(newCapacity) + for index in 0 ..< count { + self.append(pointer[index]) + } + } + + public func subdata(in range: Range) -> [UInt8] { + .init(self[range]) + } +} diff --git a/Sources/Bluetooth/Extensions/DataConvertible.swift b/Sources/Bluetooth/Extensions/DataConvertible.swift index 122a43d35..5af7a03d0 100644 --- a/Sources/Bluetooth/Extensions/DataConvertible.swift +++ b/Sources/Bluetooth/Extensions/DataConvertible.swift @@ -20,18 +20,24 @@ internal protocol DataConvertible { var dataLength: Int { get } } -#if canImport(Foundation) -extension Data { +extension DataContainer { /// Initialize data with contents of value. init (_ value: T) { let length = value.dataLength - self.init(capacity: length) + self.init() + self.reserveCapacity(length) self += value assert(self.count == length) } } -#endif + +extension DataContainer { + + mutating func append (_ value: T) { + self += value + } +} extension BluetoothUUID: DataConvertible { @@ -94,40 +100,3 @@ extension UInt32: UnsafeDataConvertible { } extension UInt64: UnsafeDataConvertible { } extension UInt128: UnsafeDataConvertible { } extension BluetoothAddress: UnsafeDataConvertible { } - -// MARK: - DataContainer - -/// Data container type. -@usableFromInline -internal protocol DataContainer: RandomAccessCollection where Self.Index == Int { - - subscript(index: Int) -> UInt8 { get } - - mutating func append(_ newElement: UInt8) - - mutating func append(_ pointer: UnsafePointer, count: Int) - - mutating func append (contentsOf bytes: C) where C.Element == UInt8 - - static func += (lhs: inout Self, rhs: UInt8) - static func += (lhs: inout Self, rhs: C) where C.Element == UInt8 -} - -extension DataContainer { - - mutating func append (_ value: T) { - self += value - } -} - -#if canImport(Foundation) -extension Data: DataContainer { - - @usableFromInline - static func += (lhs: inout Data, rhs: UInt8) { - lhs.append(rhs) - } -} -#endif - -extension LowEnergyAdvertisingData: DataContainer { } diff --git a/Sources/Bluetooth/LowEnergyAdvertisingData.swift b/Sources/Bluetooth/LowEnergyAdvertisingData.swift index 1a3f85af5..30e905b16 100644 --- a/Sources/Bluetooth/LowEnergyAdvertisingData.swift +++ b/Sources/Bluetooth/LowEnergyAdvertisingData.swift @@ -76,11 +76,8 @@ public extension LowEnergyAdvertisingData { } } - init? (_ collection: C) where C.Element == UInt8 { - - guard collection.count <= 31 - else { return nil } - + init (_ collection: C) where C.Element == UInt8 { + precondition(collection.count <= 31) self.init() self.length = UInt8(collection.count) collection.enumerated().forEach {