Skip to content

Commit 9df7e72

Browse files
committed
Better container types with custom encoders/decoders
1 parent fd2c9a4 commit 9df7e72

9 files changed

+254
-48
lines changed

Sources/ScaleCodec/Array.swift

Lines changed: 64 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,46 +7,77 @@
77

88
import Foundation
99

10-
extension Array: ScaleEncodable where Element: ScaleEncodable {
11-
public func encode(in encoder: ScaleEncoder) throws {
10+
extension Array: ScaleContainerEncodable {
11+
public typealias EElement = Element
12+
13+
public func encode(in encoder: ScaleEncoder, writer: @escaping (EElement, ScaleEncoder) throws -> Void) throws {
1214
try encoder.encode(compact: UInt32(count))
1315
for element in self {
14-
try encoder.encode(element)
16+
try writer(element, encoder)
1517
}
1618
}
1719
}
1820

19-
extension Array: ScaleDecodable where Element: ScaleDecodable {
20-
public init(from decoder: ScaleDecoder) throws {
21+
extension Array: ScaleContainerDecodable {
22+
public typealias DElement = Element
23+
24+
public init(from decoder: ScaleDecoder, reader: @escaping (ScaleDecoder) throws -> Element) throws {
2125
let size = try Int(decoder.decode(UInt32.self, .compact))
2226
var array = Array<Element>()
2327
array.reserveCapacity(size)
2428
for _ in 0..<size {
25-
try array.append(decoder.decode())
29+
try array.append(reader(decoder))
2630
}
2731
self = array
2832
}
2933
}
3034

35+
extension Array: ScaleEncodable where Element: ScaleEncodable {}
36+
37+
extension Array: ScaleDecodable where Element: ScaleDecodable {}
38+
3139
extension ScaleDecoder {
3240
public func decode<T: ScaleDecodable>(_ type: [T].Type, _ fixed: ScaleFixedTypeMarker) throws -> [T] {
3341
return try self.decode(fixed)
3442
}
3543

36-
public func decode<T: ScaleDecodable>(_ fixed: ScaleFixedTypeMarker) throws -> [T] {
44+
public func decode<T>(
45+
_ type: [T].Type, _ fixed: ScaleFixedTypeMarker,
46+
reader: @escaping (ScaleDecoder) throws -> T
47+
) throws -> [T] {
48+
return try self.decode(fixed, reader: reader)
49+
}
50+
51+
public func decode<T>(
52+
_ fixed: ScaleFixedTypeMarker,
53+
reader: @escaping (ScaleDecoder) throws -> T
54+
) throws -> [T] {
3755
guard case .fixed(let size) = fixed else { fatalError() } // Compiler error silencing.
3856
var values = Array<T>()
3957
values.reserveCapacity(Int(size))
4058
for _ in 0..<size {
41-
try values.append(self.decode())
59+
try values.append(reader(self))
4260
}
4361
return values
4462
}
63+
64+
public func decode<T: ScaleDecodable>(_ fixed: ScaleFixedTypeMarker) throws -> [T] {
65+
try self.decode(fixed) { decoder in try decoder.decode() }
66+
}
4567
}
4668

4769
extension ScaleEncoder {
4870
@discardableResult
4971
public func encode<T: ScaleEncodable>(_ values: [T], fixed: UInt) throws -> ScaleEncoder {
72+
try self.encode(values, fixed: fixed) { val, enc in try enc.encode(val) }
73+
return self
74+
}
75+
76+
@discardableResult
77+
public func encode<T>(
78+
_ values: [T], fixed: UInt,
79+
writer: @escaping (T, ScaleEncoder) throws -> Void
80+
) throws -> ScaleEncoder {
5081
guard values.count == fixed else {
5182
throw SEncodingError.invalidValue(
5283
values, SEncodingError.Context(
@@ -56,22 +87,45 @@ extension ScaleEncoder {
5687
)
5788
}
5889
for val in values {
59-
try self.encode(val)
90+
try writer(val, self)
6091
}
6192
return self
6293
}
6394
}
6495

6596
extension SCALE {
66-
public func decode<T: ScaleDecodable>(_ type: [T].Type, _ fixed: ScaleFixedTypeMarker, from data: Data) throws -> [T] {
97+
public func decode<T: ScaleDecodable>(
98+
_ type: [T].Type, _ fixed: ScaleFixedTypeMarker, from data: Data
99+
) throws -> [T] {
67100
return try self.decode(fixed, from: data)
68101
}
69102

103+
public func decode<T>(
104+
_ type: [T].Type, _ fixed: ScaleFixedTypeMarker, from data: Data,
105+
reader: @escaping (ScaleDecoder) throws -> T
106+
) throws -> [T] {
107+
return try self.decode(fixed, from: data, reader: reader)
108+
}
109+
70110
public func decode<T: ScaleDecodable>(_ fixed: ScaleFixedTypeMarker, from data: Data) throws -> [T] {
71111
return try self.decoder(data: data).decode(fixed)
72112
}
73113

114+
public func decode<T>(
115+
_ fixed: ScaleFixedTypeMarker, from data: Data,
116+
reader: @escaping (ScaleDecoder) throws -> T
117+
) throws -> [T] {
118+
return try self.decoder(data: data).decode(fixed, reader: reader)
119+
}
120+
74121
public func encode<T: ScaleEncodable>(_ values: [T], fixed: UInt) throws -> Data {
75122
return try self.encoder().encode(values, fixed: fixed).output
76123
}
124+
125+
public func encode<T>(
126+
_ values: [T], fixed: UInt,
127+
writer: @escaping (T, ScaleEncoder) throws -> Void
128+
) throws -> Data {
129+
return try self.encoder().encode(values, fixed: fixed, writer: writer).output
130+
}
77131
}

Sources/ScaleCodec/Containers.swift

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
//
2+
// Collections.swift
3+
//
4+
//
5+
// Created by Yehor Popovych on 10/10/20.
6+
//
7+
8+
import Foundation
9+
10+
public protocol ScaleContainerEncodable {
11+
associatedtype EElement
12+
13+
func encode(in encoder: ScaleEncoder, writer: @escaping (EElement, ScaleEncoder) throws -> Void) throws
14+
}
15+
16+
public protocol ScaleContainerDecodable {
17+
associatedtype DElement
18+
19+
init(from decoder: ScaleDecoder, reader: @escaping (ScaleDecoder) throws -> DElement) throws
20+
}
21+
22+
extension ScaleContainerEncodable where EElement: ScaleEncodable {
23+
public func encode(in encoder: ScaleEncoder) throws {
24+
try self.encode(in: encoder) { val, enc in
25+
try enc.encode(val)
26+
}
27+
}
28+
}
29+
30+
extension ScaleContainerDecodable where DElement: ScaleDecodable {
31+
public init(from decoder: ScaleDecoder) throws {
32+
try self.init(from: decoder) { try $0.decode() }
33+
}
34+
}
35+
36+
public protocol ScaleDoubleContainerEncodable {
37+
associatedtype ELeft
38+
associatedtype ERight
39+
40+
func encode(
41+
in encoder: ScaleEncoder,
42+
lwriter: @escaping (ELeft, ScaleEncoder) throws -> Void,
43+
rwriter: @escaping (ERight, ScaleEncoder) throws -> Void
44+
) throws
45+
}
46+
47+
public protocol ScaleDoubleContainerDecodable {
48+
associatedtype DLeft
49+
associatedtype DRight
50+
51+
init(
52+
from decoder: ScaleDecoder,
53+
lreader: @escaping (ScaleDecoder) throws -> DLeft,
54+
rreader: @escaping (ScaleDecoder) throws -> DRight
55+
) throws
56+
}
57+
58+
extension ScaleDoubleContainerEncodable where ELeft: ScaleEncodable {
59+
public func encode(in encoder: ScaleEncoder, writer: @escaping (ERight, ScaleEncoder) throws -> Void) throws {
60+
try encode(in: encoder, lwriter: { val, enc in try enc.encode(val) }, rwriter: writer)
61+
}
62+
}
63+
64+
extension ScaleDoubleContainerEncodable where ERight: ScaleEncodable {
65+
public func encode(in encoder: ScaleEncoder, writer: @escaping (ELeft, ScaleEncoder) throws -> Void) throws {
66+
try encode(in: encoder, lwriter: writer, rwriter: { val, enc in try enc.encode(val) })
67+
}
68+
}
69+
70+
extension ScaleDoubleContainerDecodable where DLeft: ScaleDecodable {
71+
public init(from decoder: ScaleDecoder, reader: @escaping (ScaleDecoder) throws -> DRight) throws {
72+
try self.init(from: decoder, lreader: { try $0.decode() }, rreader: reader)
73+
}
74+
}
75+
76+
extension ScaleDoubleContainerDecodable where DRight: ScaleDecodable {
77+
public init(from decoder: ScaleDecoder, reader: @escaping (ScaleDecoder) throws -> DLeft) throws {
78+
try self.init(from: decoder, lreader: reader, rreader: { try $0.decode() })
79+
}
80+
}
81+

Sources/ScaleCodec/Decoder.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ public protocol ScaleDecodable {
1111
init(from decoder: ScaleDecoder) throws
1212
}
1313

14-
public protocol ScaleDecoder {
14+
public protocol ScaleDecoder: class {
1515
var length: Int { get }
1616
var path: [String] { get }
1717

Sources/ScaleCodec/Dictionary.swift

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,44 @@
77

88
import Foundation
99

10-
extension Dictionary: ScaleEncodable where Key: ScaleEncodable, Value: ScaleEncodable {
11-
public func encode(in encoder: ScaleEncoder) throws {
12-
try encoder.encode(self.map { STuple($0) })
10+
extension Dictionary: ScaleDoubleContainerEncodable {
11+
public typealias ELeft = Key
12+
public typealias ERight = Value
13+
14+
public func encode(
15+
in encoder: ScaleEncoder,
16+
lwriter: @escaping (Key, ScaleEncoder) throws -> Void,
17+
rwriter: @escaping (Value, ScaleEncoder) throws -> Void
18+
) throws {
19+
let array = Array(self)
20+
try array.encode(in: encoder) { (kv, enc) in
21+
try lwriter(kv.key, enc); try rwriter(kv.value, enc)
22+
}
1323
}
1424
}
1525

16-
extension Dictionary: ScaleDecodable where Key: ScaleDecodable, Value: ScaleDecodable {
17-
public init(from decoder: ScaleDecoder) throws {
18-
let array = try decoder.decode(Array<STuple2<Key, Value>>.self).map { $0.tuple }
26+
extension Dictionary: ScaleDoubleContainerDecodable {
27+
public typealias DLeft = Key
28+
public typealias DRight = Value
29+
30+
public init(
31+
from decoder: ScaleDecoder,
32+
lreader: @escaping (ScaleDecoder) throws -> Key,
33+
rreader: @escaping (ScaleDecoder) throws -> Value
34+
) throws {
35+
let array = try Array<(DLeft, DRight)>(from: decoder) { try (lreader($0), rreader($0)) }
1936
self = Dictionary(uniqueKeysWithValues: array)
2037
}
2138
}
39+
40+
extension Dictionary: ScaleContainerEncodable where Key: ScaleEncodable {
41+
public typealias EElement = Value
42+
}
43+
44+
extension Dictionary: ScaleContainerDecodable where Key: ScaleDecodable {
45+
public typealias DElement = Value
46+
}
47+
48+
extension Dictionary: ScaleEncodable where Key: ScaleEncodable, Value: ScaleEncodable {}
49+
50+
extension Dictionary: ScaleDecodable where Key: ScaleDecodable, Value: ScaleDecodable {}

Sources/ScaleCodec/Encoder.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ public protocol ScaleEncodable {
1111
func encode(in encoder: ScaleEncoder) throws;
1212
}
1313

14-
public protocol ScaleEncoder {
14+
public protocol ScaleEncoder: class {
1515
var path: [String] { get }
1616
var output: Data { get }
1717

Sources/ScaleCodec/Error.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public enum SEncodingError : Error {
1818
}
1919
}
2020

21-
case invalidValue(ScaleEncodable, SEncodingError.Context)
21+
case invalidValue(Any, SEncodingError.Context)
2222
}
2323

2424
public enum SDecodingError : Error {

Sources/ScaleCodec/Optional.swift

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,28 +7,34 @@
77

88
import Foundation
99

10-
extension Optional: ScaleEncodable where Wrapped: ScaleEncodable {
11-
public func encode(in encoder: ScaleEncoder) throws {
12-
if Wrapped.self == Bool.self {
10+
extension Optional: ScaleContainerEncodable {
11+
public typealias EElement = Wrapped
12+
13+
public func encode(in encoder: ScaleEncoder, writer: @escaping (EElement, ScaleEncoder) throws -> Void) throws {
14+
if EElement.self == Bool.self {
1315
try encodeBool(encoder: encoder, self as! Bool?)
1416
} else {
1517
switch self {
1618
case .none: try encoder.encode(UInt8(0x00))
17-
case .some(let val): try encoder.encode(UInt8(0x01)).encode(val)
19+
case .some(let val): try writer(val, encoder.encode(UInt8(0x01)))
1820
}
1921
}
2022
}
2123
}
2224

23-
extension Optional: ScaleDecodable where Wrapped: ScaleDecodable {
24-
public init(from decoder: ScaleDecoder) throws {
25-
if Wrapped.self == Bool.self {
26-
self = try decodeBool(decoder: decoder) as! Wrapped?
25+
extension Optional: ScaleEncodable where Wrapped: ScaleEncodable {}
26+
27+
extension Optional: ScaleContainerDecodable {
28+
public typealias DElement = Wrapped
29+
30+
public init(from decoder: ScaleDecoder, reader: @escaping (ScaleDecoder) throws -> DElement) throws {
31+
if DElement.self == Bool.self {
32+
self = try decodeBool(decoder: decoder) as! DElement?
2733
} else {
2834
let val = try decoder.decode(UInt8.self)
2935
switch val {
3036
case 0x00: self = .none
31-
case 0x01: self = try .some(decoder.decode())
37+
case 0x01: self = try .some(reader(decoder))
3238
default:
3339
throw SDecodingError.dataCorrupted(
3440
SDecodingError.Context(
@@ -41,6 +47,8 @@ extension Optional: ScaleDecodable where Wrapped: ScaleDecodable {
4147
}
4248
}
4349

50+
extension Optional: ScaleDecodable where Wrapped: ScaleDecodable {}
51+
4452
private func encodeBool(encoder: ScaleEncoder, _ value: Bool?) throws {
4553
switch value {
4654
case .none: try encoder.encode(UInt8(0x00))

0 commit comments

Comments
 (0)