@@ -6,9 +6,9 @@ public extension String {
6
6
self = Base32 . encodeToString ( bytes: bytes, options: options)
7
7
}
8
8
9
- /// Decode base32 encoded strin
10
- func base32decoded( ) throws -> [ UInt8 ] {
11
- try Base32 . decode ( string: self )
9
+ /// Decode base32 encoded string
10
+ func base32decoded( options : Base32 . DecodingOptions = [ ] ) throws -> [ UInt8 ] {
11
+ try Base32 . decode ( string: self , options : options )
12
12
}
13
13
}
14
14
@@ -22,8 +22,25 @@ public enum Base32 {
22
22
public static let omitPaddingCharacter = EncodingOptions ( rawValue: UInt ( 1 << 0 ) )
23
23
}
24
24
25
- public enum DecodingError : Swift . Error , Equatable {
26
- case invalidCharacter( UInt8 )
25
+ /// Decoding options
26
+ public struct DecodingOptions : OptionSet {
27
+ public let rawValue : UInt
28
+ public init ( rawValue: UInt ) { self . rawValue = rawValue }
29
+
30
+ public static let allowNullCharacters = DecodingOptions ( rawValue: UInt ( 1 << 0 ) )
31
+ }
32
+
33
+ public struct DecodingError : Swift . Error , Equatable {
34
+ enum _Internal {
35
+ case invalidCharacter
36
+ }
37
+
38
+ fileprivate let value : _Internal
39
+ init ( _ value: _Internal ) {
40
+ self . value = value
41
+ }
42
+
43
+ public static var invalidCharacter : Self { . init( . invalidCharacter) }
27
44
}
28
45
29
46
/// Base32 Encode a buffer to an array of bytes
@@ -70,7 +87,10 @@ public enum Base32 {
70
87
}
71
88
72
89
/// Base32 decode string
73
- public static func decode( string encoded: String ) throws -> [ UInt8 ] {
90
+ public static func decode(
91
+ string encoded: String ,
92
+ options: DecodingOptions = [ ]
93
+ ) throws -> [ UInt8 ] {
74
94
let decoded = try encoded. utf8. withContiguousStorageIfAvailable { characterPointer -> [ UInt8 ] in
75
95
guard characterPointer. count > 0 else {
76
96
return [ ]
@@ -80,7 +100,11 @@ public enum Base32 {
80
100
81
101
return try characterPointer. withMemoryRebound ( to: UInt8 . self) { input -> [ UInt8 ] in
82
102
try [ UInt8] ( unsafeUninitializedCapacity: capacity) { output, length in
83
- length = try Self . _decode ( from: input, into: output)
103
+ if options. contains ( . allowNullCharacters) {
104
+ length = try Self . _decode ( from: input [ ... ] , into: output [ ... ] )
105
+ } else {
106
+ length = try Self . _strictDecode ( from: input, into: output)
107
+ }
84
108
}
85
109
}
86
110
}
@@ -95,7 +119,10 @@ public enum Base32 {
95
119
}
96
120
97
121
/// Base32 decode a buffer to an array of UInt8
98
- public static func decode< Buffer: Collection > ( bytes: Buffer ) throws -> [ UInt8 ] where Buffer. Element == UInt8 {
122
+ public static func decode< Buffer: Collection > (
123
+ bytes: Buffer ,
124
+ options: DecodingOptions = [ ]
125
+ ) throws -> [ UInt8 ] where Buffer. Element == UInt8 {
99
126
guard bytes. count > 0 else {
100
127
return [ ]
101
128
}
@@ -104,7 +131,11 @@ public enum Base32 {
104
131
let outputLength = ( ( input. count + 7 ) / 8 ) * 5
105
132
106
133
return try [ UInt8] ( unsafeUninitializedCapacity: outputLength) { output, length in
107
- length = try Self . _decode ( from: input, into: output)
134
+ if options. contains ( . allowNullCharacters) {
135
+ length = try Self . _decode ( from: input [ ... ] , into: output [ ... ] )
136
+ } else {
137
+ length = try Self . _strictDecode ( from: input, into: output)
138
+ }
108
139
}
109
140
}
110
141
@@ -153,27 +184,98 @@ extension Base32 {
153
184
/* F8 */ 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 ,
154
185
]
155
186
187
+ private static let strictDecodeTable : [ UInt8 ] = [
188
+ /* 00 */ 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 ,
189
+ /* 08 */ 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 ,
190
+ /* 10 */ 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 ,
191
+ /* 18 */ 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 ,
192
+ /* 20 */ 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 ,
193
+ /* 28 */ 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 ,
194
+ /* 30 */ 0x80 , 0x80 , 0x1A , 0x1B , 0x1C , 0x1D , 0x1E , 0x1F ,
195
+ /* 38 */ 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0xC0 , 0x80 , 0x80 ,
196
+ /* 40 */ 0x80 , 0x00 , 0x01 , 0x02 , 0x03 , 0x04 , 0x05 , 0x06 ,
197
+ /* 48 */ 0x07 , 0x08 , 0x09 , 0x0A , 0x0B , 0x0C , 0x0D , 0x0E ,
198
+ /* 50 */ 0x0F , 0x10 , 0x11 , 0x12 , 0x13 , 0x14 , 0x15 , 0x16 ,
199
+ /* 58 */ 0x17 , 0x18 , 0x19 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 ,
200
+ /* 60 */ 0x80 , 0x00 , 0x01 , 0x02 , 0x03 , 0x04 , 0x05 , 0x06 ,
201
+ /* 68 */ 0x07 , 0x08 , 0x09 , 0x0A , 0x0B , 0x0C , 0x0D , 0x0E ,
202
+ /* 60 */ 0x0F , 0x10 , 0x11 , 0x12 , 0x13 , 0x14 , 0x15 , 0x16 ,
203
+ /* 68 */ 0x17 , 0x18 , 0x19 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 ,
204
+
205
+ /* 80 */ 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 ,
206
+ /* 88 */ 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 ,
207
+ /* 90 */ 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 ,
208
+ /* 98 */ 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 ,
209
+ /* A0 */ 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 ,
210
+ /* A8 */ 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 ,
211
+ /* B0 */ 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 ,
212
+ /* B8 */ 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 ,
213
+ /* C0 */ 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 ,
214
+ /* C8 */ 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 ,
215
+ /* D0 */ 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 ,
216
+ /* D8 */ 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 ,
217
+ /* E0 */ 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 ,
218
+ /* E8 */ 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 ,
219
+ /* F0 */ 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 ,
220
+ /* F8 */ 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 ,
221
+ ]
222
+
156
223
private static let encodeTable : [ UInt8 ] = [
157
224
/* 00 */ 0x41 , 0x42 , 0x43 , 0x44 , 0x45 , 0x46 , 0x47 , 0x48 ,
158
225
/* 08 */ 0x49 , 0x4A , 0x4B , 0x4C , 0x4D , 0x4E , 0x4F , 0x50 ,
159
226
/* 10 */ 0x51 , 0x52 , 0x53 , 0x54 , 0x55 , 0x56 , 0x57 , 0x58 ,
160
227
/* 18 */ 0x59 , 0x5A , 0x32 , 0x33 , 0x34 , 0x35 , 0x36 , 0x37 ,
161
228
]
162
229
163
- private static func _decode( from input: UnsafeBufferPointer < UInt8 > , into output: UnsafeMutableBufferPointer < UInt8 > ) throws -> Int {
230
+ /// Decode Base32 assuming there are no null characters
231
+ private static func _strictDecode( from input: UnsafeBufferPointer < UInt8 > , into output: UnsafeMutableBufferPointer < UInt8 > ) throws -> Int {
164
232
guard input. count != 0 else { return 0 }
165
233
166
- var bitsLeft = 0
167
- var buffer : UInt32 = 0
168
234
var outputIndex = 0
235
+ // work out how many blocks can go through the fast path. Last block
236
+ // should be passed to the slow path
237
+ let inputMinusLastBlock = ( input. count - 1 ) & ~ 0x7
169
238
var i = 0
170
- loop: while i < input. count {
239
+ while i < inputMinusLastBlock {
240
+ let v1 = self . strictDecodeTable [ Int ( input [ i] ) ]
241
+ let v2 = self . strictDecodeTable [ Int ( input [ i + 1 ] ) ]
242
+ let v3 = self . strictDecodeTable [ Int ( input [ i + 2 ] ) ]
243
+ let v4 = self . strictDecodeTable [ Int ( input [ i + 3 ] ) ]
244
+ let v5 = self . strictDecodeTable [ Int ( input [ i + 4 ] ) ]
245
+ let v6 = self . strictDecodeTable [ Int ( input [ i + 5 ] ) ]
246
+ let v7 = self . strictDecodeTable [ Int ( input [ i + 6 ] ) ]
247
+ let v8 = self . strictDecodeTable [ Int ( input [ i + 7 ] ) ]
248
+ let vCombined = v1 | v2 | v3 | v4 | v5 | v6 | v7 | v8
249
+ if ( vCombined & ~ 0x1F ) != 0 {
250
+ throw DecodingError . invalidCharacter
251
+ }
252
+ i += 8
253
+ output [ outputIndex] = ( v1 << 3 ) | ( v2 >> 2 )
254
+ output [ outputIndex + 1 ] = ( v2 << 6 ) | ( v3 << 1 ) | ( v4 >> 4 )
255
+ output [ outputIndex + 2 ] = ( v4 << 4 ) | ( v5 >> 1 )
256
+ output [ outputIndex + 3 ] = ( v5 << 7 ) | ( v6 << 2 ) | ( v7 >> 3 )
257
+ output [ outputIndex + 4 ] = ( v7 << 5 ) | v8
258
+ outputIndex += 5
259
+ }
260
+
261
+ return try self . _decode ( from: input [ i... ] , into: output [ outputIndex... ] )
262
+ }
263
+
264
+ /// Decode Base32 with the possibility of null characters or padding
265
+ private static func _decode( from input: UnsafeBufferPointer < UInt8 > . SubSequence , into output: UnsafeMutableBufferPointer < UInt8 > . SubSequence ) throws -> Int {
266
+ guard input. count != 0 else { return output. startIndex }
267
+ var output = output
268
+ var bitsLeft = 0
269
+ var buffer : UInt32 = 0
270
+ var outputIndex = output. startIndex
271
+ var i = input. startIndex
272
+ loop: while i < input. endIndex {
171
273
let index = Int ( input [ i] )
172
274
i += 1
173
275
let v = self . decodeTable [ index]
174
276
switch v {
175
277
case 0x80 :
176
- throw DecodingError . invalidCharacter ( UInt8 ( index ) )
278
+ throw DecodingError . invalidCharacter
177
279
case 0x40 :
178
280
continue
179
281
case 0xC0 :
@@ -191,9 +293,9 @@ extension Base32 {
191
293
}
192
294
}
193
295
// Any characters left should be padding
194
- while i < input. count {
296
+ while i < input. endIndex {
195
297
let index = Int ( input [ i] )
196
- guard self . decodeTable [ index] == 0xC0 else { throw DecodingError . invalidCharacter ( UInt8 ( index ) ) }
298
+ guard self . decodeTable [ index] == 0xC0 else { throw DecodingError . invalidCharacter }
197
299
i += 1
198
300
}
199
301
return outputIndex
0 commit comments