diff --git a/.github/workflows/deploy_to_cocoapods.yaml b/.github/workflows/deploy_to_cocoapods.yaml index 8554517..3cec717 100644 --- a/.github/workflows/deploy_to_cocoapods.yaml +++ b/.github/workflows/deploy_to_cocoapods.yaml @@ -6,7 +6,7 @@ on: - '*' jobs: - build: + deploy: runs-on: macOS-latest steps: - uses: actions/checkout@v1 diff --git a/.gitignore b/.gitignore index 09a1e7c..8d0ffb4 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ /Packages /*.xcodeproj bin/protoc +.swiftpm/ diff --git a/.swift-version b/.swift-version deleted file mode 100644 index ef425ca..0000000 --- a/.swift-version +++ /dev/null @@ -1 +0,0 @@ -5.2 diff --git a/BinaryCodable.podspec b/BinaryCodable.podspec index 8b54996..5ba9357 100644 --- a/BinaryCodable.podspec +++ b/BinaryCodable.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'BinaryCodable' - s.version = '0.3.0' + s.version = '0.3.1' s.license = 'Apache 2.0' s.summary = 'Codable-like interfaces for binary representations.' s.homepage = 'https://github.com/jverkoey/BinaryCodable' @@ -10,6 +10,7 @@ Pod::Spec.new do |s| s.ios.deployment_target = '13.0' s.osx.deployment_target = '10.15' + s.swift_versions = ['5.2'] s.source_files = ['Sources/**/*.swift'] end diff --git a/CHANGELOG.md b/CHANGELOG.md index 411f9e6..f0d165f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 0.3.1 + +This patch release fixes a crashing bug introduced in 0.3.0 that occurred when decoding two-byte values from misaligned memory. + # 0.3.0 This minor release adds a `decodeRemainder` method to `BinaryDecodingContainer` and drops support for iOS 12, macOS 10.12-10.14, and Swift 4. diff --git a/Sources/BinaryCodable/BinaryDataCoders/BinaryDataDecoder.swift b/Sources/BinaryCodable/BinaryDataCoders/BinaryDataDecoder.swift index 2926054..c4e7d64 100644 --- a/Sources/BinaryCodable/BinaryDataCoders/BinaryDataDecoder.swift +++ b/Sources/BinaryCodable/BinaryDataCoders/BinaryDataDecoder.swift @@ -111,10 +111,13 @@ private class BinaryDataDecodingContainer: BinaryDecodingContainer { throw BinaryDecodingError.dataCorrupted(.init(debugDescription: "Not enough data to create a a type of \(type). Needed: \(byteWidth). Received: \(bytes.count).")) } - let value = bytes.withUnsafeBytes { ptr -> T in - return ptr.load(as: T.self) + return bytes.withUnsafeBytes { ptr in + var value: T = 0 + withUnsafeMutableBytes(of: &value) { valuePtr in + valuePtr.copyMemory(from: UnsafeRawBufferPointer(rebasing: ptr[0..(_ type: T.Type) throws -> T { @@ -124,10 +127,13 @@ private class BinaryDataDecodingContainer: BinaryDecodingContainer { throw BinaryDecodingError.dataCorrupted(.init(debugDescription: "Not enough data to create a a type of \(type). Needed: \(byteWidth). Received: \(bytes.count).")) } - let value = bytes.withUnsafeBytes { ptr -> T in - return ptr.load(as: T.self) + return bytes.withUnsafeBytes { ptr in + var value: T = 0 + withUnsafeMutableBytes(of: &value) { valuePtr in + valuePtr.copyMemory(from: UnsafeRawBufferPointer(rebasing: ptr[0.. String { diff --git a/Tests/BinaryCodableTests/MisalignedDecoderTests.swift b/Tests/BinaryCodableTests/MisalignedDecoderTests.swift new file mode 100644 index 0000000..e439c1b --- /dev/null +++ b/Tests/BinaryCodableTests/MisalignedDecoderTests.swift @@ -0,0 +1,61 @@ +// Copyright 2020-present the BinaryCodable authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import BinaryCodable +import XCTest + +private struct MisalignedUInt16Packet: BinaryDecodable { + let twoByteInt: UInt16 + init(from decoder: BinaryDecoder) throws { + var container = decoder.container(maxLength: nil) + _ = try container.decode(length: 1) + self.twoByteInt = try container.decode(UInt16.self) + } +} + +private struct MisalignedFloatPacket: BinaryDecodable { + let fourByteFloat: Float + init(from decoder: BinaryDecoder) throws { + var container = decoder.container(maxLength: nil) + _ = try container.decode(length: 1) + self.fourByteFloat = try container.decode(Float.self) + } +} + +final class MisalignedDecoderTests: XCTestCase { + + func testUInt16() throws { + // Given + let packetData: [UInt8] = [0, 3, 0] + let decoder = BinaryDataDecoder() + + // When + let packet = try decoder.decode(MisalignedUInt16Packet.self, from: packetData) + + // Then + XCTAssertEqual(packet.twoByteInt, 3) + } + + func testFloat() throws { + // Given + let packetData: [UInt8] = [0, 0, 0, 0x80, 0x3f] + let decoder = BinaryDataDecoder() + + // When + let packet = try decoder.decode(MisalignedFloatPacket.self, from: packetData) + + // Then + XCTAssertEqual(packet.fourByteFloat, 1.0, accuracy: 0.001) + } +}