Skip to content

Commit 544238d

Browse files
authored
Merge pull request #1722 from spevans/pr_sr_7195
2 parents 6dd329b + ad620ff commit 544238d

File tree

2 files changed

+50
-80
lines changed

2 files changed

+50
-80
lines changed

Foundation/JSONSerialization.swift

Lines changed: 25 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -301,14 +301,6 @@ private struct JSONWriter {
301301
let pretty: Bool
302302
let sortedKeys: Bool
303303
let writer: (String?) -> Void
304-
305-
private lazy var _numberformatter: CFNumberFormatter = {
306-
let formatter: CFNumberFormatter
307-
formatter = CFNumberFormatterCreate(nil, CFLocaleCopyCurrent(), kCFNumberFormatterNoStyle)
308-
CFNumberFormatterSetProperty(formatter, kCFNumberFormatterMaxFractionDigits, NSNumber(value: 15))
309-
CFNumberFormatterSetFormat(formatter, "0.###############"._cfObject)
310-
return formatter
311-
}()
312304

313305
init(pretty: Bool = false, sortedKeys: Bool = false, writer: @escaping (String?) -> Void) {
314306
self.pretty = pretty
@@ -334,75 +326,49 @@ private struct JSONWriter {
334326
case let str as String:
335327
try serializeString(str)
336328
case let boolValue as Bool:
337-
serializeBool(boolValue)
329+
writer(boolValue.description)
338330
case let num as Int:
339-
serializeInteger(value: num)
331+
writer(num.description)
340332
case let num as Int8:
341-
serializeInteger(value: num)
333+
writer(num.description)
342334
case let num as Int16:
343-
serializeInteger(value: num)
335+
writer(num.description)
344336
case let num as Int32:
345-
serializeInteger(value: num)
337+
writer(num.description)
346338
case let num as Int64:
347-
serializeInteger(value: num)
339+
writer(num.description)
348340
case let num as UInt:
349-
serializeInteger(value: num)
341+
writer(num.description)
350342
case let num as UInt8:
351-
serializeInteger(value: num)
343+
writer(num.description)
352344
case let num as UInt16:
353-
serializeInteger(value: num)
345+
writer(num.description)
354346
case let num as UInt32:
355-
serializeInteger(value: num)
347+
writer(num.description)
356348
case let num as UInt64:
357-
serializeInteger(value: num)
349+
writer(num.description)
358350
case let array as Array<Any?>:
359351
try serializeArray(array)
360352
case let dict as Dictionary<AnyHashable, Any?>:
361353
try serializeDictionary(dict)
362354
case let num as Float:
363-
try serializeNumber(NSNumber(value: num))
355+
try serializeFloat(num)
364356
case let num as Double:
365-
try serializeNumber(NSNumber(value: num))
357+
try serializeFloat(num)
366358
case let num as Decimal:
367359
writer(num.description)
368360
case let num as NSDecimalNumber:
369361
writer(num.description)
370362
case is NSNull:
371363
try serializeNull()
372364
case _ where __SwiftValue.store(obj) is NSNumber:
373-
try serializeNumber(__SwiftValue.store(obj) as! NSNumber)
365+
let num = __SwiftValue.store(obj) as! NSNumber
366+
writer(num.description)
374367
default:
375368
throw NSError(domain: NSCocoaErrorDomain, code: CocoaError.propertyListReadCorrupt.rawValue, userInfo: ["NSDebugDescription" : "Invalid object cannot be serialized"])
376369
}
377370
}
378371

379-
private func serializeInteger<T: UnsignedInteger>(value: T, isNegative: Bool = false) {
380-
let maxIntLength = 22 // 20 digits in UInt64 + optional sign + trailing '\0'
381-
let asciiZero: CChar = 0x30 // ASCII '0' == 0x30
382-
let asciiMinus: CChar = 0x2d // ASCII '-' == 0x2d
383-
384-
var number = UInt64(value)
385-
var buffer = Array<CChar>(repeating: 0, count: maxIntLength)
386-
var pos = maxIntLength - 1
387-
388-
repeat {
389-
pos -= 1
390-
buffer[pos] = asciiZero + CChar(number % 10)
391-
number /= 10
392-
} while number != 0
393-
394-
if isNegative {
395-
pos -= 1
396-
buffer[pos] = asciiMinus
397-
}
398-
let output = String(cString: Array(buffer.suffix(from: pos)))
399-
writer(output)
400-
}
401-
402-
private func serializeInteger<T: SignedInteger>(value: T) {
403-
serializeInteger(value: UInt64(value.magnitude), isNegative: value < 0)
404-
}
405-
406372
func serializeString(_ str: String) throws {
407373
writer("\"")
408374
for scalar in str.unicodeScalars {
@@ -434,37 +400,24 @@ private struct JSONWriter {
434400
writer("\"")
435401
}
436402

437-
func serializeBool(_ bool: Bool) {
438-
switch bool {
439-
case true:
440-
writer("true")
441-
case false:
442-
writer("false")
403+
private func serializeFloat<T: FloatingPoint & LosslessStringConvertible>(_ num: T) throws {
404+
guard num.isFinite else {
405+
throw NSError(domain: NSCocoaErrorDomain, code: CocoaError.propertyListReadCorrupt.rawValue, userInfo: ["NSDebugDescription" : "Invalid number value (\(num)) in JSON write"])
443406
}
407+
var str = num.description
408+
if str.hasSuffix(".0") {
409+
str.removeLast(2)
410+
}
411+
writer(str)
444412
}
445413

446414
mutating func serializeNumber(_ num: NSNumber) throws {
447415
if CFNumberIsFloatType(num._cfObject) {
448-
let dv = num.doubleValue
449-
if !dv.isFinite {
450-
let value: String
451-
if dv.isNaN {
452-
value = "NaN"
453-
} else if dv.isInfinite {
454-
value = "infinite"
455-
} else {
456-
value = String(dv)
457-
}
458-
459-
throw NSError(domain: NSCocoaErrorDomain, code: CocoaError.propertyListReadCorrupt.rawValue, userInfo: ["NSDebugDescription" : "Invalid number value (\(value)) in JSON write"])
460-
}
461-
462-
let string = CFNumberFormatterCreateStringWithNumber(nil, _numberformatter, num._cfObject)._swiftObject
463-
writer(string)
416+
try serializeFloat(num.doubleValue)
464417
} else {
465418
switch num._cfTypeID {
466419
case CFBooleanGetTypeID():
467-
serializeBool(num.boolValue)
420+
writer(num.boolValue.description)
468421
default:
469422
writer(num.stringValue)
470423
}

TestFoundation/TestJSONSerialization.swift

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1232,18 +1232,35 @@ extension TestJSONSerialization {
12321232
}
12331233

12341234
func test_serialize_Float() {
1235-
XCTAssertEqual(try trySerialize([-Float.leastNonzeroMagnitude, Float.leastNonzeroMagnitude]), "[-0,0]")
1236-
XCTAssertEqual(try trySerialize([-Float.greatestFiniteMagnitude]), "[-340282346638529000000000000000000000000]")
1237-
XCTAssertEqual(try trySerialize([Float.greatestFiniteMagnitude]), "[340282346638529000000000000000000000000]")
1238-
XCTAssertEqual(try trySerialize([Float(-1), Float.leastNonzeroMagnitude, Float(1)]), "[-1,0,1]")
1235+
XCTAssertEqual(try trySerialize([-Float.leastNonzeroMagnitude, Float.leastNonzeroMagnitude]), "[-1e-45,1e-45]")
1236+
XCTAssertEqual(try trySerialize([-Float.greatestFiniteMagnitude]), "[-3.4028235e+38]")
1237+
XCTAssertEqual(try trySerialize([Float.greatestFiniteMagnitude]), "[3.4028235e+38]")
1238+
XCTAssertEqual(try trySerialize([Float(-1), Float.leastNonzeroMagnitude, Float(1)]), "[-1,1e-45,1]")
12391239
}
12401240

12411241
func test_serialize_Double() {
1242-
XCTAssertEqual(try trySerialize([-Double.leastNonzeroMagnitude, Double.leastNonzeroMagnitude]), "[-0,0]")
1243-
XCTAssertEqual(try trySerialize([-Double.leastNormalMagnitude, Double.leastNormalMagnitude]), "[-0,0]")
1244-
XCTAssertEqual(try trySerialize([-Double.greatestFiniteMagnitude]), "[-179769313486232000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000]")
1245-
XCTAssertEqual(try trySerialize([Double.greatestFiniteMagnitude]), "[179769313486232000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000]")
1242+
XCTAssertEqual(try trySerialize([-Double.leastNonzeroMagnitude, Double.leastNonzeroMagnitude]), "[-5e-324,5e-324]")
1243+
XCTAssertEqual(try trySerialize([-Double.leastNormalMagnitude, Double.leastNormalMagnitude]), "[-2.2250738585072014e-308,2.2250738585072014e-308]")
1244+
XCTAssertEqual(try trySerialize([-Double.greatestFiniteMagnitude]), "[-1.7976931348623157e+308]")
1245+
XCTAssertEqual(try trySerialize([Double.greatestFiniteMagnitude]), "[1.7976931348623157e+308]")
12461246
XCTAssertEqual(try trySerialize([Double(-1.0), Double(1.0)]), "[-1,1]")
1247+
1248+
// Test round-tripping Double values
1249+
let value1 = 7.7087009966199993
1250+
let value2 = 7.7087009966200002
1251+
let dict1 = ["value": value1]
1252+
let dict2 = ["value": value2]
1253+
let jsonData1 = try! JSONSerialization.data(withJSONObject: dict1)
1254+
let jsonData2 = try! JSONSerialization.data(withJSONObject: dict2)
1255+
let jsonString1 = String(decoding: jsonData1, as: UTF8.self)
1256+
let jsonString2 = String(decoding: jsonData2, as: UTF8.self)
1257+
1258+
XCTAssertEqual(jsonString1, "{\"value\":7.708700996619999}")
1259+
XCTAssertEqual(jsonString2, "{\"value\":7.70870099662}")
1260+
let decodedDict1 = try! JSONSerialization.jsonObject(with: jsonData1) as! [String : Double]
1261+
let decodedDict2 = try! JSONSerialization.jsonObject(with: jsonData2) as! [String : Double]
1262+
XCTAssertEqual(decodedDict1["value"], value1)
1263+
XCTAssertEqual(decodedDict2["value"], value2)
12471264
}
12481265

12491266
func test_serialize_Decimal() {

0 commit comments

Comments
 (0)