Skip to content

Commit 28db28b

Browse files
authored
SWIFT-154: Int should encode as Int32 or Int64 as needed (#91)
1 parent 45dbeb5 commit 28db28b

File tree

2 files changed

+33
-13
lines changed

2 files changed

+33
-13
lines changed

Sources/MongoSwift/BSON/BsonValue.swift

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -377,19 +377,18 @@ extension Double: BsonValue {
377377
}
378378
}
379379

380-
/// An extension of `Int` to represent the BSON Int32 type.
381-
/// While the bitwidth of Int is machine-dependent, we assume for simplicity
382-
/// that it is always 32 bits. Use `Int64` if 64 bits are needed.
380+
/// An extension of `Int` to represent the BSON Int32 or Int64 type.
381+
/// The `Int` will be encoded as an Int32 if possible, or an Int64 if necessary.
383382
extension Int: BsonValue {
384383
public var bsonType: BsonType { return .int32 }
385384
public func encode(to data: UnsafeMutablePointer<bson_t>, forKey key: String) throws {
386-
guard let int32 = Int32(exactly: self) else {
387-
throw MongoError.bsonEncodeError(message:
388-
"`Int` value \(self) does not fit in an `Int32`. Use an `Int64` instead")
385+
if let int32 = Int32(exactly: self) {
386+
return try int32.encode(to: data, forKey: key)
389387
}
390-
if !bson_append_int32(data, key, Int32(key.count), int32) {
391-
throw bsonEncodeError(value: self, forKey: key)
388+
if let int64 = Int64(exactly: self) {
389+
return try int64.encode(to: data, forKey: key)
392390
}
391+
throw MongoError.bsonEncodeError(message: "`Int` value \(self) could not be encoded as `Int32` or `Int64`")
393392
}
394393

395394
public static func from(iter: inout bson_iter_t) -> BsonValue {

Tests/MongoSwiftTests/DocumentTests.swift

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ final class DocumentTests: XCTestCase {
3636
("testEquatable", testEquatable),
3737
("testRawBSON", testRawBSON),
3838
("testValueBehavior", testValueBehavior),
39-
("testInvalidInt", testInvalidInt),
39+
("testIntEncodesAsInt32OrInt64", testIntEncodesAsInt32OrInt64),
4040
("testBSONCorpus", testBSONCorpus),
4141
("testMerge", testMerge)
4242
]
@@ -198,11 +198,32 @@ final class DocumentTests: XCTestCase {
198198
XCTAssertNotEqual(doc1, doc2)
199199
}
200200

201-
func testInvalidInt() {
202-
let doc1 = Document()
203-
let v = Int(Int32.max) + 1
204-
expect(try v.encode(to: doc1.data, forKey: "x")).to(throwError())
201+
func testIntEncodesAsInt32OrInt64() {
202+
/* Skip this test on 32-bit platforms. Use MemoryLayout instead of
203+
* Int.bitWidth to avoid a compiler warning.
204+
* See: https://forums.swift.org/t/how-can-i-condition-on-the-size-of-int/9080/4 */
205+
if MemoryLayout<Int>.size == 4 {
206+
return
207+
}
208+
209+
let int32min_sub1 = Int64(Int32.min) - Int64(1)
210+
let int32max_add1 = Int64(Int32.max) + Int64(1)
211+
212+
var doc: Document = [
213+
"int32min": Int(Int32.min),
214+
"int32max": Int(Int32.max),
215+
"int32min-1": Int(int32min_sub1),
216+
"int32max+1": Int(int32max_add1),
217+
"int64min": Int(Int64.min),
218+
"int64max": Int(Int64.max)
219+
]
205220

221+
expect(doc["int32min"] as? Int).to(equal(Int(Int32.min)))
222+
expect(doc["int32max"] as? Int).to(equal(Int(Int32.max)))
223+
expect(doc["int32min-1"] as? Int64).to(equal(int32min_sub1))
224+
expect(doc["int32max+1"] as? Int64).to(equal(int32max_add1))
225+
expect(doc["int64min"] as? Int64).to(equal(Int64.min))
226+
expect(doc["int64max"] as? Int64).to(equal(Int64.max))
206227
}
207228

208229
// swiftlint:disable:next cyclomatic_complexity

0 commit comments

Comments
 (0)