Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 51 additions & 11 deletions Sources/Decoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@ public struct TLVDecoder {
public var log: ((String) -> ())?

/// Format for numeric values.
public var numericFormat: TLVNumericFormat = .littleEndian
public var numericFormatting: TLVNumericFormatting = .default

/// Format for UUID values.
public var uuidFormat: TLVUUIDFormat = .bytes
public var uuidFormatting: TLVUUIDFormatting = .default

/// Format for Date values.
public var dateFormat: TLVDateFormat = .secondsSince1970
public var dateFormatting: TLVDateFormatting = .default

// MARK: - Initialization

Expand All @@ -41,9 +41,9 @@ public struct TLVDecoder {
let items = try decode(data)

let options = Decoder.Options(
numericFormat: numericFormat,
uuidFormat: uuidFormat,
dateFormat: dateFormat
numericFormatting: numericFormatting,
uuidFormatting: uuidFormatting,
dateFormatting: dateFormatting
)

let decoder = Decoder(referencing: .items(items),
Expand Down Expand Up @@ -110,6 +110,39 @@ public struct TLVDecoder {
}
}

// MARK: - Deprecated

public extension TLVDecoder {

@available(*, deprecated, message: "Renamed to numericFormatting")
var numericFormat: TLVNumericFormat {
get { return numericFormatting }
set { numericFormatting = newValue }
}

@available(*, deprecated, message: "Renamed to uuidFormatting")
var uuidFormat: TLVUUIDFormat {
get { return uuidFormatting }
set { uuidFormatting = newValue }
}

@available(*, deprecated, message: "Renamed to dateFormatting")
var dateFormat: TLVDateFormat {
get { return dateFormatting }
set { dateFormatting = newValue }
}
}

// MARK: - Combine

#if canImport(Combine)
import Combine

extension TLVDecoder: TopLevelDecoder { }
#endif

// MARK: - Decoder

internal extension TLVDecoder {

final class Decoder: Swift.Decoder {
Expand Down Expand Up @@ -206,7 +239,14 @@ internal extension TLVDecoder {

internal extension TLVDecoder.Decoder {

typealias Options = TLVOptions
struct Options {

let numericFormatting: TLVNumericFormatting

let uuidFormatting: TLVUUIDFormatting

let dateFormatting: TLVDateFormatting
}
}

// MARK: - Coding Key
Expand Down Expand Up @@ -243,7 +283,7 @@ internal extension TLVDecoder.Decoder {
func unboxNumeric <T: TLVRawDecodable & FixedWidthInteger> (_ data: Data, as type: T.Type) throws -> T {

var numericValue = try unbox(data, as: type)
switch options.numericFormat {
switch options.numericFormatting {
case .bigEndian:
numericValue = T.init(bigEndian: numericValue)
case .littleEndian:
Expand Down Expand Up @@ -291,7 +331,7 @@ private extension TLVDecoder.Decoder {

func unboxUUID(_ data: Data) throws -> UUID {

switch options.uuidFormat {
switch options.uuidFormatting {
case .bytes:
guard data.count == MemoryLayout<uuid_t>.size else {
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: codingPath, debugDescription: "Invalud number of bytes (\(data.count)) for UUID"))
Expand All @@ -310,7 +350,7 @@ private extension TLVDecoder.Decoder {

func unboxDate(_ data: Data) throws -> Date {

switch options.dateFormat {
switch options.dateFormatting {
case .secondsSince1970:
let timeInterval = try unboxDouble(data)
return Date(timeIntervalSince1970: timeInterval)
Expand All @@ -320,7 +360,7 @@ private extension TLVDecoder.Decoder {
case .iso8601:
guard #available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *)
else { fatalError("ISO8601DateFormatter is unavailable on this platform.") }
return try unboxDate(data, using: TLVDateFormat.iso8601Formatter)
return try unboxDate(data, using: TLVDateFormatting.iso8601Formatter)
case let .formatted(formatter):
return try unboxDate(data, using: formatter)
}
Expand Down
88 changes: 74 additions & 14 deletions Sources/Encoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,16 @@ public struct TLVEncoder {
public var log: ((String) -> ())?

/// Format for numeric values.
public var numericFormat: TLVNumericFormat = .littleEndian
public var outputFormatting: TLVOutputFormatting = .default

/// Format for numeric values.
public var numericFormatting: TLVNumericFormatting = .default

/// Format for UUID values.
public var uuidFormat: TLVUUIDFormat = .bytes
public var uuidFormatting: TLVUUIDFormatting = .default

/// Format for Date values.
public var dateFormat: TLVDateFormat = .secondsSince1970
public var dateFormatting: TLVDateFormatting = .default

// MARK: - Initialization

Expand All @@ -39,10 +42,12 @@ public struct TLVEncoder {
log?("Will encode \(String(reflecting: T.self))")

let options = Encoder.Options(
numericFormat: numericFormat,
uuidFormat: uuidFormat,
dateFormat: dateFormat
outputFormatting: outputFormatting,
numericFormatting: numericFormatting,
uuidFormatting: uuidFormatting,
dateFormatting: dateFormatting
)

let encoder = Encoder(userInfo: userInfo, log: log, options: options)
try value.encode(to: encoder)
assert(encoder.stack.containers.count == 1)
Expand All @@ -63,6 +68,39 @@ public struct TLVEncoder {
}
}

// MARK: - Deprecated

public extension TLVEncoder {

@available(*, deprecated, message: "Renamed to numericFormatting")
var numericFormat: TLVNumericFormat {
get { return numericFormatting }
set { numericFormatting = newValue }
}

@available(*, deprecated, message: "Renamed to uuidFormatting")
var uuidFormat: TLVUUIDFormat {
get { return uuidFormatting }
set { uuidFormatting = newValue }
}

@available(*, deprecated, message: "Renamed to dateFormatting")
var dateFormat: TLVDateFormat {
get { return dateFormatting }
set { dateFormatting = newValue }
}
}

// MARK: - Combine

#if canImport(Combine)
import Combine

extension TLVEncoder: TopLevelEncoder { }
#endif

// MARK: - Encoder

internal extension TLVEncoder {

final class Encoder: Swift.Encoder {
Expand Down Expand Up @@ -134,7 +172,16 @@ internal extension TLVEncoder {

internal extension TLVEncoder.Encoder {

typealias Options = TLVOptions
struct Options {

public let outputFormatting: TLVOutputFormatting

public let numericFormatting: TLVNumericFormatting

public let uuidFormatting: TLVUUIDFormatting

public let dateFormatting: TLVDateFormatting
}
}

internal extension TLVEncoder.Encoder {
Expand Down Expand Up @@ -163,7 +210,7 @@ internal extension TLVEncoder.Encoder {
func boxNumeric <T: TLVRawEncodable & FixedWidthInteger> (_ value: T) -> Data {

let numericValue: T
switch options.numericFormat {
switch options.numericFormatting {
case .bigEndian:
numericValue = value.bigEndian
case .littleEndian:
Expand Down Expand Up @@ -205,7 +252,7 @@ private extension TLVEncoder.Encoder {

func boxUUID(_ uuid: UUID) -> Data {

switch options.uuidFormat {
switch options.uuidFormatting {
case .bytes:
return Data(uuid)
case .string:
Expand All @@ -215,15 +262,15 @@ private extension TLVEncoder.Encoder {

func boxDate(_ date: Date) -> Data {

switch options.dateFormat {
switch options.dateFormatting {
case .secondsSince1970:
return boxDouble(date.timeIntervalSince1970)
case .millisecondsSince1970:
return boxDouble(date.timeIntervalSince1970 * 1000)
case .iso8601:
guard #available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *)
else { fatalError("ISO8601DateFormatter is unavailable on this platform.") }
return boxDate(date, using: TLVDateFormat.iso8601Formatter)
return boxDate(date, using: TLVDateFormatting.iso8601Formatter)
case let .formatted(formatter):
return boxDate(date, using: formatter)
}
Expand Down Expand Up @@ -280,13 +327,26 @@ internal extension TLVEncoder.Encoder {

final class ItemsContainer {

var items = [TLVItem]()
private(set) var items = [TLVItem]()

init() { }

var data: Data {
return Data(items)
}

@inline(__always)
func append(_ item: TLVItem, options: Options) {
items.append(item)
if options.outputFormatting.sortedKeys {
items.sort(by: { $0.type.rawValue < $1.type.rawValue })
}
}

@inline(__always)
fileprivate func append(_ item: TLVItem) {
items.append(item)
}
}

final class ItemContainer {
Expand Down Expand Up @@ -457,7 +517,7 @@ internal final class TLVKeyedContainer <K : CodingKey> : KeyedEncodingContainerP

let type = try encoder.typeCode(for: key, value: value)
let item = TLVItem(type: type, value: data)
self.container.items.append(item)
self.container.append(item, options: encoder.options)
}
}

Expand Down Expand Up @@ -624,7 +684,7 @@ internal final class TLVUnkeyedEncodingContainer: UnkeyedEncodingContainer {
let item = TLVItem(type: index, value: data)

// write
self.container.items.append(item)
self.container.append(item) // already sorted
}
}

Expand Down
67 changes: 53 additions & 14 deletions Sources/Format.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,59 @@

import Foundation

/// TLV Numeric Encoding Format
public enum TLVNumericFormat: Equatable, Hashable {
/// The output formatting options that determine the readability, size, and element order of an encoded TLV object.
public struct TLVOutputFormatting: Equatable, Hashable {

/// The output formatting option that sorts keys in numerical order.
public var sortedKeys: Bool
}

public extension TLVOutputFormatting {

/// The default TLV output formatting options.
static var `default`: TLVOutputFormatting {
return .init(sortedKeys: true)
}
}

/// TLV number formatting (endianness).
public enum TLVNumericFormatting: Equatable, Hashable {

case bigEndian
case littleEndian
case bigEndian
}

/// TLV UUID Encoding Format
public enum TLVUUIDFormat: Equatable, Hashable {
public extension TLVNumericFormatting {

/// The default TLV endianness for binary representation of numbers.
static var `default`: TLVNumericFormatting {
return .littleEndian
}
}

@available(*, deprecated, message: "Renamed to TLVNumericFormatting")
public typealias TLVNumericFormat = TLVNumericFormatting

/// TLV `UUID` Encoding Format
public enum TLVUUIDFormatting: Equatable, Hashable {

case bytes
case string
}

/// TLV Date Encoding Format
public enum TLVDateFormat: Equatable {
public extension TLVUUIDFormatting {

/// The default TLV `UUID` format.
static var `default`: TLVUUIDFormatting {
return .bytes
}
}

@available(*, deprecated, message: "Renamed to TLVUUIDFormatting")
public typealias TLVUUIDFormat = TLVUUIDFormatting

/// TLV `Date` Encoding Format
public enum TLVDateFormatting: Equatable {

/// Encodes dates in terms of seconds since midnight UTC on January 1, 1970.
case secondsSince1970
Expand All @@ -39,18 +76,20 @@ public enum TLVDateFormat: Equatable {
case formatted(DateFormatter)
}

internal struct TLVOptions {

let numericFormat: TLVNumericFormat
public extension TLVDateFormatting {

let uuidFormat: TLVUUIDFormat

let dateFormat: TLVDateFormat
/// The default TLV `Date` format.
static var `default`: TLVDateFormatting {
return .secondsSince1970
}
}

@available(*, deprecated, message: "Renamed to TLVDateFormatting")
public typealias TLVDateFormat = TLVDateFormatting

// MARK: - Formatters

internal extension TLVDateFormat {
internal extension TLVDateFormatting {

@available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *)
static let iso8601Formatter: ISO8601DateFormatter = {
Expand Down
Loading