diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index ad8d099..feb3c8b 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -20,14 +20,14 @@ jobs: name: Build and Test Swift Package iOS uses: StanfordSpezi/.github/.github/workflows/xcodebuild-or-fastlane.yml@v2 with: - scheme: SpeziFileFormats-Package + scheme: SpeziFileFormats resultBundle: SpeziFileFormats-iOS.xcresult artifactname: SpeziFileFormats-iOS.xcresult watchos: name: Build and Test Swift Package watchOS uses: StanfordSpezi/.github/.github/workflows/xcodebuild-or-fastlane.yml@v2 with: - scheme: SpeziFileFormats-Package + scheme: SpeziFileFormats destination: 'platform=watchOS Simulator,name=Apple Watch Series 9 (45mm)' resultBundle: SpeziFileFormats-watchOS.xcresult artifactname: SpeziFileFormats-watchOS.xcresult @@ -35,7 +35,7 @@ jobs: name: Build and Test Swift Package visionOS uses: StanfordSpezi/.github/.github/workflows/xcodebuild-or-fastlane.yml@v2 with: - scheme: SpeziFileFormats-Package + scheme: SpeziFileFormats destination: 'platform=visionOS Simulator,name=Apple Vision Pro' resultBundle: SpeziFileFormats-visionOS.xcresult artifactname: SpeziFileFormats-visionOS.xcresult @@ -44,7 +44,7 @@ jobs: uses: StanfordSpezi/.github/.github/workflows/xcodebuild-or-fastlane.yml@v2 with: runsonlabels: '["macOS", "self-hosted"]' - scheme: SpeziFileFormats-Package + scheme: SpeziFileFormats resultBundle: SpeziFileFormats-tvOS.xcresult destination: 'platform=tvOS Simulator,name=Apple TV 4K (3rd generation)' artifactname: SpeziFileFormats-tvOS.xcresult @@ -54,7 +54,7 @@ jobs: with: runsonlabels: '["macOS", "self-hosted"]' xcodeversion: latest - scheme: SpeziFileFormats-Package + scheme: SpeziFileFormats resultBundle: SpeziFileFormats-macOS.xcresult destination: 'platform=macOS,arch=arm64' artifactname: SpeziFileFormats-macOS.xcresult diff --git a/.spi.yml b/.spi.yml index f7549a6..4e89a74 100644 --- a/.spi.yml +++ b/.spi.yml @@ -11,5 +11,4 @@ builder: configs: - platform: ios documentation_targets: - - ByteCoding - - XCTByteCoding + - EDFFormat diff --git a/Package.swift b/Package.swift index 270b59d..d58bc6e 100644 --- a/Package.swift +++ b/Package.swift @@ -21,44 +21,22 @@ let package = Package( .tvOS(.v16) ], products: [ - .library(name: "ByteCoding", targets: ["ByteCoding"]), - .library(name: "EDFFormat", targets: ["EDFFormat"]), - .library(name: "XCTByteCoding", targets: ["XCTByteCoding"]) + .library(name: "EDFFormat", targets: ["EDFFormat"]) ], dependencies: [ - .package(url: "https://github.com/apple/swift-nio.git", from: "2.59.0") + .package(url: "https://github.com/StanfordSpezi/SpeziNetworking.git", branch: "initial-setup") ], targets: [ - .target( - name: "ByteCoding", - dependencies: [ - .product(name: "NIO", package: "swift-nio"), - .product(name: "NIOFoundationCompat", package: "swift-nio") - ] - ), .target( name: "EDFFormat", dependencies: [ - .target(name: "ByteCoding") - ] - ), - .target( - name: "XCTByteCoding", - dependencies: [ - .target(name: "ByteCoding") - ] - ), - .testTarget( - name: "ByteCodingTests", - dependencies: [ - .target(name: "ByteCoding"), - .target(name: "XCTByteCoding") + .product(name: "ByteCoding", package: "SpeziNetworking") ] ), .testTarget( name: "EDFFormatTests", dependencies: [ - .target(name: "ByteCoding"), + .product(name: "ByteCoding", package: "SpeziNetworking"), .target(name: "EDFFormat") ] ) diff --git a/Sources/ByteCoding/Bool+ByteCodable.swift b/Sources/ByteCoding/Bool+ByteCodable.swift deleted file mode 100644 index a18891b..0000000 --- a/Sources/ByteCoding/Bool+ByteCodable.swift +++ /dev/null @@ -1,37 +0,0 @@ -// -// This source file is part of the Stanford Spezi open-source project -// -// SPDX-FileCopyrightText: 2024 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import NIO - - -extension Bool: ByteCodable { - /// Decode a `Bool` from its byte representation. - /// - /// - Note: Note that the byte representation uses a whole byte. - /// - Parameters: - /// - byteBuffer: The ByteBuffer to decode from. - /// - endianness: The endianness to use for decoding. - public init?(from byteBuffer: inout ByteBuffer, preferredEndianness endianness: Endianness) { - guard let bytes = byteBuffer.readBytes(length: 1), - let byte = bytes.first else { - return nil - } - - self = byte > 0 - } - - /// Encodes a `Bool` to its byte representation. - /// - /// - Note: Note that the byte representation uses a whole byte. - /// - Parameters: - /// - byteBuffer: The ByteBuffer to write to. - /// - endianness: The endianness to use for encoding. - public func encode(to byteBuffer: inout ByteBuffer, preferredEndianness endianness: Endianness) { - byteBuffer.writeBytes([self ? 1 : 0]) - } -} diff --git a/Sources/ByteCoding/ByteCodable.swift b/Sources/ByteCoding/ByteCodable.swift deleted file mode 100644 index 7149225..0000000 --- a/Sources/ByteCoding/ByteCodable.swift +++ /dev/null @@ -1,119 +0,0 @@ -// -// This source file is part of the Stanford Spezi open-source project -// -// SPDX-FileCopyrightText: 2023 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import Foundation -import NIOCore -import NIOFoundationCompat - - -/// A type that is decodable from a byte representation. -/// -/// Conforming types can be decoded from a `ByteBuffer´ assuming it holds -/// properly formatted binary data. -public protocol ByteDecodable { - /// Decode the type from the `ByteBuffer`. - /// - /// Initialize a new instance using the byte representation provided by the `ByteBuffer`. - /// This call should move the `readerIndex` forwards. - /// - /// - Note: Returns nil if no valid byte representation could be found. - /// - Parameters: - /// - byteBuffer: The ByteBuffer to read from. - /// - endianness: The preferred endianness to use for decoding if applicable. - /// This might not apply to certain data structures that operate on single byte level. - init?(from byteBuffer: inout ByteBuffer, preferredEndianness endianness: Endianness) -} - - -/// A type that is decodable to a byte representation. -/// -/// Conforming types can be encoded into a `ByteBuffer`. -public protocol ByteEncodable { - /// Encode into the `ByteBuffer`. - /// - /// Encode the byte representation of this type into the provided `ByteBuffer`. - /// This call should move the `writerIndex` forwards. - /// - /// - Parameters: - /// - byteBuffer: The ByteBuffer to write into. - /// - endianness: The preferred endianness to use for encoding if applicable. - /// This might not apply to certain data structures that operate on single byte level. - func encode(to byteBuffer: inout ByteBuffer, preferredEndianness endianness: Endianness) -} - - -/// A type that is encodable to and decodable from and to a byte representation. -/// -/// Conforming types can be encoded into or decodable from a `ByteBuffer`. -public typealias ByteCodable = ByteEncodable & ByteDecodable - - -extension ByteDecodable { - /// Decode the type from the `ByteBuffer`. - /// - /// Initialize a new instance using the byte representation provided by the `ByteBuffer`. - /// This call should move the `readerIndex` forwards. - /// - /// - Important: This uses `little` endianness as the default preferred endianness. - /// - /// - Note: Returns nil if no valid byte representation could be found. - /// - Parameter byteBuffer: The ByteBuffer to read from. - @_disfavoredOverload - public init?(from byteBuffer: inout ByteBuffer) { - self.init(from: &byteBuffer, preferredEndianness: .little) - } -} - - -extension ByteEncodable { - /// Encode into the `ByteBuffer`. - /// - /// Encode the byte representation of this type into the provided `ByteBuffer`. - /// This call should move the `writerIndex` forwards. - /// - /// - Important: This uses `little` endianness as the default preferred endianness. - /// - /// - Parameter byteBuffer: The ByteBuffer to write into. - @_disfavoredOverload - public func encode(to byteBuffer: inout ByteBuffer) { - self.encode(to: &byteBuffer, preferredEndianness: .little) - } -} - - -extension ByteDecodable { - /// Decode the type from `Data`. - /// - /// Initialize a new instance using the byte representation provided. - /// - /// - Note: Returns nil if no valid byte representation could be found. - /// - Parameters: - /// - data: The data to decode. - /// - endianness: The preferred endianness to use for decoding if applicable. - /// This might not apply to certain data structures that operate on single byte level. - public init?(data: Data, preferredEndianness endianness: Endianness = .little) { - var buffer = ByteBuffer(data: data) - self.init(from: &buffer, preferredEndianness: endianness) - } -} - - -extension ByteEncodable { - /// Encode to data. - /// - /// Encode the byte representation of this type. - /// - /// - Parameter endianness: The preferred endianness to use for encoding if applicable. - /// This might not apply to certain data structures that operate on single byte level. - /// - Returns: The encoded data. - public func encode(preferredEndianness endianness: Endianness = .little) -> Data { - var buffer = ByteBuffer() - encode(to: &buffer, preferredEndianness: endianness) - return buffer.getData(at: 0, length: buffer.readableBytes) ?? Data() - } -} diff --git a/Sources/ByteCoding/ByteCoding.docc/ByteCoding.md b/Sources/ByteCoding/ByteCoding.docc/ByteCoding.md deleted file mode 100644 index 2ebf36a..0000000 --- a/Sources/ByteCoding/ByteCoding.docc/ByteCoding.md +++ /dev/null @@ -1,36 +0,0 @@ -# ``ByteCoding`` - - - -Encode and decode types from and to their byte representation. - -## Overview - -This library provides a standardized approach of encoding and decoding types from and to their byte representation. - -## Topics - -### Coding - -- ``ByteCodable`` -- ``ByteEncodable`` -- ``ByteDecodable`` - -### 24 Bit Integer Support - -- ``NIOCore/ByteBuffer/getUInt24(at:endianness:)`` -- ``NIOCore/ByteBuffer/readUInt24(endianness:)`` -- ``NIOCore/ByteBuffer/setUInt24(_:at:endianness:)`` -- ``NIOCore/ByteBuffer/writeUInt24(_:endianness:)`` -- ``NIOCore/ByteBuffer/getInt24(at:endianness:)`` -- ``NIOCore/ByteBuffer/readInt24(endianness:)`` -- ``NIOCore/ByteBuffer/setInt24(_:at:endianness:)`` -- ``NIOCore/ByteBuffer/writeInt24(_:endianness:)`` diff --git a/Sources/ByteCoding/Data+ByteCodable.swift b/Sources/ByteCoding/Data+ByteCodable.swift deleted file mode 100644 index 22bd896..0000000 --- a/Sources/ByteCoding/Data+ByteCodable.swift +++ /dev/null @@ -1,38 +0,0 @@ -// -// This source file is part of the Stanford Spezi open-source project -// -// SPDX-FileCopyrightText: 2023 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import Foundation -import NIO - - -extension Data: ByteCodable { - /// Decode a data blob. - /// - /// Copies all bytes from the ByteBuffer into a `Data` instance. - /// - Parameters: - /// - byteBuffer: The ByteBuffer to decode from. - /// - endianness: The preferred endianness to use for decoding if applicable. - /// This is unused with the Data implementation. - public init?(from byteBuffer: inout ByteBuffer, preferredEndianness endianness: Endianness) { - guard let data = byteBuffer.readData(length: byteBuffer.readableBytes) else { - return nil - } - self = data - } - - /// Encode a data blob. - /// - /// Copies the data instance into the ByteBuffer. - /// - Parameters: - /// - byteBuffer: The ByteBuffer to write to. - /// - endianness: The preferred endianness to use for encoding if applicable. - /// This is unused with the Data implementation. - public func encode(to byteBuffer: inout ByteBuffer, preferredEndianness endianness: Endianness) { - byteBuffer.writeData(self) - } -} diff --git a/Sources/ByteCoding/FixedWithInteger+ByteCodable.swift b/Sources/ByteCoding/FixedWithInteger+ByteCodable.swift deleted file mode 100644 index d75e1dc..0000000 --- a/Sources/ByteCoding/FixedWithInteger+ByteCodable.swift +++ /dev/null @@ -1,51 +0,0 @@ -// -// This source file is part of the Stanford Spezi open-source project -// -// SPDX-FileCopyrightText: 2024 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import NIO - - -/// `ByteCodable` types that are a `FixedWithInteger`. -protocol FixedWidthByteCodable: FixedWidthInteger, ByteCodable {} - - -extension FixedWidthByteCodable { - /// Decodes a fixed-width integer from its byte representation. - /// - /// Decodes a `FixedWidthInteger` from a `ByteBuffer`. - /// - Parameters: - /// - byteBuffer: The ByteBuffer to decode from. - /// - endianness: The endianness to use for decoding. - public init?(from byteBuffer: inout ByteBuffer, preferredEndianness endianness: Endianness) { - guard let value = byteBuffer.readInteger(endianness: endianness, as: Self.self) else { - return nil - } - self = value - } - - /// Encodes a fixed-width integer to its byte representation. - /// - /// Encodes a `FixedWidthInteger` into a `ByteBuffer`. - /// - Parameters: - /// - byteBuffer: The ByteBuffer to write to. - /// - endianness: The endianness to use for encoding. - public func encode(to byteBuffer: inout ByteBuffer, preferredEndianness endianness: Endianness) { - byteBuffer.writeInteger(self, endianness: endianness) - } -} - - -extension UInt8: FixedWidthByteCodable {} -extension UInt16: FixedWidthByteCodable {} -extension UInt32: FixedWidthByteCodable {} -extension UInt64: FixedWidthByteCodable {} - - -extension Int8: FixedWidthByteCodable {} -extension Int16: FixedWidthByteCodable {} -extension Int32: FixedWidthByteCodable {} -extension Int64: FixedWidthByteCodable {} diff --git a/Sources/ByteCoding/Float+ByteCodable.swift b/Sources/ByteCoding/Float+ByteCodable.swift deleted file mode 100644 index a6be374..0000000 --- a/Sources/ByteCoding/Float+ByteCodable.swift +++ /dev/null @@ -1,64 +0,0 @@ -// -// This source file is part of the Stanford Spezi open-source project -// -// SPDX-FileCopyrightText: 2024 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import Foundation -import NIO - - -extension Float32: ByteCodable { - /// Decodes a float from its byte representation. - /// - /// Decodes a `Float32` from a `ByteBuffer`. - /// - Parameters: - /// - byteBuffer: The ByteBuffer to decode from. - /// - endianness: The endianness to use for decoding. - public init?(from byteBuffer: inout ByteBuffer, preferredEndianness endianness: Endianness) { - guard let bitPattern = UInt32(from: &byteBuffer, preferredEndianness: endianness) else { - return nil - } - - self.init(bitPattern: bitPattern) - } - - /// Encodes a float to its byte representation. - /// - /// Encodes a `Float32` into a `ByteBuffer`. - /// - Parameters: - /// - byteBuffer: The ByteBuffer to write to. - /// - endianness: The endianness to use for encoding. - public func encode(to byteBuffer: inout ByteBuffer, preferredEndianness endianness: Endianness) { - bitPattern.encode(to: &byteBuffer, preferredEndianness: endianness) - } -} - - -extension Float64: ByteCodable { - /// Decodes a float from its byte representation. - /// - /// Decodes a `Float64` from a `ByteBuffer`. - /// - Parameters: - /// - byteBuffer: The ByteBuffer to decode from. - /// - endianness: The endianness to use for decoding. - public init?(from byteBuffer: inout ByteBuffer, preferredEndianness endianness: Endianness) { - guard let bitPattern = UInt64(from: &byteBuffer, preferredEndianness: endianness) else { - return nil - } - - self.init(bitPattern: bitPattern) - } - - /// Encodes a float to its byte representation. - /// - /// Encodes a `Float64` into a `ByteBuffer`. - /// - Parameters: - /// - byteBuffer: The ByteBuffer to write to. - /// - endianness: The endianness to use for encoding. - public func encode(to byteBuffer: inout ByteBuffer, preferredEndianness endianness: Endianness) { - bitPattern.encode(to: &byteBuffer, preferredEndianness: endianness) - } -} diff --git a/Sources/ByteCoding/String+ByteCodable.swift b/Sources/ByteCoding/String+ByteCodable.swift deleted file mode 100644 index 3348962..0000000 --- a/Sources/ByteCoding/String+ByteCodable.swift +++ /dev/null @@ -1,42 +0,0 @@ -// -// This source file is part of the Stanford Spezi open-source project -// -// SPDX-FileCopyrightText: 2024 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import NIO - - -extension String: ByteCodable { - /// Decodes an utf8 string from its byte representation. - /// - /// Decodes an utf8 string from a `ByteBuffer`. - /// - /// - Note: This implementation assumes that all bytes in the ByteBuffer are representing - /// the string. - /// - Parameters - /// - byteBuffer: The bytebuffer to decode from. - /// - endianness: The preferred endianness to use for decoding if applicable. - /// This is unused with the String implementation. - public init?(from byteBuffer: inout ByteBuffer, preferredEndianness endianness: Endianness) { - guard let string = byteBuffer.readString(length: byteBuffer.readableBytes) else { - return nil - } - - self = string - } - - /// Encodes an utf8 string to its byte representation. - /// - /// Encodes an utf8 string into a `ByteBuffer`. - /// - /// - Parameters - /// - byteBuffer: The bytebuffer to write to. - /// - endianness: The preferred endianness to use for decoding if applicable. - /// This is unused with the String implementation. - public func encode(to byteBuffer: inout ByteBuffer, preferredEndianness endianness: Endianness) { - byteBuffer.writeString(self) - } -} diff --git a/Sources/ByteCoding/TestingSupport/Data+HexString.swift b/Sources/ByteCoding/TestingSupport/Data+HexString.swift deleted file mode 100644 index 5798667..0000000 --- a/Sources/ByteCoding/TestingSupport/Data+HexString.swift +++ /dev/null @@ -1,70 +0,0 @@ -// -// This source file is part of the Stanford Spezi open-source project -// -// SPDX-FileCopyrightText: 2024 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import Foundation - - -extension Data { - /// Create `Data` from a hex string. - /// - /// The hex string may be prefixed with `"0x"` or `"0X"`. - /// - Parameter hex: The hex string. - @_spi(TestingSupport) - public init?(hex: String) { - // while this seems complicated, and you can do it with shorter code, - // this doesn't incur any heap allocations for string. Pretty neat. - - var index = hex.startIndex - - let hexCount: Int - - if hex.hasPrefix("0x") || hex.hasPrefix("0X") { - index = hex.index(index, offsetBy: 2) - hexCount = hex.count - 2 - } else { - hexCount = hex.count - } - - var bytes: [UInt8] = [] - bytes.reserveCapacity(hexCount / 2 + hexCount % 2) - - if !hexCount.isMultiple(of: 2) { - guard let byte = UInt8(String(hex[index]), radix: 16) else { - return nil - } - bytes.append(byte) - - index = hex.index(after: index) - } - - - while index < hex.endIndex { - guard let byte = UInt8(hex[index ... hex.index(after: index)], radix: 16) else { - return nil - } - bytes.append(byte) - - index = hex.index(index, offsetBy: 2) - } - - guard hexCount / bytes.count == 2 else { - return nil - } - self.init(bytes) - } - - - /// Create hex string from Data. - /// - Returns: The hex formatted data string - @_spi(TestingSupport) - public func hexString() -> String { - map { character in - String(format: "%02hhx", character) - }.joined() - } -} diff --git a/Sources/ByteCoding/Utils/ByteBuffer+Int24.swift b/Sources/ByteCoding/Utils/ByteBuffer+Int24.swift deleted file mode 100644 index 40b97d0..0000000 --- a/Sources/ByteCoding/Utils/ByteBuffer+Int24.swift +++ /dev/null @@ -1,183 +0,0 @@ -// -// This source file is part of the Stanford Spezi open-source project -// -// SPDX-FileCopyrightText: 2024 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import Foundation -import NIOCore - - -extension ByteBuffer { - /// Get 24-bit signed integer at a given index without moving the reader index. - /// - /// - Parameters: - /// - index: The starting index of the integer in the `ByteBuffer`. - /// - endianness: The endianness of the integer in the `ByteBuffer`. - /// - Returns: A 24-bit signed integer put into a `Int32` type or `nil` if the bytes are not readable. - @inlinable - public func getInt24(at index: Int, endianness: Endianness = .big) -> Int32? { - guard var bitPattern = getUInt24(at: index, endianness: endianness) else { - return nil - } - - // what this method is doing here, is translating the 24-bit two's complement into a 32-bit two's complement. - - // if its larger than the largest positive number, we want to make sure that all upper 8 bits are flipped to one. - if bitPattern > 0x7FFFFF { // (2 ^ 23) - 1 - bitPattern |= 0xFF000000 - } - - // I love Swift for that! We can just reinterpret the 32UInt bit pattern into a Int32! - return Int32(bitPattern: bitPattern) - } - - /// Read 24-bit signed integer and move the reader index forward accordingly. - /// - /// - Parameter endianness: The endianness of the integer in the `ByteBuffer`. - /// - Returns: A 24-bit signed integer put into a `Int32` type or `nil` if the bytes are not readable. - @inlinable - public mutating func readInt24(endianness: Endianness = .big) -> Int32? { - guard let value = getInt24(at: self.readerIndex, endianness: endianness) else { - return nil - } - self.moveReaderIndex(forwardBy: 3) - return value - } -} - - -// see https://github.com/apple/swift-nio-extras/pull/114 -extension ByteBuffer { - /// Get 24-bit unsigned integer at a given index without moving the reader index. - /// - /// - Parameters: - /// - index: The starting index of the integer in the `ByteBuffer`. - /// - endianness: The endianness of the integer in the `ByteBuffer`. - /// - Returns: A 24-bit unsigned integer put into a `UInt32` type or `nil` if the bytes are not readable. - @inlinable - public func getUInt24(at index: Int, endianness: Endianness = .big) -> UInt32? { - let mostSignificant: UInt16 - let leastSignificant: UInt8 - switch endianness { - case .big: - guard let uint16 = self.getInteger(at: index, endianness: .big, as: UInt16.self), - let uint8 = self.getInteger(at: index + 2, endianness: .big, as: UInt8.self) else { return nil } - mostSignificant = uint16 - leastSignificant = uint8 - case .little: - guard let uint8 = self.getInteger(at: index, endianness: .little, as: UInt8.self), - let uint16 = self.getInteger(at: index + 1, endianness: .little, as: UInt16.self) else { return nil } - mostSignificant = uint16 - leastSignificant = uint8 - } - return (UInt32(mostSignificant) << 8) &+ UInt32(leastSignificant) - } - - /// Read 24-bit unsigned integer and move the reader index forward accordingly. - /// - /// - Parameter endianness: The endianness of the integer in the `ByteBuffer`. - /// - Returns: A 24-bit unsigned integer put into a `UInt32` type or `nil` if the bytes are not readable. - @inlinable - public mutating func readUInt24(endianness: Endianness = .big) -> UInt32? { - guard let integer = getUInt24(at: self.readerIndex, endianness: endianness) else { - return nil - } - self.moveReaderIndex(forwardBy: 3) - return integer - } -} - - -extension ByteBuffer { - /// The maximum 24-bit integer in a `Int32` type. - @usableFromInline static let maxInt24 = Int32(bitPattern: 0x007FFFFF) - /// The minimum 24-bit integer in a `Int32` type. - @usableFromInline static let minInt24 = Int32(bitPattern: 0xFF800000) - - /// Set 24-bit signed integer at a given index without moving the writer index. - /// - /// Note that while the method accepts an `Int32`, the value range is restricted to an `Int24`. - /// Passing a larger value will result in a runtime crash. - /// - /// - Parameters: - /// - integer: The integer to write. - /// - index: The index of the first byte to write. - /// - endianness: The endianness to use for writing. - /// - Returns: The number of bytes written. - @inlinable - @discardableResult - public mutating func setInt24(_ integer: Int32, at index: Int, endianness: Endianness = .big) -> Int { - precondition(integer <= Self.maxInt24 && integer >= Self.minInt24, "integer value does not fit into 24 bit integer") - - var bitPattern = UInt32(bitPattern: integer) - - // we verified above that this integer fits into the range, so now just set to most significant byte to zero (2s complement representation of Int32) - bitPattern &= 0xFF_FF_FF - - return self.setUInt24(bitPattern, at: index, endianness: endianness) - } - - /// Write 24-bit signed integer and move the writer index forward accordingly. - /// - /// Note that while the method accepts an `Int32`, the value range is restricted to an `Int24`. - /// Passing a larger value will result in a runtime crash. - /// - /// - Parameters: - /// - integer: The integer to write. - /// - endianness: The endianness to use for writing. - /// - Returns: The number of bytes written. - @inlinable - @discardableResult - public mutating func writeInt24(_ integer: Int32, endianness: Endianness = .big) -> Int { - let bytesWritten = setInt24(integer, at: self.writerIndex, endianness: endianness) - self.moveWriterIndex(forwardBy: 3) - return bytesWritten - } -} - - -extension ByteBuffer { - /// Set 24-bit unsigned integer at a given index without moving the writer index. - /// - /// Note that while the method accepts an `UInt32`, the value range is restricted to an `UInt24`. - /// Passing a larger value will result in a runtime crash. - /// - /// - Parameters: - /// - integer: The integer to write. - /// - index: The index of the first byte to write. - /// - endianness: The endianness to use for writing. - /// - Returns: The number of bytes written. - @inlinable - @discardableResult - public mutating func setUInt24(_ integer: UInt32, at index: Int, endianness: Endianness = .big) -> Int { - precondition(integer & 0xFF_FF_FF == integer, "integer value does not fit into 24 bit un-singed integer") - switch endianness { - case .little: - return setInteger(UInt8(integer & 0xFF), at: index, endianness: .little) - + setInteger(UInt16((integer >> 8) & 0xFF_FF), at: index + 1, endianness: .little) - case .big: - return setInteger(UInt16((integer >> 8) & 0xFF_FF), at: index, endianness: .big) + - setInteger(UInt8(integer & 0xFF), at: index + 2, endianness: .big) - } - } - - /// Write 24-bit unsigned integer and move the writer index forward accordingly. - /// - /// Note that while the method accepts an `UInt32`, the value range is restricted to an `UInt24`. - /// Passing a larger value will result in a runtime crash. - /// - /// - Parameters: - /// - integer: The integer to write. - /// - endianness: The endianness to use for writing. - /// - Returns: The number of bytes written. - @inlinable - @discardableResult - public mutating func writeUInt24(_ integer: UInt32, endianness: Endianness = .big) -> Int { - let bytesWritten = setUInt24(integer, at: self.writerIndex, endianness: endianness) - self.moveWriterIndex(forwardBy: 3) - return bytesWritten - } -} diff --git a/Sources/XCTByteCoding/TestIdentity.swift b/Sources/XCTByteCoding/TestIdentity.swift deleted file mode 100644 index f3e8f86..0000000 --- a/Sources/XCTByteCoding/TestIdentity.swift +++ /dev/null @@ -1,53 +0,0 @@ -// -// This source file is part of the Stanford Spezi open-source project -// -// SPDX-FileCopyrightText: 2024 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -import ByteCoding -import Foundation -import NIO -import XCTest - - -/// Tests the identity invariant of a `ByteCodable` implementation. -/// -/// This function encodes a provided value into its byte representation, then -/// decodes it back into the value and asserts its equality using `XCTAssertEqual`. -/// -/// - Parameter value: The value to encode and decode. -/// - Throws: Failed to decode. -public func testIdentity(from value: T) throws { - let data = value.encode() - - var decodingBuffer = ByteBuffer(data: data) - - let instance: T = try XCTUnwrap(T(from: &decodingBuffer)) - - XCTAssertEqual(instance, value) -} - - -/// Tests the identity invariant of a `ByteCodable` implementation. -/// -/// This function decodes the type from the provided byte representation, then -/// encodes it back into its byte representations and asserts its equality using `XCTAssertEqual`. -/// - Parameters: -/// - type: The type to test. -/// - data: The data representation to decode. -/// - Throws: Failed to decode. -public func testIdentity(of type: T.Type, from data: Data) throws { - var decodingBuffer = ByteBuffer(data: data) - - let instance: T = try XCTUnwrap(T(from: &decodingBuffer)) - - var encodingBuffer = ByteBuffer() - encodingBuffer.reserveCapacity(data.count) - - instance.encode(to: &encodingBuffer) - - let encodingData = Data(buffer: encodingBuffer) - XCTAssertEqual(encodingData, data) -} diff --git a/Sources/XCTByteCoding/XCTByteCoding.docc/XCTByteCoding.md b/Sources/XCTByteCoding/XCTByteCoding.docc/XCTByteCoding.md deleted file mode 100644 index e90bf1f..0000000 --- a/Sources/XCTByteCoding/XCTByteCoding.docc/XCTByteCoding.md +++ /dev/null @@ -1,24 +0,0 @@ -# ``XCTByteCoding`` - -Unit testing utilities for Byte Coding. - - - -## Overview - -This package provides several utilities that make your life easier when testing your `ByteCodable` implementation. - -## Topics - -### Testing Byte Codable - -- ``testIdentity(from:)`` -- ``testIdentity(of:from:)`` diff --git a/Tests/ByteCodingTests/ByteCodableTests.swift b/Tests/ByteCodingTests/ByteCodableTests.swift deleted file mode 100644 index cc84b13..0000000 --- a/Tests/ByteCodingTests/ByteCodableTests.swift +++ /dev/null @@ -1,164 +0,0 @@ -// -// This source file is part of the Stanford Spezi open source project -// -// SPDX-FileCopyrightText: 2024 Stanford University and the project authors (see CONTRIBUTORS.md) -// -// SPDX-License-Identifier: MIT -// - -@_spi(TestingSupport) @testable import ByteCoding -import NIO -import XCTByteCoding -import XCTest - - -final class ByteCodableTests: XCTestCase { - func testData() throws { - let data = try XCTUnwrap(Data(hex: "0xAABBCCDDEE")) - - try testIdentity(of: Data.self, from: data) - - let data0 = Data(data: data) - XCTAssertEqual(data0, data) - } - - func testBoolean() throws { - let trueData = try XCTUnwrap(Data(hex: "0x01")) - try testIdentity(of: Bool.self, from: trueData) - - let falseData = try XCTUnwrap(Data(hex: "0x00")) - try testIdentity(of: Bool.self, from: falseData) - - var empty = ByteBuffer() - XCTAssertNil(Bool(from: &empty)) - } - - func testString() throws { - let data = try XCTUnwrap("Hello World".data(using: .utf8)) - try testIdentity(of: String.self, from: data) - - var empty = ByteBuffer() - XCTAssertEqual(String(from: &empty), "") - } - - func testInt8() throws { - try testIdentity(from: Int8.max) - try testIdentity(from: Int8.min) - } - - func testInt16() throws { - try testIdentity(from: Int16.max) - try testIdentity(from: Int16.min) - } - - func testInt32() throws { - try testIdentity(from: Int32.max) - try testIdentity(from: Int32.min) - } - - func testInt64() throws { - try testIdentity(from: Int64.max) - try testIdentity(from: Int64.min) - } - - func testUInt8() throws { - try testIdentity(from: UInt8.max) - try testIdentity(from: UInt8.min) - - var empty = ByteBuffer() - XCTAssertNil(UInt8(from: &empty)) - } - - func testUInt16() throws { - try testIdentity(from: UInt16.max) - try testIdentity(from: UInt16.min) - } - - func testUInt32() throws { - try testIdentity(from: UInt32.max) - try testIdentity(from: UInt32.min) - } - - func testUInt64() throws { - try testIdentity(from: UInt64.max) - try testIdentity(from: UInt64.min) - } - - func testFloat32() throws { - try testIdentity(from: Float32.pi) - try testIdentity(from: Float32.infinity) - try testIdentity(from: Float32(17.2783912)) - } - - func testFloat64() throws { - try testIdentity(from: Float64.pi) - try testIdentity(from: Float64.infinity) - try testIdentity(from: Float64(23712.2123123)) - } - - func testReadInt24Big() throws { - let data = try XCTUnwrap(Data(hex: "0xFF0000")) - var buffer = ByteBuffer(data: data) - - let uint = try XCTUnwrap(buffer.getUInt24(at: 0, endianness: .big)) - let int = try XCTUnwrap(buffer.readInt24(endianness: .big)) - - XCTAssertEqual(uint, 0xFF0000) - XCTAssertEqual(int, -65536) - } - - func testReadInt24Little() throws { - let data = try XCTUnwrap(Data(hex: "0x0000FF")) - var buffer = ByteBuffer(data: data) - - let uint = try XCTUnwrap(buffer.readUInt24(endianness: .little)) - buffer.moveReaderIndex(to: 0) - let int = try XCTUnwrap(buffer.readInt24(endianness: .little)) - - XCTAssertEqual(uint, 0xFF0000) - XCTAssertEqual(int, -65536) - } - - func testReadInt24Reading() throws { - let data = try XCTUnwrap(Data(hex: "0x6fffff")) - let buffer = ByteBuffer(data: data) - - let intBE = try XCTUnwrap(buffer.getInt24(at: 0, endianness: .big)) - let intLE = try XCTUnwrap(buffer.getInt24(at: 0, endianness: .little)) - - XCTAssertEqual(intBE, 7340031) - XCTAssertEqual(intLE, -145) - } - - func testInt24WriteLE() { - var buffer = ByteBuffer() - - buffer.writeInt24(-65536, endianness: .little) - buffer.writeInt24(-8_388_608, endianness: .little) - buffer.writeInt24(7340031, endianness: .little) - - let data = buffer.getData(at: 0, length: buffer.readableBytes) - XCTAssertEqual(data?.hexString().uppercased(), "0000FF" + "000080" + "FFFF6F") - } - - func testInt24WriteBE() throws { - var buffer = ByteBuffer() - - buffer.writeInt24(-65536, endianness: .big) - buffer.writeInt24(-8_388_608, endianness: .big) - buffer.writeInt24(7340031, endianness: .big) - - let data = try XCTUnwrap(buffer.getData(at: 0, length: buffer.readableBytes)) - XCTAssertEqual(data.hexString().uppercased(), "FF0000" + "800000" + "6FFFFF") - } - - func testUint24Write() throws { - var buffer = ByteBuffer() - - buffer.writeUInt24(256, endianness: .big) - buffer.writeUInt24(512, endianness: .little) - - let data = try XCTUnwrap(buffer.getData(at: 0, length: buffer.readableBytes)) - XCTAssertEqual(data.hexString().uppercased(), "000100" + "000200") - } -}