@@ -196,7 +196,7 @@ extension Decimal {
196
196
decimalSeparator: String . UTF8View ,
197
197
matchEntireString: Bool
198
198
) -> ( result: Decimal ? , processedLength: Int ) {
199
- _decimal ( from: stringView, decimalSeparator: decimalSeparator, matchEntireString: matchEntireString)
199
+ _decimal ( from: stringView, decimalSeparator: decimalSeparator, matchEntireString: matchEntireString) . asOptional
200
200
}
201
201
#endif
202
202
internal func _toString( with locale: Locale ? = nil ) -> String {
@@ -250,11 +250,26 @@ extension Decimal {
250
250
return String ( buffer. reversed ( ) )
251
251
}
252
252
253
- internal static func _decimal(
254
- from stringView: String . UTF8View ,
255
- decimalSeparator: String . UTF8View ,
253
+ internal enum DecimalParseResult {
254
+ case success( Decimal , processedLength: Int )
255
+ case parseFailure
256
+ case overlargeValue
257
+
258
+ var asOptional : ( result: Decimal ? , processedLength: Int ) {
259
+ switch self {
260
+ case let . success( decimal, processedLength) : ( decimal, processedLength: processedLength)
261
+ default : ( nil , processedLength: 0 )
262
+ }
263
+ }
264
+ }
265
+
266
+ @_specialize ( where UTF8Collection == String. UTF8View)
267
+ @_specialize ( where UTF8Collection == BufferView< UInt8 > )
268
+ internal static func _decimal< UTF8Collection: Collection > (
269
+ from utf8View: UTF8Collection ,
270
+ decimalSeparator: String . UTF8View = " . " . utf8,
256
271
matchEntireString: Bool
257
- ) -> ( result : Decimal ? , processedLength : Int ) {
272
+ ) -> DecimalParseResult where UTF8Collection . Element == UTF8 . CodeUnit {
258
273
func multiplyBy10AndAdd(
259
274
_ decimal: Decimal ,
260
275
number: UInt16
@@ -268,47 +283,47 @@ extension Decimal {
268
283
}
269
284
}
270
285
271
- func skipWhiteSpaces( from index: String . UTF8View . Index ) -> String . UTF8View . Index {
286
+ func skipWhiteSpaces( from index: UTF8Collection . Index ) -> UTF8Collection . Index {
272
287
var i = index
273
- while i != stringView . endIndex &&
274
- Character ( utf8Scalar: stringView [ i] ) . isWhitespace {
275
- stringView . formIndex ( after: & i)
288
+ while i != utf8View . endIndex &&
289
+ Character ( utf8Scalar: utf8View [ i] ) . isWhitespace {
290
+ utf8View . formIndex ( after: & i)
276
291
}
277
292
return i
278
293
}
279
294
280
- func stringViewContainsDecimalSeparator( at index: String . UTF8View . Index ) -> Bool {
295
+ func stringViewContainsDecimalSeparator( at index: UTF8Collection . Index ) -> Bool {
281
296
for indexOffset in 0 ..< decimalSeparator. count {
282
- let stringIndex = stringView . index ( index, offsetBy: indexOffset)
297
+ let stringIndex = utf8View . index ( index, offsetBy: indexOffset)
283
298
let decimalIndex = decimalSeparator. index (
284
299
decimalSeparator. startIndex,
285
300
offsetBy: indexOffset
286
301
)
287
- if stringView [ stringIndex] != decimalSeparator [ decimalIndex] {
302
+ if utf8View [ stringIndex] != decimalSeparator [ decimalIndex] {
288
303
return false
289
304
}
290
305
}
291
306
return true
292
307
}
293
308
294
309
var result = Decimal ( )
295
- var index = stringView . startIndex
310
+ var index = utf8View . startIndex
296
311
index = skipWhiteSpaces ( from: index)
297
312
// Get the sign
298
- if index != stringView . endIndex &&
299
- ( stringView [ index] == UInt8 . _plus ||
300
- stringView [ index] == UInt8 . _minus) {
301
- result. _isNegative = ( stringView [ index] == UInt8 . _minus) ? 1 : 0
313
+ if index != utf8View . endIndex &&
314
+ ( utf8View [ index] == UInt8 . _plus ||
315
+ utf8View [ index] == UInt8 . _minus) {
316
+ result. _isNegative = ( utf8View [ index] == UInt8 . _minus) ? 1 : 0
302
317
// Advance over the sign
303
- stringView . formIndex ( after: & index)
318
+ utf8View . formIndex ( after: & index)
304
319
}
305
320
// Build mantissa
306
321
var tooBigToFit = false
307
322
308
- while index != stringView . endIndex,
309
- let digitValue = stringView [ index] . digitValue {
323
+ while index != utf8View . endIndex,
324
+ let digitValue = utf8View [ index] . digitValue {
310
325
defer {
311
- stringView . formIndex ( after: & index)
326
+ utf8View . formIndex ( after: & index)
312
327
}
313
328
// Multiply the value by 10 and add the current digit
314
329
func incrementExponent( _ decimal: inout Decimal ) {
@@ -324,7 +339,7 @@ extension Decimal {
324
339
if tooBigToFit {
325
340
incrementExponent ( & result)
326
341
if result. isNaN {
327
- return ( result : nil , processedLength : 0 )
342
+ return . overlargeValue
328
343
}
329
344
continue
330
345
}
@@ -333,20 +348,20 @@ extension Decimal {
333
348
tooBigToFit = true
334
349
incrementExponent ( & result)
335
350
if result. isNaN {
336
- return ( result : nil , processedLength : 0 )
351
+ return . overlargeValue
337
352
}
338
353
continue
339
354
}
340
355
result = product
341
356
}
342
357
// Get the decimal point
343
- if index != stringView . endIndex && stringViewContainsDecimalSeparator ( at: index) {
344
- stringView . formIndex ( & index, offsetBy: decimalSeparator. count)
358
+ if index != utf8View . endIndex && stringViewContainsDecimalSeparator ( at: index) {
359
+ utf8View . formIndex ( & index, offsetBy: decimalSeparator. count)
345
360
// Continue to build the mantissa
346
- while index != stringView . endIndex,
347
- let digitValue = stringView [ index] . digitValue {
361
+ while index != utf8View . endIndex,
362
+ let digitValue = utf8View [ index] . digitValue {
348
363
defer {
349
- stringView . formIndex ( after: & index)
364
+ utf8View . formIndex ( after: & index)
350
365
}
351
366
guard !tooBigToFit else {
352
367
continue
@@ -360,38 +375,38 @@ extension Decimal {
360
375
// Before decrementing the exponent, we need to check
361
376
// if it's still possible to decrement.
362
377
if result. _exponent == Int8 . min {
363
- return ( result : nil , processedLength : 0 )
378
+ return . overlargeValue
364
379
}
365
380
result. _exponent -= 1
366
381
}
367
382
}
368
383
// Get the exponent if any
369
- if index != stringView . endIndex && ( stringView [ index] == UInt8 . _E || stringView [ index] == UInt8 . _e) {
370
- stringView . formIndex ( after: & index)
384
+ if index != utf8View . endIndex && ( utf8View [ index] == UInt8 . _E || utf8View [ index] == UInt8 . _e) {
385
+ utf8View . formIndex ( after: & index)
371
386
var exponentIsNegative = false
372
387
var exponent = 0
373
388
// Get the exponent sign
374
- if stringView [ index] == UInt8 . _minus || stringView [ index] == UInt8 . _plus {
375
- exponentIsNegative = stringView [ index] == UInt8 . _minus
376
- stringView . formIndex ( after: & index)
389
+ if utf8View [ index] == UInt8 . _minus || utf8View [ index] == UInt8 . _plus {
390
+ exponentIsNegative = utf8View [ index] == UInt8 . _minus
391
+ utf8View . formIndex ( after: & index)
377
392
}
378
393
// Build the exponent
379
- while index != stringView . endIndex,
380
- let digitValue = stringView [ index] . digitValue {
394
+ while index != utf8View . endIndex,
395
+ let digitValue = utf8View [ index] . digitValue {
381
396
exponent = 10 * exponent + digitValue
382
397
if exponent > 2 * Int( Int8 . max) {
383
398
// Too big to fit
384
- return ( result : nil , processedLength : 0 )
399
+ return . overlargeValue
385
400
}
386
- stringView . formIndex ( after: & index)
401
+ utf8View . formIndex ( after: & index)
387
402
}
388
403
if exponentIsNegative {
389
404
exponent = - exponent
390
405
}
391
406
// Check to see if it will fit into the exponent field
392
407
exponent += Int ( result. _exponent)
393
408
if exponent > Int8 . max || exponent < Int8 . min {
394
- return ( result : nil , processedLength : 0 )
409
+ return . overlargeValue
395
410
}
396
411
result. _exponent = Int32 ( exponent)
397
412
}
@@ -401,27 +416,27 @@ extension Decimal {
401
416
if matchEntireString {
402
417
// Trim end spaces
403
418
index = skipWhiteSpaces ( from: index)
404
- guard index == stringView . endIndex else {
419
+ guard index == utf8View . endIndex else {
405
420
// Any unprocessed content means the string
406
421
// contains something not valid
407
- return ( result : nil , processedLength : 0 )
422
+ return . parseFailure
408
423
}
409
424
}
410
- if index == stringView . startIndex {
425
+ if index == utf8View . startIndex {
411
426
// If we weren't able to process any character
412
427
// the entire string isn't a valid decimal
413
- return ( result : nil , processedLength : 0 )
428
+ return . parseFailure
414
429
}
415
430
result. compact ( )
416
- let processedLength = stringView . distance ( from: stringView . startIndex, to: index)
431
+ let processedLength = utf8View . distance ( from: utf8View . startIndex, to: index)
417
432
// if we get to this point, and have NaN,
418
433
// then the input string was probably "-0"
419
434
// or some variation on that, and
420
435
// normalize that to zero.
421
436
if result. isNaN {
422
- return ( result : Decimal ( 0 ) , processedLength: processedLength)
437
+ return . success ( Decimal ( 0 ) , processedLength: processedLength)
423
438
}
424
- return ( result : result, processedLength: processedLength)
439
+ return . success ( result, processedLength: processedLength)
425
440
}
426
441
}
427
442
0 commit comments