Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove dependency on Half only where obsolete #27

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
2 changes: 1 addition & 1 deletion CBORCoding.podspec
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Pod::Spec.new do |s|

s.name = "CBORCoding"
s.version = "1.3.2"
s.version = "1.4.0"
s.summary = "A CBOR Encoder and Decoder"
s.description = <<-DESC
A lightweight framework containing a coder pair for encoding and decoding `Codable` conforming types to and from CBOR document format for iOS, macOS, tvOS, and watchOS.
Expand Down
4 changes: 2 additions & 2 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
"repositoryURL": "https://github.com/SomeRandomiOSDev/Half",
"state": {
"branch": null,
"revision": "8c80711c6ff57caa51e73e3b97a841c414af06cf",
"version": "1.3.1"
"revision": "9f95ddc774eefae46d92492ed8d49457dcf031f4",
"version": "1.3.2"
}
}
]
Expand Down
36 changes: 27 additions & 9 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,27 +1,45 @@
// swift-tools-version:5.5
import PackageDescription

#if arch(x86_64)
let macOS = SupportedPlatform.macOS(.v10_10)
let macCatalyst = SupportedPlatform.macCatalyst(.v13)
#else
let macOS = SupportedPlatform.macOS(.v11)
let macCatalyst = SupportedPlatform.macCatalyst(.v14)
#endif

#if (os(macOS) || targetEnvironment(macCatalyst)) && arch(x86_64)
// We still need Half
let halfTarget: [Target.Dependency] = ["Half"]
#else
let halfTarget: [Target.Dependency] = []
#endif

let halfPackage: [Package.Dependency] = [
.package(url: "https://github.com/SomeRandomiOSDev/Half", from: "1.3.1")
]

let package = Package(
name: "CBORCoding",

platforms: [
.iOS("9.0"),
.macOS("10.10"),
.tvOS("9.0"),
.watchOS("2.0")
.iOS(.v14),
macOS,
.tvOS(.v14),
.watchOS(.v7),
macCatalyst
],

products: [
.library(name: "CBORCoding", targets: ["CBORCoding"])
],

dependencies: [
.package(url: "https://github.com/SomeRandomiOSDev/Half", from: "1.3.1")
],
dependencies: halfPackage,

targets: [
.target(name: "CBORCoding", dependencies: ["Half"]),
.testTarget(name: "CBORCodingTests", dependencies: ["CBORCoding", "Half"])
.target(name: "CBORCoding", dependencies: halfTarget),
.testTarget(name: "CBORCodingTests", dependencies: ["CBORCoding"] + halfTarget)
],

swiftLanguageVersions: [.version("4.2"), .version("5")]
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ let decoder = CBORDecoder()
let stinger = try decoder.decode(Car.self, from: encodedData)
```

## 16-bit Floating Point Support

This package supports encoding/decoding Swift's `Float16` where available. Unfortunately that does not include macOS on Intel processors, where an almost-identical type [`Half`](https://github.com/SomeRandomiOSDev/Half) is supported instead.

## CBOR

**Concise Binary Object Representation** is a data format for being able to encode formatted data with a goal of a having as small a message size as possible.
Expand Down
3 changes: 2 additions & 1 deletion Sources/CBORCoding/CBOR.swift
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,8 @@ public enum CBOR {

/// The string composed by joining together all of the data chunks and interpreting
/// it as a UTF8 encoded string.
@inline(__always) public var stringValue: String? {
@inline(__always)
public var stringValue: String? {
return stringValue(as: .utf8)
}

Expand Down
110 changes: 47 additions & 63 deletions Sources/CBORCoding/CBORDecoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
//

import Foundation
import Half

#if canImport(Combine)
import Combine
Expand Down Expand Up @@ -286,34 +285,34 @@ internal class __CBORDecoder: Decoder, SingleValueDecodingContainer {
}

fileprivate func decode<T: Decodable>(_ value: Any, as type: T.Type) throws -> T {
let result: T

// swiftlint:disable force_cast
if type == Data.self, let value = value as? CBORDecodedData {
result = value.decodedDataValue() as! T
} else if type == String.self, let value = value as? CBORDecodedString {
result = try value.decodedStringValue() as! T
} else if type == CBOR.NegativeUInt64.self {
result = try decode(value, as: CBOR.NegativeUInt64.self) as! T
} else if type == CBOR.Undefined.self {
return value.decodedDataValue() as! T
}
if type == String.self, let value = value as? CBORDecodedString {
return try value.decodedStringValue() as! T
}
if type == CBOR.NegativeUInt64.self {
return try decode(value, as: CBOR.NegativeUInt64.self) as! T
}
if type == CBOR.Undefined.self {
guard let decodedValue = value as? T else {
throw DecodingError._typeMismatch(at: codingPath, expectation: type, reality: value)
}

result = decodedValue
} else if let decodedValue = value as? T {
result = decodedValue
} else if type == Half.self {
result = try decodeFloatingPoint(value, as: Half.self) as! T
} else {
storage.push(container: value)
defer { storage.popContainer() }

result = try type.init(from: self)
return decodedValue
}
if let decodedValue = value as? T {
return decodedValue
}
if type == Float16.self {
return try decodeFloatingPoint(value, as: Float16.self) as! T
}
// swiftlint:enable force_cast

return result
storage.push(container: value)
defer { storage.popContainer() }
// swiftlint:enable force_cast
return try type.init(from: self)
}

//
Expand Down Expand Up @@ -378,54 +377,39 @@ internal class __CBORDecoder: Decoder, SingleValueDecodingContainer {
}

fileprivate func decodeFloatingPoint<T>(_ value: Any, as type: T.Type) throws -> T where T: BinaryFloatingPoint {
let floatingPoint: T
if let half = value as? Half {
if half.isNaN {
if half.isSignalingNaN {
floatingPoint = .signalingNaN
} else {
floatingPoint = .nan
}
} else {
guard let value = T(exactly: half) else {
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: codingPath, debugDescription: "Decoded CBOR number <\(half)> does not fit in \(type)."))
}

floatingPoint = value
if let half = value as? Float16 {
guard !half.isNaN else {
return half.isSignalingNaN ? .signalingNaN : .nan
}
} else if let float = value as? Float {
if float.isNaN {
if float.isSignalingNaN {
floatingPoint = .signalingNaN
} else {
floatingPoint = .nan
}
} else {
guard let value = T(exactly: float) else {
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: codingPath, debugDescription: "Decoded CBOR number <\(float)> does not fit in \(type)."))
}

floatingPoint = value
guard let value = T(exactly: half) else {
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: codingPath, debugDescription: "Decoded CBOR number <\(half)> does not fit in \(type)."))
}
} else if let double = value as? Double {
if double.isNaN {
if double.isSignalingNaN {
floatingPoint = .signalingNaN
} else {
floatingPoint = .nan
}
} else {
guard let value = T(exactly: double) else {
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: codingPath, debugDescription: "Decoded CBOR number <\(double)> does not fit in \(type)."))
}

floatingPoint = value
return value
}

if let float = value as? Float {
guard !float.isNaN else {
return float.isSignalingNaN ? .signalingNaN : .nan
}
} else {
throw DecodingError._typeMismatch(at: codingPath, expectation: type, reality: value)
guard let value = T(exactly: float) else {
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: codingPath, debugDescription: "Decoded CBOR number <\(float)> does not fit in \(type)."))
}

return value
}
if let double = value as? Double {
guard !double.isNaN else {
return double.isSignalingNaN ? .signalingNaN : .nan
}
guard let value = T(exactly: double) else {
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: codingPath, debugDescription: "Decoded CBOR number <\(double)> does not fit in \(type)."))
}

return floatingPoint
return value

}
throw DecodingError._typeMismatch(at: codingPath, expectation: type, reality: value)
}

fileprivate func decode(_ value: Any, as type: CBOR.NegativeUInt64.Type) throws -> CBOR.NegativeUInt64 {
Expand Down
100 changes: 52 additions & 48 deletions Sources/CBORCoding/CBOREncoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
//

import Foundation
import Half

#if canImport(Combine)
import Combine
Expand Down Expand Up @@ -227,7 +226,7 @@ open class CBOREncoder {
return Data(bytes)
}

internal static func encode(_ value: Half) -> Data {
internal static func encode(_ value: Float16) -> Data {
var half = value
let data = Data(bytes: &half, count: MemoryLayout.size(ofValue: half))

Expand Down Expand Up @@ -712,60 +711,65 @@ internal class __CBOREncoder: CBOREncoderProtocol, SingleValueEncodingContainer
}

fileprivate func encodeValueToCBOR(_ value: Encodable) throws -> Any? {
let result: Any?

if let encoded = value as? CBOR.CBOREncoded {
result = encoded.encodedData
} else if let date = value as? Date {
result = CBOREncoder.encode(date, using: options.dateEncodingStrategy)
} else if let data = value as? Data {
result = CBOREncoder.encode(data)
} else if let url = value as? URL {
return encoded.encodedData
}
if let date = value as? Date {
return CBOREncoder.encode(date, using: options.dateEncodingStrategy)
}
if let data = value as? Data {
return CBOREncoder.encode(data)
}
if let url = value as? URL {
// swiftlint:disable force_try
result = try! CBOREncoder.encode(url, forTag: .uri).encodedData
return try! CBOREncoder.encode(url, forTag: .uri).encodedData
// swiftlint:enable force_try
} else if value is NSNull || value is CBOR.Null {
result = CBOREncoder.encodeNil()
} else if value is CBOR.Undefined {
result = CBOREncoder.encodeUndefined()
} else if let dict = value as? [String: Encodable] {
result = try encode(dict)
} else if let dict = value as? [Int: Encodable] {
result = try encode(dict)
} else if let data = value as? CBOR.IndefiniteLengthData {
result = try encode(data)
} else if let string = value as? CBOR.IndefiniteLengthString {
result = try encode(string)
} else if let value = value as? CBOR.NegativeUInt64 {
result = CBOREncoder.encode(value)
} else if let value = value as? Half {
result = CBOREncoder.encode(value)
} else {
let action: () throws -> Any? = {
let depth = self.storage.count

do {
try value.encode(to: self)
} catch {
if self.storage.count > depth {
_ = self.storage.popContainer()
}

throw error
}
if value is NSNull || value is CBOR.Null {
return CBOREncoder.encodeNil()
}
if value is CBOR.Undefined {
return CBOREncoder.encodeUndefined()
}
if let dict = value as? [String: Encodable] {
return try encode(dict)
}
if let dict = value as? [Int: Encodable] {
return try encode(dict)
}
if let data = value as? CBOR.IndefiniteLengthData {
return try encode(data)
}
if let string = value as? CBOR.IndefiniteLengthString {
return try encode(string)
}
if let value = value as? CBOR.NegativeUInt64 {
return CBOREncoder.encode(value)
}
if let value = value as? Float16 {
return CBOREncoder.encode(value)
}
let action: () throws -> Any? = {
let depth = self.storage.count

do {
try value.encode(to: self)
} catch {
if self.storage.count > depth {
_ = self.storage.popContainer()
}

guard self.storage.count > depth else { return nil }
return self.storage.popContainer()
throw error
}

if newContainerLength.contains(.includeSubcontainers) {
result = try action()
} else {
result = try definiteLengthContainerContext(action)
}
guard self.storage.count > depth else { return nil }
return self.storage.popContainer()
}

return result
if newContainerLength.contains(.includeSubcontainers) {
return try action()
} else {
return try definiteLengthContainerContext(action)
}
}

// MARK: Encoder Protocol Requirements
Expand Down
Loading