Merge 'master' into develop

This commit is contained in:
Caleb Kleveter 2019-05-20 11:40:45 -05:00
commit 32668fb167
No known key found for this signature in database
GPG Key ID: B38DBD5CF2C98D69
14 changed files with 227 additions and 63 deletions

3
.gitignore vendored
View File

@ -2,4 +2,5 @@
/.build
/Packages
/*.xcodeproj
/build
/build
/DerivedData

View File

@ -33,12 +33,19 @@ public final class CSVEncoder {
///
/// Currently, this decideds how `nil` and `bool` values should be handled.
public var encodingOptions: CSVCodingOptions
/// The struct that configures serialization options
public var configuration: Config
/// Creates a new `CSVEncoder` instance.
///
/// - Parameter encodingOptions: The encoding options the use when encoding an object.
public init(encodingOptions: CSVCodingOptions = .default) {
/// - Parameters:
/// - encodingOptions: The encoding options the use when encoding an object.
/// - configuration: The struct that configures serialization options
public init(encodingOptions: CSVCodingOptions = .default, configuration: Config = Config.default) {
self.encodingOptions = encodingOptions
self.configuration = configuration
}
/// Creates a `CSVSyncEncoder` using the registered encoding options.
@ -46,7 +53,7 @@ public final class CSVEncoder {
/// This encoder is for if you have several objects that you want to encode at
/// a single time into a single document.
public var sync: CSVSyncEncoder {
return CSVSyncEncoder(encodingOptions: self.encodingOptions)
return CSVSyncEncoder(encodingOptions: self.encodingOptions, configuration: self.configuration)
}
/// Creates a new `CSVAsyncEncoder` using the registered encoding options.
@ -59,7 +66,7 @@ public final class CSVEncoder {
/// - Returns: A `CSVAsyncEncoder` instance with the current encoder's encoding
/// options and the `onRow` closure as its callback.
public func async(_ onRow: @escaping ([UInt8]) -> ()) -> CSVAsyncEncoder {
return CSVAsyncEncoder(encodingOptions: self.encodingOptions, onRow: onRow)
return CSVAsyncEncoder(encodingOptions: self.encodingOptions, configuration: self.configuration, onRow: onRow)
}
}
@ -68,9 +75,11 @@ public final class CSVEncoder {
/// You can get an instance of the `CSVSyncEncoder` with the `CSVEncoder.sync` property.
public final class CSVSyncEncoder {
internal var encodingOptions: CSVCodingOptions
internal var configuration: Config
internal init(encodingOptions: CSVCodingOptions) {
internal init(encodingOptions: CSVCodingOptions, configuration: Config = Config.default) {
self.encodingOptions = encodingOptions
self.configuration = configuration
}
/// Encodes an array of encodable objects into a single CSV document.
@ -83,7 +92,7 @@ public final class CSVSyncEncoder {
var rows: [[UInt8]] = []
rows.reserveCapacity(objects.count)
let encoder = AsyncEncoder(encodingOptions: self.encodingOptions) { row in
let encoder = AsyncEncoder(encodingOptions: self.encodingOptions, configuration: self.configuration) { row in
rows.append(row)
}
try objects.forEach(encoder.encode)
@ -99,9 +108,9 @@ public final class CSVAsyncEncoder {
internal var encodingOptions: CSVCodingOptions
private var encoder: AsyncEncoder
internal init(encodingOptions: CSVCodingOptions, onRow: @escaping ([UInt8]) -> ()) {
internal init(encodingOptions: CSVCodingOptions, configuration: Config = Config.default, onRow: @escaping ([UInt8]) -> ()) {
self.encodingOptions = encodingOptions
self.encoder = AsyncEncoder(encodingOptions: encodingOptions, onRow: onRow)
self.encoder = AsyncEncoder(encodingOptions: encodingOptions, configuration: configuration, onRow: onRow)
}
/// Encodes an `Encodable` object into a row for a CSV document and passes it into
@ -142,19 +151,31 @@ public final class CSVDecoder {
///
/// This is currently used to specify how `nil` and `Bool` values should be handled.
public var decodingOptions: CSVCodingOptions
/// The CSV configuration to use when decoding or encoding
///
/// This is used to specify if cells are wrapped in quotes and what the delimiter is (comma or tab, etc.)
public var configuration: Config
/// Creates a new `CSVDecoder` instance.
///
/// - Parameter decodingOptions: The decoding options to use when decoding data to an object.
public init(decodingOptions: CSVCodingOptions = .default) {
/// Creates a new `CSVDecoder` instance.
///
/// - Parameters:
/// - decodingOptions: The decoding options to use when decoding data to an object.
/// - configuration: The struct that configures serialization options
public init(decodingOptions: CSVCodingOptions = .default, configuration: Config = Config.default) {
self.decodingOptions = decodingOptions
self.configuration = configuration
}
/// Creates a `CSVSyncDecoder` with the registered encoding options.
///
/// This decoder is for if you have whole CSV document you want to decode at once.
public var sync: CSVSyncDecoder {
return CSVSyncDecoder(decodingOptions: self.decodingOptions)
return CSVSyncDecoder(decodingOptions: self.decodingOptions, configuration: self.configuration)
}
/// Creates a `CSVAsyncDecoder` instance with the registered encoding options.
@ -176,7 +197,8 @@ public final class CSVDecoder {
decoding: D.self,
onInstance: onInstance,
length: length,
decodingOptions: self.decodingOptions
decodingOptions: self.decodingOptions,
configuration: self.configuration
)
}
}
@ -186,9 +208,11 @@ public final class CSVDecoder {
/// You can get an instance of `CSVSyncDecoder` from the `CSVDecoder.sync` property.
public final class CSVSyncDecoder {
internal var decodingOptions: CSVCodingOptions
internal var configuration: Config
internal init(decodingOptions: CSVCodingOptions) {
internal init(decodingOptions: CSVCodingOptions, configuration: Config = Config.default) {
self.decodingOptions = decodingOptions
self.configuration = configuration
}
/// Decodes a whole CSV document into an array of a specified `Decodable` type.
@ -224,9 +248,10 @@ public final class CSVAsyncDecoder {
internal var length: Int
internal var decoding: Decodable.Type
internal var decodingOptions: CSVCodingOptions
internal var configuration: Config
private var rowDecoder: AsyncDecoder
internal init<D>(decoding: D.Type, onInstance: @escaping (D) -> (), length: Int, decodingOptions: CSVCodingOptions)
internal init<D>(decoding: D.Type, onInstance: @escaping (D) -> (), length: Int, decodingOptions: CSVCodingOptions, configuration: Config = Config.default)
where D: Decodable
{
let callback = { (decoded: Decodable) in
@ -240,10 +265,12 @@ public final class CSVAsyncDecoder {
self.length = length
self.decoding = decoding
self.decodingOptions = decodingOptions
self.configuration = configuration
self.rowDecoder = AsyncDecoder(
decoding: D.self,
path: [],
decodingOptions: decodingOptions,
configuration: configuration,
onInstance: callback
)
@ -260,7 +287,7 @@ public final class CSVAsyncDecoder {
///
/// - Parameter data: A section of the CSV document to decode.
/// - Throws: Errors that occur during the decoding process.
public func decode<C>(_ data: C)throws where C: Collection, C.Element == UInt8 {
public func decode<C>(_ data: C) throws where C: Collection, C.Element == UInt8 {
try self.rowDecoder.decode(Array(data), length: self.length)
}
}

View File

@ -20,12 +20,13 @@ internal final class AsyncDecoder: Decoder {
info: [CodingUserInfoKey : Any] = [:],
data: Storage = .none,
decodingOptions: CSVCodingOptions,
configuration: Config = Config.default,
onInstance: @escaping (Decodable)throws -> ()
) {
self.codingPath = path
self.userInfo = info
self.decoding = decoding
self.handler = AsyncDecoderHandler { _ in return }
self.handler = AsyncDecoderHandler(configuration: configuration){ _ in return }
self.decodingOptions = decodingOptions
self.onInstance = onInstance
self.data = data
@ -82,8 +83,8 @@ internal final class AsyncDecoderHandler {
private var columnCount: Int
private var currentColumn: Int
init(onRow: @escaping ([String: [UInt8]])throws -> ()) {
self.parser = Parser()
init(configuration: Config = Config.default, onRow: @escaping ([String: [UInt8]])throws -> ()) {
self.parser = Parser(configuration: configuration)
self.currentRow = [:]
self.onRow = onRow
self.columnCount = 0

View File

@ -5,18 +5,21 @@ final class AsyncEncoder: Encoder {
let userInfo: [CodingUserInfoKey : Any]
let container: DataContainer
let encodingOptions: CSVCodingOptions
let configuration: Config
let onRow: ([UInt8]) -> ()
init(
path: [CodingKey] = [],
info: [CodingUserInfoKey : Any] = [:],
encodingOptions: CSVCodingOptions,
configuration: Config = Config.default,
onRow: @escaping ([UInt8]) -> ()
) {
self.codingPath = path
self.userInfo = info
self.container = DataContainer(section: .header)
self.encodingOptions = encodingOptions
self.configuration = configuration
self.onRow = onRow
}
@ -39,14 +42,14 @@ final class AsyncEncoder: Encoder {
switch self.container.section {
case .header:
try object.encode(to: self)
self.onRow(Array(self.container.cells.joined(separator: [44])))
self.onRow(Array(self.container.cells.joined(separator: [self.configuration.cellSeparator])))
self.container.section = .row
self.container.rowCount += 1
self.container.cells = []
fallthrough
case .row:
try object.encode(to: self)
self.onRow(Array(self.container.cells.joined(separator: [44])))
self.onRow(Array(self.container.cells.joined(separator: [self.configuration.cellSeparator])))
self.container.rowCount += 1
self.container.cells = []
}

View File

@ -8,14 +8,18 @@ final class AsyncKeyedEncoder<K>: KeyedEncodingContainerProtocol where K: Coding
self.codingPath = path
self.encoder = encoder
}
var delimiter: UInt8? {
return self.encoder.configuration.cellDelimiter
}
func _encode(_ value: [UInt8], for key: K) {
switch self.encoder.container.section {
case .header:
let bytes = Array([[34], key.stringValue.bytes.escaped, [34]].joined())
let bytes = key.stringValue.bytes.escaping(self.delimiter)
self.encoder.container.cells.append(bytes)
case .row:
let bytes = Array([[34], value.escaped, [34]].joined())
let bytes = value.escaping(self.delimiter)
self.encoder.container.cells.append(bytes)
}
}
@ -41,9 +45,17 @@ final class AsyncKeyedEncoder<K>: KeyedEncodingContainerProtocol where K: Coding
func encode(_ value: String, forKey key: K) throws { self._encode(value.bytes, for: key) }
func encode<T>(_ value: T, forKey key: K) throws where T : Encodable {
let encoder = AsyncEncoder(encodingOptions: self.encoder.encodingOptions, onRow: self.encoder.onRow)
try value.encode(to: encoder)
self._encode(encoder.container.cells[0], for: key)
switch self.encoder.container.section {
case .header: self.encoder.container.cells.append(key.stringValue.bytes.escaping(self.delimiter))
case .row:
let encoder = AsyncEncoder(
encodingOptions: self.encoder.encodingOptions,
configuration: self.encoder.configuration,
onRow: self.encoder.onRow
)
try value.encode(to: encoder)
self.encoder.container.cells.append(encoder.container.cells[0])
}
}
func encodeIfPresent(_ value: Bool?, forKey key: K) throws {

View File

@ -9,18 +9,22 @@ final class AsyncSingleValueEncoder: SingleValueEncodingContainer {
self.encoder = encoder
}
var delimiter: UInt8? {
return self.encoder.configuration.cellDelimiter
}
func encodeNil() throws {
let value = self.encoder.encodingOptions.nilCodingStrategy.bytes()
self.encoder.container.cells.append(value.escaped)
let value = self.encoder.encodingOptions.nilCodingStrategy.bytes().escaping(self.delimiter)
self.encoder.container.cells.append(value)
}
func encode(_ value: Bool) throws {
let value = self.encoder.encodingOptions.boolCodingStrategy.bytes(from: value)
self.encoder.container.cells.append(value.escaped)
let value = self.encoder.encodingOptions.boolCodingStrategy.bytes(from: value).escaping(self.delimiter)
self.encoder.container.cells.append(value)
}
func encode(_ value: String) throws { self.encoder.container.cells.append(value.bytes.escaped) }
func encode(_ value: Double) throws { self.encoder.container.cells.append(value.bytes.escaped) }
func encode(_ value: Float) throws { self.encoder.container.cells.append(value.bytes.escaped) }
func encode(_ value: Int) throws { self.encoder.container.cells.append(value.bytes.escaped) }
func encode(_ value: String) throws { self.encoder.container.cells.append(value.bytes.escaping(self.delimiter)) }
func encode(_ value: Double) throws { self.encoder.container.cells.append(value.bytes.escaping(self.delimiter)) }
func encode(_ value: Float) throws { self.encoder.container.cells.append(value.bytes.escaping(self.delimiter)) }
func encode(_ value: Int) throws { self.encoder.container.cells.append(value.bytes.escaping(self.delimiter)) }
func encode<T>(_ value: T) throws where T : Encodable {
let column = self.codingPath.map { $0.stringValue }.joined(separator: ".")

38
Sources/CSV/Config.swift Normal file
View File

@ -0,0 +1,38 @@
// Wraps the Configuration options for Parse/Encode/Decode
/// The `Config` struct allows for configuring `Parser` and `Serializer`
/// to allow for separators other than comma or string delimiters
/// like quotation marks
public struct Config {
/// The character that separates one cell from another.
public let cellSeparator: UInt8
/// The character that is used to denote the start and end of a cell's contents.
public let cellDelimiter: UInt8?
/// The deault `Config` instance that uses commas for cell separators and double quotes
/// for cell delimiters.
public static let `default`: Config = Config(cellSeparator: 44, cellDelimiter: 34)
/// Creates a new `Config` instance
///
/// - Parameters:
/// - cellSeparator: The character that separates one cell from another.
/// - cellDelimiter: The character that is used to denote the start and end of a cell's contents.
public init(cellSeparator: UInt8, cellDelimiter: UInt8?) {
self.cellSeparator = cellSeparator
self.cellDelimiter = cellDelimiter
}
/// Creates a new `Config` instance from `UnicdeScalar` literals.
///
/// - Parameters:
/// - separator: The `UnicodeScalar` for the separator between cells (`','`).
/// - delimiter: The `UnicdeScalar` for the delimiter that marks the start and end of a cell (`'"'`).
public init(separator: UnicodeScalar, delimiter: UnicodeScalar) {
self.cellSeparator = UInt8(ascii: separator)
self.cellDelimiter = UInt8(ascii: delimiter)
}
}

View File

@ -1,3 +1,4 @@
// '\t' => 9
// '\n' => 10
// '\r' => 13
// '"' => 34
@ -69,6 +70,9 @@ public struct Parser {
/// The callback that is called when a cell is parsed.
public var onCell: CellHandler?
/// The struct that configures parsing options
public var configuration: Config
private var state: State
@ -81,9 +85,11 @@ public struct Parser {
/// - Parameters:
/// - onHeader: The callback that will be called when a header is parsed.
/// - onCell: The callback that will be called when a cell is parsed.
public init(onHeader: HeaderHandler? = nil, onCell: CellHandler? = nil) {
/// - configuration: The struct that configures parsing options
public init(onHeader: HeaderHandler? = nil, onCell: CellHandler? = nil, configuration: Config = Config.default) {
self.onHeader = onHeader
self.onCell = onCell
self.configuration = configuration
self.state = State()
}
@ -113,10 +119,10 @@ public struct Parser {
while index < data.endIndex {
let byte = data[index]
switch byte {
case 34:
case configuration.cellDelimiter:
currentCell.append(contentsOf: data[slice.start..<slice.end])
slice = (index + 1, index + 1)
switch self.state.inQuotes && index + 1 < data.endIndex && data[index + 1] == 34 {
switch self.state.inQuotes && index + 1 < data.endIndex && data[index + 1] == configuration.cellDelimiter {
case true: index += 1
case false: self.state.inQuotes.toggle()
}
@ -136,7 +142,7 @@ public struct Parser {
if self.state.position == .headers { updateState = true }
fallthrough
}
case 44:
case configuration.cellSeparator:
if self.state.inQuotes {
slice.end += 1
} else {
@ -190,9 +196,15 @@ public struct Parser {
/// A synchronous wrapper for the `Parser` type for parsing whole CSV documents at once.
public final class SyncParser {
/// The struct configures parsing options
public var configuration: Config
/// Creates a new `SyncParser` instance
public init() {}
///
/// - Parameter configuration: The struct configures parsing options
public init(configuration: Config = Config.default ) { self.configuration = configuration }
/// Parses a whole CSV document at once.
///
@ -207,7 +219,8 @@ public final class SyncParser {
},
onCell: { header, cell in
results[header, default: []].append(cell.count > 0 ? cell : nil)
}
},
configuration: configuration
)
parser.parse(data)
@ -229,7 +242,8 @@ public final class SyncParser {
let title = String(decoding: header, as: UTF8.self)
let contents = String(decoding: cell, as: UTF8.self)
results[title, default: []].append(cell.count > 0 ? contents : nil)
}
},
configuration: configuration
)
parser.parse(Array(data.utf8))

View File

@ -61,15 +61,21 @@ extension Dictionary: KeyedCollection { }
/// - Note: You should create a new `Serializer` dictionary you serialize.
public struct Serializer {
private var serializedHeaders: Bool
/// The struct configures serialization options
var configuration: Config
/// The callback that will be called with each row that is serialized.
public var onRow: ([UInt8])throws -> ()
/// Creates a new `Serializer` instance.
///
/// - Parameter onRow: The callback that will be called with each row that is serialized.
public init(onRow: @escaping ([UInt8])throws -> ()) {
/// - Parameter: The struct configures serialization options
/// - configuration: The struct that configures serialization options
/// - onRow: The callback that will be called with each row that is serialized.
public init(configuration: Config = Config.default, onRow: @escaping ([UInt8])throws -> ()) {
self.serializedHeaders = false
self.configuration = configuration
self.onRow = onRow
}
@ -94,8 +100,10 @@ public struct Serializer {
guard data.count > 0 else { return errors.result }
if !self.serializedHeaders {
let headers = data.keys.map { title in Array([[34], title.bytes.escaped, [34]].joined()) }
do { try self.onRow(Array(headers.joined(separator: [44]))) }
let headers = data.keys.map { title -> [UInt8] in
return title.bytes.escaping(self.configuration.cellDelimiter)
}
do { try self.onRow(Array(headers.joined(separator: [configuration.cellSeparator]))) }
catch let error { errors.errors.append(error) }
self.serializedHeaders = true
}
@ -103,9 +111,9 @@ public struct Serializer {
guard let first = data.first?.value else { return errors.result }
(first.startIndex..<first.endIndex).forEach { index in
let cells = data.values.map { column -> [UInt8] in
return Array([[34], column[index].bytes.escaped, [34]].joined())
return column[index].bytes.escaping(self.configuration.cellDelimiter)
}
do { try onRow(Array(cells.joined(separator: [44]))) }
do { try onRow(Array(cells.joined(separator: [configuration.cellSeparator]))) }
catch let error { errors.errors.append(error) }
}
@ -116,8 +124,15 @@ public struct Serializer {
/// A synchronous wrapper for the `Serializer` struct for parsing a whole CSV document.
public struct SyncSerializer {
/// The serilization options for the `SyncSerializer` instance.
let configuration: Config
/// Creates a new `SyncSerializer` instance.
public init () { }
///
/// - Parameter configuration: The serilization options for the `SyncSerializer` instance.
public init (configuration: Config = Config.default) {
self.configuration = configuration
}
/// Serializes a dictionary to CSV document data. Usually this will be a dictionary of type
/// `[BytesRepresentable: [BytesRepresentable]], but it can be any type you conform to the proper protocols.
@ -134,7 +149,7 @@ public struct SyncSerializer {
var rows: [[UInt8]] = []
rows.reserveCapacity(data.first?.value.count ?? 0)
var serializer = Serializer { row in rows.append(row) }
var serializer = Serializer(configuration: self.configuration) { row in rows.append(row) }
serializer.serialize(data)
return Array(rows.joined(separator: [10]))

View File

@ -12,8 +12,15 @@ extension String {
}
extension Array where Element == UInt8 {
var escaped: [UInt8] {
return self.contains(34) ? Array(self.split(separator: 34).joined(separator: [34, 34])) : self
func escaping(_ character: UInt8?) -> [UInt8] {
guard let code = character else {
return self
}
let contents = self.contains(code) ?
Array(self.split(separator: code, omittingEmptySubsequences: false).joined(separator: [code, code])) :
self
return Array([[code], contents, [code]].joined())
}
}

View File

@ -44,7 +44,7 @@ final class DecoderTests: XCTestCase {
for _ in 0..<1_000 {
let decoder = CSVDecoder().async(for: Person.self, length: contentLength) { _ in return }
do {
try bytes.forEach(decoder.decode)
try bytes.forEach{ try decoder.decode($0) }
} catch let error as DecodingError {
XCTFail(error.failureReason ?? "No failure reason")
error.errorDescription.map { print($0) }

View File

@ -59,6 +59,26 @@ final class EncoderTests: XCTestCase {
}
}
}
func testEscapingDelimiters() throws {
let quotePerson = Person(firstName: "A", lastName: "J", age: 42, gender: .male, tagLine: #"All "with quotes""#)
let hashPerson = Person(firstName: "M", lastName: "A", age: 28, gender: .female, tagLine: "#iWin#")
let quoteResult = """
"first name","last_name","age","gender","tagLine"
"A","J","42","M","All ""with quotes""\"
"""
let hashResult = """
#first name#,#last_name#,#age#,#gender#,#tagLine#
#M#,#A#,#28#,#F#,###iWin###
"""
let quoteEncoder = CSVEncoder().sync
let hashEncoder = CSVEncoder(encodingOptions: .default, configuration: .init(cellSeparator: 44, cellDelimiter: 35)).sync
try XCTAssertEqual(quoteEncoder.encode([quotePerson]), Data(quoteResult.utf8))
try XCTAssertEqual(hashEncoder.encode([hashPerson]), Data(hashResult.utf8))
}
}
fileprivate struct Person: Codable, Equatable {

View File

@ -3,7 +3,7 @@ import CSV
final class SerializerTests: XCTestCase {
func testSyncSerialize() {
let serializer = SyncSerializer()
let serializer = SyncSerializer(configuration: Config.default)
let serialized = serializer.serialize(orderedData)
let string = String(decoding: serialized, as: UTF8.self)
@ -23,7 +23,7 @@ final class SerializerTests: XCTestCase {
func testChunkedSerialize() throws {
var rows: [[UInt8]] = []
var serializer = Serializer { row in rows.append(row) }
var serializer = Serializer(configuration: Config.default) { row in rows.append(row) }
for chunk in orderedChunks {
try serializer.serialize(chunk).get()
}
@ -42,6 +42,28 @@ final class SerializerTests: XCTestCase {
}
}
}
func testEscapedDelimiter() {
let quoteData: OrderedKeyedCollection = ["list": ["Standard string", #"A string with "quotes""#]]
let hashData: OrderedKeyedCollection = ["list": ["Some string without hashes", "A #string with# hashes"]]
let quoteResult = """
"list"
"Standard string"
"A string with ""quotes""\"
"""
let hashResult = """
#list#
#Some string without hashes#
#A ##string with## hashes#
"""
let quoteSerializer = SyncSerializer()
let hashSerializer = SyncSerializer(configuration: .init(cellSeparator: 44, cellDelimiter: 35))
XCTAssertEqual(quoteSerializer.serialize(quoteData), Array(quoteResult.utf8))
XCTAssertEqual(hashSerializer.serialize(hashData), Array(hashResult.utf8))
}
}
internal struct OrderedKeyedCollection<K, V>: KeyedCollection, ExpressibleByDictionaryLiteral where K: Hashable {

View File

@ -16,7 +16,7 @@ final class StressTests: XCTestCase {
var parser = Parser(onHeader: { _ in return }, onCell: { _, _ in return })
let csv = Array(data)
// Baseline: 4.630
// Baseline: 3.686
measure {
parser.parse(csv)
}
@ -26,7 +26,7 @@ final class StressTests: XCTestCase {
let parser = SyncParser()
let csv = Array(data)
// Baseline: 10.825
// Baseline: 10.510
// Time to beat: 9.142
measure {
_ = parser.parse(csv)
@ -38,7 +38,7 @@ final class StressTests: XCTestCase {
let csv = Array(data)
let parsed = SyncParser().parse(csv)
// Baseline: 18.957
// Baseline: 21.453
// Time to beat: 11.932
measure {
serializer.serialize(parsed)
@ -50,7 +50,7 @@ final class StressTests: XCTestCase {
let csv = Array(data)
let parsed = SyncParser().parse(csv)
// Baseline: 18.047
// Baseline: 22.903
// Time to beat: 11.932
measure {
_ = serializer.serialize(parsed)
@ -60,7 +60,7 @@ final class StressTests: XCTestCase {
func testMeasureAsyncDecoding() {
let decoder = CSVDecoder(decodingOptions: self.codingOptions)
// Baseline: 14.347
// Baseline: 18.217
measure {
do {
let async = decoder.async(
@ -82,7 +82,7 @@ final class StressTests: XCTestCase {
func testMeasureSyncDecoding() {
let decoder = CSVDecoder(decodingOptions: self.codingOptions).sync
// Baseline: 19.736
// Baseline: 18.970
// Time to beat: 18.489
measure {
do {
@ -97,7 +97,7 @@ final class StressTests: XCTestCase {
let people = try CSVDecoder(decodingOptions: self.codingOptions).sync.decode(Response.self, from: data)
let encoder = CSVEncoder(encodingOptions: codingOptions)
// Baseline: 11.477
// Baseline: 14.969
// Time to beat: 9.477
measure {
do {
@ -113,7 +113,7 @@ final class StressTests: XCTestCase {
let people = try CSVDecoder(decodingOptions: self.codingOptions).sync.decode(Response.self, from: data)
let encoder = CSVEncoder(encodingOptions: codingOptions).sync
// Baseline: 13.412
// Baseline: 18.147
// Time to beat: 9.477
measure {
do {