-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2 from PureSwift/Codable
Added Codable support
- Loading branch information
Showing
18 changed files
with
2,316 additions
and
317 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
// swift-tools-version:4.1 | ||
import PackageDescription | ||
|
||
let package = Package(name: "TLVCoding", | ||
products: [ | ||
.library( | ||
name: "TLVCoding", | ||
targets: ["TLVCoding"] | ||
) | ||
], | ||
targets: [ | ||
.target(name: "TLVCoding", path: "./Sources"), | ||
.testTarget(name: "TLVCodingTests", dependencies: ["TLVCoding"]) | ||
], | ||
swiftLanguageVersions: [4]) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
// swift-tools-version:4.2 | ||
import PackageDescription | ||
|
||
let package = Package(name: "TLVCoding", | ||
products: [ | ||
.library( | ||
name: "TLVCoding", | ||
targets: ["TLVCoding"] | ||
) | ||
], | ||
targets: [ | ||
.target(name: "TLVCoding", path: "./Sources"), | ||
.testTarget(name: "TLVCodingTests", dependencies: ["TLVCoding"]) | ||
], | ||
swiftLanguageVersions: [.v4_2]) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
// | ||
// CodingKey.swift | ||
// TLVCoding | ||
// | ||
// Created by Alsey Coleman Miller on 5/12/19. | ||
// Copyright © 2019 PureSwift. All rights reserved. | ||
// | ||
|
||
public protocol TLVCodingKey: CodingKey { | ||
|
||
init?(code: TLVTypeCode) | ||
|
||
var code: TLVTypeCode { get } | ||
} | ||
|
||
public extension TLVCodingKey { | ||
|
||
init?(intValue: Int) { | ||
|
||
guard intValue <= Int(UInt8.max), | ||
intValue >= Int(UInt8.min) | ||
else { return nil } | ||
|
||
self.init(code: TLVTypeCode(rawValue: UInt8(intValue))) | ||
} | ||
|
||
var intValue: Int? { | ||
return Int(code.rawValue) | ||
} | ||
} | ||
|
||
public extension TLVCodingKey where Self: RawRepresentable, Self.RawValue == TLVTypeCode.RawValue { | ||
|
||
init?(code: TLVTypeCode) { | ||
self.init(rawValue: code.rawValue) | ||
} | ||
|
||
var code: TLVTypeCode { | ||
return TLVTypeCode(rawValue: rawValue) | ||
} | ||
} | ||
|
||
#if swift(>=4.2) | ||
public extension TLVCodingKey where Self: CaseIterable, Self: RawRepresentable, Self.RawValue == TLVTypeCode.RawValue { | ||
|
||
init?(stringValue: String) { | ||
|
||
guard let value = Self.allCases.first(where: { $0.stringValue == stringValue }) | ||
else { return nil } | ||
|
||
self = value | ||
} | ||
} | ||
#endif | ||
|
||
internal extension TLVTypeCode { | ||
|
||
init? <K: CodingKey> (codingKey: K) { | ||
|
||
if let tlvCodingKey = codingKey as? TLVCodingKey { | ||
|
||
self = tlvCodingKey.code | ||
|
||
} else if let intValue = codingKey.intValue { | ||
|
||
guard intValue <= Int(UInt8.max), | ||
intValue >= Int(UInt8.min) | ||
else { return nil } | ||
|
||
self.init(rawValue: UInt8(intValue)) | ||
|
||
} else if MemoryLayout<K>.size == MemoryLayout<UInt8>.size, | ||
Mirror(reflecting: codingKey).displayStyle == .enum { | ||
|
||
self.init(rawValue: unsafeBitCast(codingKey, to: UInt8.self)) | ||
|
||
} else { | ||
|
||
return nil | ||
} | ||
} | ||
} | ||
|
||
internal extension Sequence where Element == CodingKey { | ||
|
||
/// KVC path string for current coding path. | ||
var path: String { | ||
return reduce("", { $0 + "\($0.isEmpty ? "" : ".")" + $1.stringValue }) | ||
} | ||
} | ||
|
||
internal extension CodingKey { | ||
|
||
static var sanitizedName: String { | ||
|
||
let rawName = String(reflecting: self) | ||
#if swift(>=5.0) | ||
var elements = rawName.split(separator: ".") | ||
#else | ||
var elements = rawName.components(separatedBy: ".") | ||
#endif | ||
guard elements.count > 2 | ||
else { return rawName } | ||
elements.removeFirst() | ||
#if swift(>=5.0) | ||
elements.removeAll { $0.contains("(unknown context") } | ||
#else | ||
while let index = elements.index(where: { $0.contains("(unknown context") }) { | ||
elements.remove(at: index) | ||
} | ||
#endif | ||
return elements.reduce("", { $0 + ($0.isEmpty ? "" : ".") + $1 }) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
// | ||
// Data.swift | ||
// TLVCoding | ||
// | ||
// Created by Alsey Coleman Miller on 5/12/19. | ||
// Copyright © 2019 PureSwift. All rights reserved. | ||
// | ||
|
||
import Foundation | ||
|
||
internal extension Data { | ||
|
||
#if swift(>=5.0) || (swift(>=4.2) && XCODE) | ||
func subdataNoCopy(in range: Range<Int>) -> Data { | ||
|
||
// stored in heap, can reuse buffer | ||
if count > Data.inlineBufferSize { | ||
|
||
#if swift(>=5.0) | ||
return withUnsafeBytes { (buffer: UnsafeRawBufferPointer) in | ||
Data(bytesNoCopy: UnsafeMutableRawPointer(mutating: buffer.baseAddress!.advanced(by: range.lowerBound)), | ||
count: range.count, | ||
deallocator: .none) | ||
} | ||
#else | ||
return withUnsafeBytes { | ||
Data(bytesNoCopy: UnsafeMutableRawPointer(mutating: $0.advanced(by: range.lowerBound)), | ||
count: range.count, | ||
deallocator: .none) | ||
} | ||
#endif | ||
|
||
} else { | ||
|
||
// stored in stack, must copy | ||
return subdata(in: range) | ||
} | ||
} | ||
#elseif swift(>=4.2) | ||
func subdataNoCopy(in range: Range<Int>) -> Data { | ||
|
||
return withUnsafeBytes { | ||
Data(bytesNoCopy: UnsafeMutableRawPointer(mutating: $0.advanced(by: range.lowerBound)), | ||
count: range.count, | ||
deallocator: .none) | ||
} | ||
} | ||
#elseif swift(>=4.0) | ||
func subdataNoCopy(in range: CountableRange<Int>) -> Data { | ||
|
||
let pointer = withUnsafeBytes { UnsafeMutableRawPointer(mutating: $0).advanced(by: range.lowerBound) } | ||
return Data(bytesNoCopy: pointer, count: range.count, deallocator: .none) | ||
} | ||
|
||
/// Returns a new copy of the data in a specified range. | ||
func subdata(in range: CountableRange<Int>) -> Data { | ||
return Data(self[range]) | ||
} | ||
#endif | ||
|
||
func suffixNoCopy(from index: Int) -> Data { | ||
|
||
return subdataNoCopy(in: index ..< count) | ||
} | ||
|
||
func suffixCheckingBounds(from start: Int) -> Data { | ||
|
||
if count > start { | ||
|
||
return Data(suffix(from: start)) | ||
|
||
} else { | ||
|
||
return Data() | ||
} | ||
} | ||
} | ||
|
||
#if swift(>=5.0) || (swift(>=4.2) && XCODE) | ||
private extension Data { | ||
|
||
/// Size of the inline buffer for `Foundation.Data` used in Swift 5. | ||
/// | ||
/// Used to determine wheather data is stored on stack or in heap. | ||
static var inlineBufferSize: Int { | ||
|
||
// Keep up to date | ||
// https://github.com/apple/swift-corelibs-foundation/blob/master/Foundation/Data.swift#L621 | ||
#if arch(x86_64) || arch(arm64) || arch(s390x) || arch(powerpc64) || arch(powerpc64le) | ||
typealias Buffer = (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | ||
UInt8, UInt8, UInt8, UInt8, UInt8, UInt8) //len //enum | ||
#elseif arch(i386) || arch(arm) | ||
typealias Buffer = (UInt8, UInt8, UInt8, UInt8, | ||
UInt8, UInt8) | ||
#endif | ||
|
||
return MemoryLayout<Buffer>.size | ||
} | ||
} | ||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
// | ||
// DataConvertible.swift | ||
// TLVCoding | ||
// | ||
// Created by Alsey Coleman Miller on 5/12/19. | ||
// Copyright © 2019 PureSwift. All rights reserved. | ||
// | ||
|
||
import Foundation | ||
|
||
/// Can be converted into data. | ||
internal protocol DataConvertible { | ||
|
||
/// Append data representation into buffer. | ||
static func += <T: DataContainer> (data: inout T, value: Self) | ||
|
||
/// Length of value when encoded into data. | ||
var dataLength: Int { get } | ||
} | ||
|
||
extension Data { | ||
|
||
/// Initialize data with contents of value. | ||
@inline(__always) | ||
init <T: DataConvertible> (_ value: T) { | ||
self.init(capacity: value.dataLength) | ||
self += value | ||
} | ||
|
||
init <T: Sequence> (_ sequence: T) where T.Element: DataConvertible { | ||
|
||
let dataLength = sequence.reduce(0, { $0 + $1.dataLength }) | ||
self.init(capacity: dataLength) | ||
sequence.forEach { self += $0 } | ||
} | ||
} | ||
|
||
// MARK: - UnsafeDataConvertible | ||
|
||
/// Internal Data casting protocol | ||
internal protocol UnsafeDataConvertible: DataConvertible { } | ||
|
||
extension UnsafeDataConvertible { | ||
|
||
var dataLength: Int { | ||
return MemoryLayout<Self>.size | ||
} | ||
|
||
/// Append data representation into buffer. | ||
static func += <T: DataContainer> (data: inout T, value: Self) { | ||
#if swift(>=4.2) | ||
withUnsafePointer(to: value) { | ||
$0.withMemoryRebound(to: UInt8.self, capacity: MemoryLayout<Self>.size) { | ||
data.append($0, count: MemoryLayout<Self>.size) | ||
} | ||
} | ||
#else | ||
var value = value | ||
withUnsafePointer(to: &value) { | ||
$0.withMemoryRebound(to: UInt8.self, capacity: MemoryLayout<Self>.size) { | ||
data.append($0, count: MemoryLayout<Self>.size) | ||
} | ||
} | ||
#endif | ||
} | ||
} | ||
|
||
extension UInt16: UnsafeDataConvertible { } | ||
extension UInt32: UnsafeDataConvertible { } | ||
extension UInt64: UnsafeDataConvertible { } | ||
|
||
// MARK: - DataContainer | ||
|
||
/// Data container type. | ||
internal protocol DataContainer: RandomAccessCollection where Self.Index == Int { | ||
|
||
subscript(index: Int) -> UInt8 { get } | ||
|
||
subscript(range: Range<Int>) -> Slice<Self> { get } | ||
|
||
mutating func append(_ newElement: UInt8) | ||
|
||
mutating func append(_ pointer: UnsafePointer<UInt8>, count: Int) | ||
|
||
mutating func append <C: Collection> (contentsOf bytes: C) where C.Element == UInt8 | ||
|
||
#if swift(>=4.2) | ||
static func += (lhs: inout Self, rhs: UInt8) | ||
static func += <C: Collection> (lhs: inout Self, rhs: C) where C.Element == UInt8 | ||
#endif | ||
} | ||
|
||
extension DataContainer { | ||
|
||
#if swift(>=4.2) | ||
#else | ||
static func += (lhs: inout Self, rhs: UInt8) { | ||
lhs.append(rhs) | ||
} | ||
|
||
static func += <C: Collection> (lhs: inout Self, rhs: C) where C.Element == UInt8 { | ||
lhs.append(contentsOf: rhs) | ||
} | ||
#endif | ||
|
||
mutating func append <T: DataConvertible> (_ value: T) { | ||
self += value | ||
} | ||
} | ||
|
||
extension Data: DataContainer { | ||
|
||
#if swift(>=4.2) | ||
static func += (lhs: inout Data, rhs: UInt8) { | ||
lhs.append(rhs) | ||
} | ||
#endif | ||
} |
Oops, something went wrong.