-
-
Notifications
You must be signed in to change notification settings - Fork 727
/
Copy pathNSNumber.swift
99 lines (93 loc) · 3.62 KB
/
NSNumber.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
#if !os(Linux)
import Foundation
private let integerRoundingBehavior = NSDecimalNumberHandler(
roundingMode: .plain,
scale: 0,
raiseOnExactness: false,
raiseOnOverflow: false,
raiseOnUnderflow: false,
raiseOnDivideByZero: false)
/// NSNumber adopts DatabaseValueConvertible
extension NSNumber: DatabaseValueConvertible {
/// A database value.
///
/// If the number is an integer `NSDecimalNumber`, returns an INTEGER
/// database value.
///
/// Otherwise, returns an INTEGER or REAL database value, according to the
/// value stored in the `NSNumber`.
public var databaseValue: DatabaseValue {
// Don't lose precision: store integers that fits in Int64 as Int64
if let decimal = self as? NSDecimalNumber,
decimal == decimal.rounding(accordingToBehavior: integerRoundingBehavior), // integer
decimal.compare(NSDecimalNumber(value: Int64.max)) != .orderedDescending, // decimal <= Int64.max
decimal.compare(NSDecimalNumber(value: Int64.min)) != .orderedAscending // decimal >= Int64.min
{
return int64Value.databaseValue
}
switch String(cString: objCType) {
case "c":
return Int64(int8Value).databaseValue
case "C":
return Int64(uint8Value).databaseValue
case "s":
return Int64(int16Value).databaseValue
case "S":
return Int64(uint16Value).databaseValue
case "i":
return Int64(int32Value).databaseValue
case "I":
return Int64(uint32Value).databaseValue
case "l":
return Int64(intValue).databaseValue
case "L":
let uint = uintValue
GRDBPrecondition(
UInt64(uint) <= UInt64(Int64.max),
"could not convert \(uint) to an Int64 that can be stored in the database")
return Int64(uint).databaseValue
case "q":
return Int64(int64Value).databaseValue
case "Q":
let uint64 = uint64Value
GRDBPrecondition(
uint64 <= UInt64(Int64.max),
"could not convert \(uint64) to an Int64 that can be stored in the database")
return Int64(uint64).databaseValue
case "f":
return Double(floatValue).databaseValue
case "d":
return doubleValue.databaseValue
case "B":
return boolValue.databaseValue
case let objCType:
// Assume a GRDB bug: there is no point throwing any error.
fatalError("DatabaseValueConvertible: Unsupported NSNumber type: \(objCType)")
}
}
/// Returns a `NSNumber` from the specified database value.
///
/// If the database value is an integer or a double, returns an `NSNumber`
/// initialized from this number.
///
/// If the database value is a string, returns an `NSDecimalNumber` parsed
/// with the `en_US_POSIX` locale.
///
/// Otherwise, returns nil.
public static func fromDatabaseValue(_ dbValue: DatabaseValue) -> Self? {
switch dbValue.storage {
case .int64(let int64):
return self.init(value: int64)
case .double(let double):
return self.init(value: double)
case let .string(string):
// Must match Decimal.fromDatabaseValue(_:)
guard let decimal = Decimal(string: string, locale: posixLocale) else { return nil }
return NSDecimalNumber(decimal: decimal) as? Self
default:
return nil
}
}
}
private let posixLocale = Locale(identifier: "en_US_POSIX")
#endif