Skip to content

Commit

Permalink
Allow decoding integer values to floating point types for better inte…
Browse files Browse the repository at this point in the history
…roperability with applications written in languages like JavaScript.

Resolves #1.
  • Loading branch information
fumoboy007 committed Dec 22, 2023
1 parent f3f382f commit 0913fae
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,26 @@
extension BinaryFloatingPoint {
init?(exactly messagePackValue: DecodedMessagePackValue) {
switch messagePackValue {
case .signedInteger(let value):
// Some languages like JavaScript do not allow the developer to specify whether a number is a floating point
// or integer value. A MessagePack encoder for such a language may choose to encode the number as an integer
// when possible--even if the developer knows that the intended type of the value should be a floating point
// type.
//
// Therefore, we should allow decoding integer values to floating point types for better interoperability
// with applications written in such languages.
guard let value = Self(exactly: value) else {
return nil
}
self = value

case .unsignedInteger(let value):
// See above comment for the `signedInteger` case.
guard let value = Self(exactly: value) else {
return nil
}
self = value

case .float32(let value):
if let value = Self(exactly: value) {
self = value
Expand All @@ -44,8 +64,6 @@ extension BinaryFloatingPoint {
case .invalid,
.nil,
.boolean,
.signedInteger,
.unsignedInteger,
.string,
.binary,
.array,
Expand Down
35 changes: 35 additions & 0 deletions Tests/MessagePackTests/MessagePackDecoderTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,8 @@ class MessagePackDecoderTests: XCTestCase {
for value in valuesToTest {
try test(value)
}

try testIntegerValues(decodeAs: Float32.self)
}

func testFloat32_nan() throws {
Expand Down Expand Up @@ -197,6 +199,8 @@ class MessagePackDecoderTests: XCTestCase {
for value in valuesToTest {
try test(value)
}

try testIntegerValues(decodeAs: Float64.self)
}

func testFloat64_nan() throws {
Expand All @@ -212,6 +216,37 @@ class MessagePackDecoderTests: XCTestCase {
XCTAssertEqual(decodedValue.significandBitPattern, value.significandBitPattern)
}

private func testIntegerValues<FloatingPointType>(
decodeAs floatingPointType: FloatingPointType.Type
) throws where FloatingPointType: BinaryFloatingPoint & Decodable {
try testValues(of: Int.self, decodeAs: floatingPointType)
try testValues(of: Int8.self, decodeAs: floatingPointType)
try testValues(of: Int16.self, decodeAs: floatingPointType)
try testValues(of: Int32.self, decodeAs: floatingPointType)
try testValues(of: Int64.self, decodeAs: floatingPointType)
try testValues(of: UInt.self, decodeAs: floatingPointType)
try testValues(of: UInt8.self, decodeAs: floatingPointType)
try testValues(of: UInt16.self, decodeAs: floatingPointType)
try testValues(of: UInt32.self, decodeAs: floatingPointType)
try testValues(of: UInt64.self, decodeAs: floatingPointType)
}

private func testValues<IntegerType, FloatingPointType>(
of integerType: IntegerType.Type,
decodeAs floatingPointType: FloatingPointType.Type
) throws where IntegerType: BinaryInteger, FloatingPointType: BinaryFloatingPoint & Decodable {
// Only test integers up to 16 bits so that the test does not take forever.
let valuesToTest = (Int16.min...Int16.max).compactMap { IntegerType(exactly: $0) }

for value in valuesToTest {
guard let floatingPointValue = FloatingPointType(exactly: value) else {
continue
}

try test(value, encodesAndThenDecodesTo: floatingPointValue)
}
}

func testString() throws {
let valuesToTest: [String] = [
"",
Expand Down

0 comments on commit 0913fae

Please sign in to comment.