Skip to content

Commit 5291791

Browse files
authored
Merge pull request #2 from PureSwift/Codable
Added Codable support
2 parents ad39aca + 095c998 commit 5291791

18 files changed

+2316
-317
lines changed

Package.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// swift-tools-version:4.1
1+
// swift-tools-version:5.0
22
import PackageDescription
33

44
let package = Package(name: "TLVCoding",
@@ -12,4 +12,4 @@ let package = Package(name: "TLVCoding",
1212
.target(name: "TLVCoding", path: "./Sources"),
1313
.testTarget(name: "TLVCodingTests", dependencies: ["TLVCoding"])
1414
],
15-
swiftLanguageVersions: [4])
15+
swiftLanguageVersions: [.v5])

[email protected]

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// swift-tools-version:4.1
2+
import PackageDescription
3+
4+
let package = Package(name: "TLVCoding",
5+
products: [
6+
.library(
7+
name: "TLVCoding",
8+
targets: ["TLVCoding"]
9+
)
10+
],
11+
targets: [
12+
.target(name: "TLVCoding", path: "./Sources"),
13+
.testTarget(name: "TLVCodingTests", dependencies: ["TLVCoding"])
14+
],
15+
swiftLanguageVersions: [4])

[email protected]

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// swift-tools-version:4.2
2+
import PackageDescription
3+
4+
let package = Package(name: "TLVCoding",
5+
products: [
6+
.library(
7+
name: "TLVCoding",
8+
targets: ["TLVCoding"]
9+
)
10+
],
11+
targets: [
12+
.target(name: "TLVCoding", path: "./Sources"),
13+
.testTarget(name: "TLVCodingTests", dependencies: ["TLVCoding"])
14+
],
15+
swiftLanguageVersions: [.v4_2])

Sources/CodingKey.swift

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
//
2+
// CodingKey.swift
3+
// TLVCoding
4+
//
5+
// Created by Alsey Coleman Miller on 5/12/19.
6+
// Copyright © 2019 PureSwift. All rights reserved.
7+
//
8+
9+
public protocol TLVCodingKey: CodingKey {
10+
11+
init?(code: TLVTypeCode)
12+
13+
var code: TLVTypeCode { get }
14+
}
15+
16+
public extension TLVCodingKey {
17+
18+
init?(intValue: Int) {
19+
20+
guard intValue <= Int(UInt8.max),
21+
intValue >= Int(UInt8.min)
22+
else { return nil }
23+
24+
self.init(code: TLVTypeCode(rawValue: UInt8(intValue)))
25+
}
26+
27+
var intValue: Int? {
28+
return Int(code.rawValue)
29+
}
30+
}
31+
32+
public extension TLVCodingKey where Self: RawRepresentable, Self.RawValue == TLVTypeCode.RawValue {
33+
34+
init?(code: TLVTypeCode) {
35+
self.init(rawValue: code.rawValue)
36+
}
37+
38+
var code: TLVTypeCode {
39+
return TLVTypeCode(rawValue: rawValue)
40+
}
41+
}
42+
43+
#if swift(>=4.2)
44+
public extension TLVCodingKey where Self: CaseIterable, Self: RawRepresentable, Self.RawValue == TLVTypeCode.RawValue {
45+
46+
init?(stringValue: String) {
47+
48+
guard let value = Self.allCases.first(where: { $0.stringValue == stringValue })
49+
else { return nil }
50+
51+
self = value
52+
}
53+
}
54+
#endif
55+
56+
internal extension TLVTypeCode {
57+
58+
init? <K: CodingKey> (codingKey: K) {
59+
60+
if let tlvCodingKey = codingKey as? TLVCodingKey {
61+
62+
self = tlvCodingKey.code
63+
64+
} else if let intValue = codingKey.intValue {
65+
66+
guard intValue <= Int(UInt8.max),
67+
intValue >= Int(UInt8.min)
68+
else { return nil }
69+
70+
self.init(rawValue: UInt8(intValue))
71+
72+
} else if MemoryLayout<K>.size == MemoryLayout<UInt8>.size,
73+
Mirror(reflecting: codingKey).displayStyle == .enum {
74+
75+
self.init(rawValue: unsafeBitCast(codingKey, to: UInt8.self))
76+
77+
} else {
78+
79+
return nil
80+
}
81+
}
82+
}
83+
84+
internal extension Sequence where Element == CodingKey {
85+
86+
/// KVC path string for current coding path.
87+
var path: String {
88+
return reduce("", { $0 + "\($0.isEmpty ? "" : ".")" + $1.stringValue })
89+
}
90+
}
91+
92+
internal extension CodingKey {
93+
94+
static var sanitizedName: String {
95+
96+
let rawName = String(reflecting: self)
97+
#if swift(>=5.0)
98+
var elements = rawName.split(separator: ".")
99+
#else
100+
var elements = rawName.components(separatedBy: ".")
101+
#endif
102+
guard elements.count > 2
103+
else { return rawName }
104+
elements.removeFirst()
105+
#if swift(>=5.0)
106+
elements.removeAll { $0.contains("(unknown context") }
107+
#else
108+
while let index = elements.index(where: { $0.contains("(unknown context") }) {
109+
elements.remove(at: index)
110+
}
111+
#endif
112+
return elements.reduce("", { $0 + ($0.isEmpty ? "" : ".") + $1 })
113+
}
114+
}

Sources/Data.swift

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
//
2+
// Data.swift
3+
// TLVCoding
4+
//
5+
// Created by Alsey Coleman Miller on 5/12/19.
6+
// Copyright © 2019 PureSwift. All rights reserved.
7+
//
8+
9+
import Foundation
10+
11+
internal extension Data {
12+
13+
#if swift(>=5.0) || (swift(>=4.2) && XCODE)
14+
func subdataNoCopy(in range: Range<Int>) -> Data {
15+
16+
// stored in heap, can reuse buffer
17+
if count > Data.inlineBufferSize {
18+
19+
#if swift(>=5.0)
20+
return withUnsafeBytes { (buffer: UnsafeRawBufferPointer) in
21+
Data(bytesNoCopy: UnsafeMutableRawPointer(mutating: buffer.baseAddress!.advanced(by: range.lowerBound)),
22+
count: range.count,
23+
deallocator: .none)
24+
}
25+
#else
26+
return withUnsafeBytes {
27+
Data(bytesNoCopy: UnsafeMutableRawPointer(mutating: $0.advanced(by: range.lowerBound)),
28+
count: range.count,
29+
deallocator: .none)
30+
}
31+
#endif
32+
33+
} else {
34+
35+
// stored in stack, must copy
36+
return subdata(in: range)
37+
}
38+
}
39+
#elseif swift(>=4.2)
40+
func subdataNoCopy(in range: Range<Int>) -> Data {
41+
42+
return withUnsafeBytes {
43+
Data(bytesNoCopy: UnsafeMutableRawPointer(mutating: $0.advanced(by: range.lowerBound)),
44+
count: range.count,
45+
deallocator: .none)
46+
}
47+
}
48+
#elseif swift(>=4.0)
49+
func subdataNoCopy(in range: CountableRange<Int>) -> Data {
50+
51+
let pointer = withUnsafeBytes { UnsafeMutableRawPointer(mutating: $0).advanced(by: range.lowerBound) }
52+
return Data(bytesNoCopy: pointer, count: range.count, deallocator: .none)
53+
}
54+
55+
/// Returns a new copy of the data in a specified range.
56+
func subdata(in range: CountableRange<Int>) -> Data {
57+
return Data(self[range])
58+
}
59+
#endif
60+
61+
func suffixNoCopy(from index: Int) -> Data {
62+
63+
return subdataNoCopy(in: index ..< count)
64+
}
65+
66+
func suffixCheckingBounds(from start: Int) -> Data {
67+
68+
if count > start {
69+
70+
return Data(suffix(from: start))
71+
72+
} else {
73+
74+
return Data()
75+
}
76+
}
77+
}
78+
79+
#if swift(>=5.0) || (swift(>=4.2) && XCODE)
80+
private extension Data {
81+
82+
/// Size of the inline buffer for `Foundation.Data` used in Swift 5.
83+
///
84+
/// Used to determine wheather data is stored on stack or in heap.
85+
static var inlineBufferSize: Int {
86+
87+
// Keep up to date
88+
// https://github.com/apple/swift-corelibs-foundation/blob/master/Foundation/Data.swift#L621
89+
#if arch(x86_64) || arch(arm64) || arch(s390x) || arch(powerpc64) || arch(powerpc64le)
90+
typealias Buffer = (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,
91+
UInt8, UInt8, UInt8, UInt8, UInt8, UInt8) //len //enum
92+
#elseif arch(i386) || arch(arm)
93+
typealias Buffer = (UInt8, UInt8, UInt8, UInt8,
94+
UInt8, UInt8)
95+
#endif
96+
97+
return MemoryLayout<Buffer>.size
98+
}
99+
}
100+
#endif

Sources/DataConvertible.swift

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
//
2+
// DataConvertible.swift
3+
// TLVCoding
4+
//
5+
// Created by Alsey Coleman Miller on 5/12/19.
6+
// Copyright © 2019 PureSwift. All rights reserved.
7+
//
8+
9+
import Foundation
10+
11+
/// Can be converted into data.
12+
internal protocol DataConvertible {
13+
14+
/// Append data representation into buffer.
15+
static func += <T: DataContainer> (data: inout T, value: Self)
16+
17+
/// Length of value when encoded into data.
18+
var dataLength: Int { get }
19+
}
20+
21+
extension Data {
22+
23+
/// Initialize data with contents of value.
24+
@inline(__always)
25+
init <T: DataConvertible> (_ value: T) {
26+
self.init(capacity: value.dataLength)
27+
self += value
28+
}
29+
30+
init <T: Sequence> (_ sequence: T) where T.Element: DataConvertible {
31+
32+
let dataLength = sequence.reduce(0, { $0 + $1.dataLength })
33+
self.init(capacity: dataLength)
34+
sequence.forEach { self += $0 }
35+
}
36+
}
37+
38+
// MARK: - UnsafeDataConvertible
39+
40+
/// Internal Data casting protocol
41+
internal protocol UnsafeDataConvertible: DataConvertible { }
42+
43+
extension UnsafeDataConvertible {
44+
45+
var dataLength: Int {
46+
return MemoryLayout<Self>.size
47+
}
48+
49+
/// Append data representation into buffer.
50+
static func += <T: DataContainer> (data: inout T, value: Self) {
51+
#if swift(>=4.2)
52+
withUnsafePointer(to: value) {
53+
$0.withMemoryRebound(to: UInt8.self, capacity: MemoryLayout<Self>.size) {
54+
data.append($0, count: MemoryLayout<Self>.size)
55+
}
56+
}
57+
#else
58+
var value = value
59+
withUnsafePointer(to: &value) {
60+
$0.withMemoryRebound(to: UInt8.self, capacity: MemoryLayout<Self>.size) {
61+
data.append($0, count: MemoryLayout<Self>.size)
62+
}
63+
}
64+
#endif
65+
}
66+
}
67+
68+
extension UInt16: UnsafeDataConvertible { }
69+
extension UInt32: UnsafeDataConvertible { }
70+
extension UInt64: UnsafeDataConvertible { }
71+
72+
// MARK: - DataContainer
73+
74+
/// Data container type.
75+
internal protocol DataContainer: RandomAccessCollection where Self.Index == Int {
76+
77+
subscript(index: Int) -> UInt8 { get }
78+
79+
subscript(range: Range<Int>) -> Slice<Self> { get }
80+
81+
mutating func append(_ newElement: UInt8)
82+
83+
mutating func append(_ pointer: UnsafePointer<UInt8>, count: Int)
84+
85+
mutating func append <C: Collection> (contentsOf bytes: C) where C.Element == UInt8
86+
87+
#if swift(>=4.2)
88+
static func += (lhs: inout Self, rhs: UInt8)
89+
static func += <C: Collection> (lhs: inout Self, rhs: C) where C.Element == UInt8
90+
#endif
91+
}
92+
93+
extension DataContainer {
94+
95+
#if swift(>=4.2)
96+
#else
97+
static func += (lhs: inout Self, rhs: UInt8) {
98+
lhs.append(rhs)
99+
}
100+
101+
static func += <C: Collection> (lhs: inout Self, rhs: C) where C.Element == UInt8 {
102+
lhs.append(contentsOf: rhs)
103+
}
104+
#endif
105+
106+
mutating func append <T: DataConvertible> (_ value: T) {
107+
self += value
108+
}
109+
}
110+
111+
extension Data: DataContainer {
112+
113+
#if swift(>=4.2)
114+
static func += (lhs: inout Data, rhs: UInt8) {
115+
lhs.append(rhs)
116+
}
117+
#endif
118+
}

0 commit comments

Comments
 (0)