Skip to content

Commit

Permalink
Merge pull request #2 from PureSwift/Codable
Browse files Browse the repository at this point in the history
Added Codable support
  • Loading branch information
colemancda authored May 14, 2019
2 parents ad39aca + 095c998 commit 5291791
Show file tree
Hide file tree
Showing 18 changed files with 2,316 additions and 317 deletions.
4 changes: 2 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// swift-tools-version:4.1
// swift-tools-version:5.0
import PackageDescription

let package = Package(name: "TLVCoding",
Expand All @@ -12,4 +12,4 @@ let package = Package(name: "TLVCoding",
.target(name: "TLVCoding", path: "./Sources"),
.testTarget(name: "TLVCodingTests", dependencies: ["TLVCoding"])
],
swiftLanguageVersions: [4])
swiftLanguageVersions: [.v5])
15 changes: 15 additions & 0 deletions [email protected]
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])
15 changes: 15 additions & 0 deletions [email protected]
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])
114 changes: 114 additions & 0 deletions Sources/CodingKey.swift
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 })
}
}
100 changes: 100 additions & 0 deletions Sources/Data.swift
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
118 changes: 118 additions & 0 deletions Sources/DataConvertible.swift
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
}
Loading

0 comments on commit 5291791

Please sign in to comment.