@@ -4,27 +4,19 @@ struct JSONUnkeyedDecodingContainer: UnkeyedDecodingContainer {
44 let codingPath : [ CodingKey ]
55 let array : [ JSONValue ]
66
7- let count : Int ? // protocol requirement to be optional
8- var isAtEnd : Bool
7+ var count : Int ? { self . array . count }
8+ var isAtEnd : Bool { self . currentIndex >= ( self . count ?? 0 ) }
99 var currentIndex = 0
1010
1111 init ( impl: JSONDecoderImpl , codingPath: [ CodingKey ] , array: [ JSONValue ] ) {
1212 self . impl = impl
1313 self . codingPath = codingPath
1414 self . array = array
15-
16- self . isAtEnd = array. count == 0
17- self . count = array. count
1815 }
1916
2017 mutating func decodeNil( ) throws -> Bool {
21- if self . array [ self . currentIndex] == . null {
22- defer {
23- currentIndex += 1
24- if currentIndex == count {
25- isAtEnd = true
26- }
27- }
18+ if try self . getNextValue ( ofType: Never . self) == . null {
19+ self . currentIndex += 1
2820 return true
2921 }
3022
@@ -34,41 +26,31 @@ struct JSONUnkeyedDecodingContainer: UnkeyedDecodingContainer {
3426 }
3527
3628 mutating func decode( _ type: Bool . Type ) throws -> Bool {
37- defer {
38- currentIndex += 1
39- if currentIndex == count {
40- isAtEnd = true
41- }
42- }
43-
44- guard case . bool( let bool) = self . array [ self . currentIndex] else {
45- throw createTypeMismatchError ( type: type, value: self . array [ self . currentIndex] )
29+ let value = try self . getNextValue ( ofType: Bool . self)
30+ guard case . bool( let bool) = value else {
31+ throw createTypeMismatchError ( type: type, value: value)
4632 }
4733
34+ self . currentIndex += 1
4835 return bool
4936 }
5037
5138 mutating func decode( _ type: String . Type ) throws -> String {
52- defer {
53- currentIndex += 1
54- if currentIndex == count {
55- isAtEnd = true
56- }
57- }
58-
59- guard case . string( let string) = self . array [ self . currentIndex] else {
60- throw createTypeMismatchError ( type: type, value: self . array [ self . currentIndex] )
39+ let value = try self . getNextValue ( ofType: String . self)
40+ guard case . string( let string) = value else {
41+ throw createTypeMismatchError ( type: type, value: value)
6142 }
6243
44+ self . currentIndex += 1
6345 return string
6446 }
6547
6648 mutating func decode( _: Double . Type ) throws -> Double {
67- try decodeLosslessStringConvertible ( )
49+ try decodeBinaryFloatingPoint ( )
6850 }
6951
7052 mutating func decode( _: Float . Type ) throws -> Float {
71- try decodeLosslessStringConvertible ( )
53+ try decodeBinaryFloatingPoint ( )
7254 }
7355
7456 mutating func decode( _: Int . Type ) throws -> Int {
@@ -112,20 +94,33 @@ struct JSONUnkeyedDecodingContainer: UnkeyedDecodingContainer {
11294 }
11395
11496 mutating func decode< T> ( _: T . Type ) throws -> T where T: Decodable {
115- let decoder = try decoderForNextElement ( )
116- return try T ( from: decoder)
97+ let decoder = try decoderForNextElement ( ofType: T . self)
98+ let result = try T ( from: decoder)
99+
100+ // Because of the requirement that the index not be incremented unless
101+ // decoding the desired result type succeeds, it can not be a tail call.
102+ // Hopefully the compiler still optimizes well enough that the result
103+ // doesn't get copied around.
104+ self . currentIndex += 1
105+ return result
117106 }
118107
119108 mutating func nestedContainer< NestedKey> ( keyedBy type: NestedKey . Type ) throws
120109 -> KeyedDecodingContainer < NestedKey > where NestedKey: CodingKey
121110 {
122- let decoder = try decoderForNextElement ( )
123- return try decoder. container ( keyedBy: type)
111+ let decoder = try decoderForNextElement ( ofType: KeyedDecodingContainer< NestedKey> . self , isNested: true )
112+ let container = try decoder. container ( keyedBy: type)
113+
114+ self . currentIndex += 1
115+ return container
124116 }
125117
126118 mutating func nestedUnkeyedContainer( ) throws -> UnkeyedDecodingContainer {
127- let decoder = try decoderForNextElement ( )
128- return try decoder. unkeyedContainer ( )
119+ let decoder = try decoderForNextElement ( ofType: UnkeyedDecodingContainer . self, isNested: true )
120+ let container = try decoder. unkeyedContainer ( )
121+
122+ self . currentIndex += 1
123+ return container
129124 }
130125
131126 mutating func superDecoder( ) throws -> Decoder {
@@ -134,17 +129,9 @@ struct JSONUnkeyedDecodingContainer: UnkeyedDecodingContainer {
134129}
135130
136131extension JSONUnkeyedDecodingContainer {
137- private mutating func decoderForNextElement( ) throws -> JSONDecoderImpl {
138- defer {
139- currentIndex += 1
140- if currentIndex == count {
141- isAtEnd = true
142- }
143- }
144-
145- let value = self . array [ self . currentIndex]
146- var newPath = self . codingPath
147- newPath. append ( ArrayKey ( index: self . currentIndex) )
132+ private mutating func decoderForNextElement< T> ( ofType: T . Type , isNested: Bool = false ) throws -> JSONDecoderImpl {
133+ let value = try self . getNextValue ( ofType: T . self, isNested: isNested)
134+ let newPath = self . codingPath + [ ArrayKey ( index: self . currentIndex) ]
148135
149136 return JSONDecoderImpl (
150137 userInfo: self . impl. userInfo,
@@ -153,6 +140,34 @@ extension JSONUnkeyedDecodingContainer {
153140 )
154141 }
155142
143+ /// - Note: Instead of having the `isNested` parameter, it would have been quite nice to just check whether
144+ /// `T` conforms to either `KeyedDecodingContainer` or `UnkeyedDecodingContainer`. Unfortunately, since
145+ /// `KeyedDecodingContainer` takes a generic parameter (the `Key` type), we can't just ask if `T` is one, and
146+ /// type-erasure workarounds are not appropriate to this use case due to, among other things, the inability to
147+ /// conform most of the types that would matter. We also can't use `KeyedDecodingContainerProtocol` for the
148+ /// purpose, as it isn't even an existential and conformance to it can't be checked at runtime at all.
149+ ///
150+ /// However, it's worth noting that the value of `isNested` is always a compile-time constant and the compiler
151+ /// can quite neatly remove whichever branch of the `if` is not taken during optimization, making doing it this
152+ /// way _much_ more performant (for what little it matters given that it's only checked in case of an error).
153+ @inline ( __always)
154+ private func getNextValue< T> ( ofType: T . Type , isNested: Bool = false ) throws -> JSONValue {
155+ guard !self . isAtEnd else {
156+ if isNested {
157+ throw DecodingError . valueNotFound ( T . self,
158+ . init( codingPath: self . codingPath,
159+ debugDescription: " Cannot get nested keyed container -- unkeyed container is at end. " ,
160+ underlyingError: nil ) )
161+ } else {
162+ throw DecodingError . valueNotFound ( T . self,
163+ . init( codingPath: [ ArrayKey ( index: self . currentIndex) ] ,
164+ debugDescription: " Unkeyed container is at end. " ,
165+ underlyingError: nil ) )
166+ }
167+ }
168+ return self . array [ self . currentIndex]
169+ }
170+
156171 @inline ( __always) private func createTypeMismatchError( type: Any . Type , value: JSONValue ) -> DecodingError {
157172 let codingPath = self . codingPath + [ ArrayKey ( index: self . currentIndex) ]
158173 return DecodingError . typeMismatch ( type, . init(
@@ -161,42 +176,32 @@ extension JSONUnkeyedDecodingContainer {
161176 }
162177
163178 @inline ( __always) private mutating func decodeFixedWidthInteger< T: FixedWidthInteger > ( ) throws -> T {
164- defer {
165- currentIndex += 1
166- if currentIndex == count {
167- isAtEnd = true
168- }
169- }
170-
171- guard case . number( let number) = self . array [ self . currentIndex] else {
172- throw self . createTypeMismatchError ( type: T . self, value: self . array [ self . currentIndex] )
179+ let value = try self . getNextValue ( ofType: T . self)
180+ guard case . number( let number) = value else {
181+ throw self . createTypeMismatchError ( type: T . self, value: value)
173182 }
174183
175184 guard let integer = T ( number) else {
176185 throw DecodingError . dataCorruptedError ( in: self ,
177186 debugDescription: " Parsed JSON number < \( number) > does not fit in \( T . self) . " )
178187 }
179188
189+ self . currentIndex += 1
180190 return integer
181191 }
182192
183- @inline ( __always) private mutating func decodeLosslessStringConvertible< T: LosslessStringConvertible > ( ) throws -> T {
184- defer {
185- currentIndex += 1
186- if currentIndex == count {
187- isAtEnd = true
188- }
189- }
190-
191- guard case . number( let number) = self . array [ self . currentIndex] else {
192- throw self . createTypeMismatchError ( type: T . self, value: self . array [ self . currentIndex] )
193+ @inline ( __always) private mutating func decodeBinaryFloatingPoint< T: LosslessStringConvertible > ( ) throws -> T {
194+ let value = try self . getNextValue ( ofType: T . self)
195+ guard case . number( let number) = value else {
196+ throw self . createTypeMismatchError ( type: T . self, value: value)
193197 }
194198
195199 guard let float = T ( number) else {
196200 throw DecodingError . dataCorruptedError ( in: self ,
197201 debugDescription: " Parsed JSON number < \( number) > does not fit in \( T . self) . " )
198202 }
199203
204+ self . currentIndex += 1
200205 return float
201206 }
202207}
0 commit comments