Skip to content

Commit aea30ad

Browse files
authored
Merge pull request #1173 from spevans/pr_sr_5640
SR-5640: JSONEncoder misrepresents UInt.max
2 parents 5d6e95f + 8d05411 commit aea30ad

File tree

4 files changed

+151
-5
lines changed

4 files changed

+151
-5
lines changed

Foundation/JSONSerialization.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -577,9 +577,12 @@ private struct JSONWriter {
577577
writer(" ")
578578
}
579579
}
580-
580+
581581
//[SR-2151] https://bugs.swift.org/browse/SR-2151
582582
private mutating func _serializationString(for number: NSNumber) -> String {
583+
if !CFNumberIsFloatType(number._cfObject) {
584+
return number.stringValue
585+
}
583586
return CFNumberFormatterCreateStringWithNumber(nil, _numberformatter, number._cfObject)._swiftObject
584587
}
585588
}

Foundation/NSNumber.swift

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ open class NSNumber : NSValue {
256256
fatalError("unsupported CFNumberType: '\(numberType)'")
257257
}
258258
}
259-
259+
260260
deinit {
261261
_CFDeinit(self)
262262
}
@@ -536,13 +536,25 @@ open class NSNumber : NSValue {
536536
}
537537
}
538538

539+
private static let _numberFormatterForNilLocale: CFNumberFormatter = {
540+
let formatter: CFNumberFormatter
541+
formatter = CFNumberFormatterCreate(nil, CFLocaleCopyCurrent(), kCFNumberFormatterNoStyle)
542+
CFNumberFormatterSetProperty(formatter, kCFNumberFormatterMaxFractionDigits, 15._bridgeToObjectiveC())
543+
return formatter
544+
}()
545+
539546
open func description(withLocale locale: Locale?) -> String {
547+
// CFNumberFormatterCreateStringWithNumber() does not like numbers of type
548+
// SInt128Type, as it loses the type when looking it up and treats it as
549+
// an SInt64Type, so special case them.
550+
if _CFNumberGetType2(_cfObject) == kCFNumberSInt128Type {
551+
return String(format: "%@", unsafeBitCast(_cfObject, to: UnsafePointer<CFNumber>.self))
552+
}
553+
540554
let aLocale = locale
541555
let formatter: CFNumberFormatter
542556
if (aLocale == nil) {
543-
formatter = CFNumberFormatterCreate(nil, CFLocaleCopyCurrent(), kCFNumberFormatterNoStyle)
544-
CFNumberFormatterSetProperty(formatter, kCFNumberFormatterMaxFractionDigits, 15._bridgeToObjectiveC())
545-
557+
formatter = NSNumber._numberFormatterForNilLocale
546558
} else {
547559
formatter = CFNumberFormatterCreate(nil, aLocale?._cfObject, kCFNumberFormatterDecimalStyle)
548560
}

TestFoundation/TestJSONEncoder.swift

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,68 @@ class TestJSONEncoder : XCTestCase {
401401
test_codingOf(value: URL(string: "https://swift.org")!, toAndFrom: "\"https://swift.org\"")
402402
}
403403

404+
405+
// UInt and Int
406+
func test_codingOfUIntMinMax() {
407+
408+
let encoder = JSONEncoder()
409+
410+
struct MyValue: Codable {
411+
let intMin:Int = Int.min
412+
let intMax:Int = Int.max
413+
let uintMin:UInt = UInt.min
414+
let uintMax:UInt = UInt.max
415+
}
416+
417+
let myValue = MyValue()
418+
let myDictI: [String:Any] = ["intMin": myValue.intMin, "intMax": myValue.intMax]
419+
let myDictU: [String:Any] = ["uintMin": myValue.uintMin, "uintMax": myValue.uintMax]
420+
let myDict1: [String:Any] = ["intMin": myValue.intMin]
421+
let myDict2: [String:Any] = ["intMax": myValue.intMax]
422+
let myDict3: [String:Any] = ["uintMin": myValue.uintMin]
423+
let myDict4: [String:Any] = ["uintMax": myValue.uintMax]
424+
425+
func compareJSON(_ s1: String, _ s2: String) {
426+
let ss1 = s1.trimmingCharacters(in: CharacterSet(charactersIn: "{}")).split(separator: Character(",")).sorted()
427+
let ss2 = s2.trimmingCharacters(in: CharacterSet(charactersIn: "{}")).split(separator: Character(",")).sorted()
428+
XCTAssertEqual(ss1, ss2)
429+
}
430+
431+
do {
432+
let result = try encoder.encode(myValue)
433+
let r = String(data: result, encoding: .utf8) ?? "nil"
434+
compareJSON(r, "{\"uintMin\":0,\"uintMax\":18446744073709551615,\"intMin\":-9223372036854775808,\"intMax\":9223372036854775807}")
435+
436+
let resultI = try JSONSerialization.data(withJSONObject: myDictI)
437+
let rI = String(data: resultI, encoding: .utf8) ?? "nil"
438+
compareJSON(rI, "{\"intMin\":-9223372036854775808,\"intMax\":9223372036854775807}")
439+
440+
let resultU = try JSONSerialization.data(withJSONObject: myDictU)
441+
let rU = String(data: resultU, encoding: .utf8) ?? "nil"
442+
compareJSON(rU, "{\"uintMax\":18446744073709551615,\"uintMin\":0}")
443+
444+
let result1 = try JSONSerialization.data(withJSONObject: myDict1)
445+
let r1 = String(data: result1, encoding: .utf8) ?? "nil"
446+
XCTAssertEqual(r1, "{\"intMin\":-9223372036854775808}")
447+
448+
let result2 = try JSONSerialization.data(withJSONObject: myDict2)
449+
let r2 = String(data: result2, encoding: .utf8) ?? "nil"
450+
XCTAssertEqual(r2, "{\"intMax\":9223372036854775807}")
451+
452+
let result3 = try JSONSerialization.data(withJSONObject: myDict3)
453+
let r3 = String(data: result3, encoding: .utf8) ?? "nil"
454+
XCTAssertEqual(r3, "{\"uintMin\":0}")
455+
456+
let result4 = try JSONSerialization.data(withJSONObject: myDict4)
457+
let r4 = String(data: result4, encoding: .utf8) ?? "nil"
458+
XCTAssertEqual(r4, "{\"uintMax\":18446744073709551615}")
459+
} catch {
460+
XCTFail(String(describing: error))
461+
}
462+
}
463+
464+
465+
404466
// MARK: - Helper Functions
405467
private var _jsonEmptyDictionary: Data {
406468
return "{}".data(using: .utf8)!
@@ -986,6 +1048,7 @@ extension TestJSONEncoder {
9861048
("test_codingOfUInt64", test_codingOfUInt64),
9871049
("test_codingOfInt", test_codingOfInt),
9881050
("test_codingOfUInt", test_codingOfUInt),
1051+
("test_codingOfUIntMinMax", test_codingOfUIntMinMax),
9891052
("test_codingOfFloat", test_codingOfFloat),
9901053
("test_codingOfDouble", test_codingOfDouble),
9911054
("test_codingOfString", test_codingOfString),

TestFoundation/TestNSNumber.swift

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ class TestNSNumber : XCTestCase {
4343
("test_description", test_description ),
4444
("test_descriptionWithLocale", test_descriptionWithLocale ),
4545
("test_objCType", test_objCType ),
46+
("test_stringValue", test_stringValue),
4647
]
4748
}
4849

@@ -1019,4 +1020,71 @@ class TestNSNumber : XCTestCase {
10191020
XCTAssertEqual("f" /* 0x66 */, objCType(NSNumber(value: Float.greatestFiniteMagnitude)))
10201021
XCTAssertEqual("d" /* 0x64 */, objCType(NSNumber(value: Double.greatestFiniteMagnitude)))
10211022
}
1023+
1024+
func test_stringValue() {
1025+
1026+
if UInt.max == UInt32.max {
1027+
XCTAssertEqual(NSNumber(value: UInt.min).stringValue, "0")
1028+
XCTAssertEqual(NSNumber(value: UInt.min + 1).stringValue, "1")
1029+
XCTAssertEqual(NSNumber(value: UInt.max).stringValue, "4294967295")
1030+
XCTAssertEqual(NSNumber(value: UInt.max - 1).stringValue, "4294967294")
1031+
} else if UInt.max == UInt64.max {
1032+
XCTAssertEqual(NSNumber(value: UInt.min).stringValue, "0")
1033+
XCTAssertEqual(NSNumber(value: UInt.min + 1).stringValue, "1")
1034+
XCTAssertEqual(NSNumber(value: UInt.max).stringValue, "18446744073709551615")
1035+
XCTAssertEqual(NSNumber(value: UInt.max - 1).stringValue, "18446744073709551614")
1036+
}
1037+
1038+
XCTAssertEqual(NSNumber(value: UInt8.min).stringValue, "0")
1039+
XCTAssertEqual(NSNumber(value: UInt8.min + 1).stringValue, "1")
1040+
XCTAssertEqual(NSNumber(value: UInt8.max).stringValue, "255")
1041+
XCTAssertEqual(NSNumber(value: UInt8.max - 1).stringValue, "254")
1042+
1043+
XCTAssertEqual(NSNumber(value: UInt16.min).stringValue, "0")
1044+
XCTAssertEqual(NSNumber(value: UInt16.min + 1).stringValue, "1")
1045+
XCTAssertEqual(NSNumber(value: UInt16.max).stringValue, "65535")
1046+
XCTAssertEqual(NSNumber(value: UInt16.max - 1).stringValue, "65534")
1047+
1048+
XCTAssertEqual(NSNumber(value: UInt32.min).stringValue, "0")
1049+
XCTAssertEqual(NSNumber(value: UInt32.min + 1).stringValue, "1")
1050+
XCTAssertEqual(NSNumber(value: UInt32.max).stringValue, "4294967295")
1051+
XCTAssertEqual(NSNumber(value: UInt32.max - 1).stringValue, "4294967294")
1052+
1053+
XCTAssertEqual(NSNumber(value: UInt64.min).stringValue, "0")
1054+
XCTAssertEqual(NSNumber(value: UInt64.min + 1).stringValue, "1")
1055+
XCTAssertEqual(NSNumber(value: UInt64.max).stringValue, "18446744073709551615")
1056+
XCTAssertEqual(NSNumber(value: UInt64.max - 1).stringValue, "18446744073709551614")
1057+
1058+
if Int.max == Int32.max {
1059+
XCTAssertEqual(NSNumber(value: Int.min).stringValue, "-2147483648")
1060+
XCTAssertEqual(NSNumber(value: Int.min + 1).stringValue, "-2147483647")
1061+
XCTAssertEqual(NSNumber(value: Int.max).stringValue, "2147483647")
1062+
XCTAssertEqual(NSNumber(value: Int.max - 1).stringValue, "2147483646")
1063+
} else if Int.max == Int64.max {
1064+
XCTAssertEqual(NSNumber(value: Int.min).stringValue, "-9223372036854775808")
1065+
XCTAssertEqual(NSNumber(value: Int.min + 1).stringValue, "-9223372036854775807")
1066+
XCTAssertEqual(NSNumber(value: Int.max).stringValue, "9223372036854775807")
1067+
XCTAssertEqual(NSNumber(value: Int.max - 1).stringValue, "9223372036854775806")
1068+
}
1069+
1070+
XCTAssertEqual(NSNumber(value: Int8.min).stringValue, "-128")
1071+
XCTAssertEqual(NSNumber(value: Int8.min + 1).stringValue, "-127")
1072+
XCTAssertEqual(NSNumber(value: Int8.max).stringValue, "127")
1073+
XCTAssertEqual(NSNumber(value: Int8.max - 1).stringValue, "126")
1074+
1075+
XCTAssertEqual(NSNumber(value: Int16.min).stringValue, "-32768")
1076+
XCTAssertEqual(NSNumber(value: Int16.min + 1).stringValue, "-32767")
1077+
XCTAssertEqual(NSNumber(value: Int16.max).stringValue, "32767")
1078+
XCTAssertEqual(NSNumber(value: Int16.max - 1).stringValue, "32766")
1079+
1080+
XCTAssertEqual(NSNumber(value: Int32.min).stringValue, "-2147483648")
1081+
XCTAssertEqual(NSNumber(value: Int32.min + 1).stringValue, "-2147483647")
1082+
XCTAssertEqual(NSNumber(value: Int32.max).stringValue, "2147483647")
1083+
XCTAssertEqual(NSNumber(value: Int32.max - 1).stringValue, "2147483646")
1084+
1085+
XCTAssertEqual(NSNumber(value: Int64.min).stringValue, "-9223372036854775808")
1086+
XCTAssertEqual(NSNumber(value: Int64.min + 1).stringValue, "-9223372036854775807")
1087+
XCTAssertEqual(NSNumber(value: Int64.max).stringValue, "9223372036854775807")
1088+
XCTAssertEqual(NSNumber(value: Int64.max - 1).stringValue, "9223372036854775806")
1089+
}
10221090
}

0 commit comments

Comments
 (0)