Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions Sources/FoundationEssentials/JSON/JSONDecoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -988,17 +988,18 @@ extension JSONDecoderImpl: Decoder {
static private func _slowpath_unwrapFixedWidthInteger<T: FixedWidthInteger>(as type: T.Type, json5: Bool, numberBuffer: BufferView<UInt8>, fullSource: BufferView<UInt8>, digitBeginning: BufferViewIndex<UInt8>, for codingPathNode: _CodingPathNode, _ additionalKey: (some CodingKey)?) throws -> T {
// This is the slow path... If the fast path has failed. For example for "34.0" as an integer, we try to parse as either a Decimal or a Double and then convert back, losslessly.
if let double = Double(prevalidatedBuffer: numberBuffer) {
// T.init(exactly:) guards against non-integer Double(s), but the parser may
// have already transformed the non-integer "1.0000000000000001" into 1, etc.
// Proper lossless behavior should be implemented by the parser.
guard let value = T(exactly: double) else {
throw JSONError.numberIsNotRepresentableInSwift(parsed: String(decoding: numberBuffer, as: UTF8.self))
}

// The distance between Double(s) is >=2 from ±2^53.
// 2^53 may represent either 2^53 or 2^53+1 rounded toward zero.
// This code makes it so you don't get integer A from integer B.
// Proper lossless behavior should be implemented by the parser.
if double.magnitude < Double(sign: .plus, exponent: Double.significandBitCount + 1, significand: 1) {
// T.init(exactly:) guards against non-integer Double(s), but the parser may
// have already transformed the non-integer "1.0000000000000001" into 1, etc.
// Proper lossless behavior should be implemented by the parser.
guard let value = T(exactly: double) else {
throw JSONError.numberIsNotRepresentableInSwift(parsed: String(decoding: numberBuffer, as: UTF8.self))
}
return value
}
}
Expand Down
5 changes: 5 additions & 0 deletions Tests/FoundationEssentialsTests/JSONEncoderTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1459,6 +1459,11 @@ final class JSONEncoderTests : XCTestCase {
_testRoundTrip(of: testValue)
}

func test_decodeLargeDoubleAsInteger() {
let data = try! JSONEncoder().encode(Double.greatestFiniteMagnitude)
XCTAssertThrowsError(try JSONDecoder().decode(UInt64.self, from: data))
}

func test_localeDecimalPolicyIndependence() {
var currentLocale: UnsafeMutablePointer<CChar>? = nil
if let localePtr = setlocale(LC_ALL, nil) {
Expand Down