Skip to content

Commit 519d466

Browse files
authored
Ability to decode from dec/hex string (#127)
1 parent d013a2b commit 519d466

File tree

2 files changed

+71
-19
lines changed

2 files changed

+71
-19
lines changed

Sources/Codable.swift

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -102,28 +102,42 @@ extension Array where Element: FixedWidthInteger {
102102

103103
extension BigInt: Codable {
104104
public init(from decoder: Decoder) throws {
105-
var container = try decoder.unkeyedContainer()
105+
if let container = try? decoder.singleValueContainer(), let stringValue = try? container.decode(String.self) {
106+
if stringValue.hasPrefix("0x") || stringValue.hasPrefix("0X") {
107+
guard let bigUInt = BigUInt(stringValue.dropFirst(2), radix: 16) else {
108+
throw DecodingError.dataCorruptedError(in: container, debugDescription: "Invalid hexadecimal BigInt string")
109+
}
110+
self.init(sign: .plus, magnitude: bigUInt)
111+
} else {
112+
guard let bigInt = BigInt(stringValue) else {
113+
throw DecodingError.dataCorruptedError(in: container, debugDescription: "Invalid decimal BigInt string")
114+
}
115+
self = bigInt
116+
}
117+
} else {
118+
var container = try decoder.unkeyedContainer()
106119

107-
// Decode sign
108-
let sign: BigInt.Sign
109-
switch try container.decode(String.self) {
110-
case "+":
111-
sign = .plus
112-
case "-":
113-
sign = .minus
114-
default:
115-
throw DecodingError.dataCorrupted(.init(codingPath: container.codingPath,
116-
debugDescription: "Invalid big integer sign"))
117-
}
120+
// Decode sign
121+
let sign: BigInt.Sign
122+
switch try container.decode(String.self) {
123+
case "+":
124+
sign = .plus
125+
case "-":
126+
sign = .minus
127+
default:
128+
throw DecodingError.dataCorrupted(.init(codingPath: container.codingPath,
129+
debugDescription: "Invalid big integer sign"))
130+
}
118131

119-
// Decode magnitude
120-
let words = try [UInt](count: container.count?.advanced(by: -1)) { () -> UInt64? in
121-
guard !container.isAtEnd else { return nil }
122-
return try container.decode(UInt64.self)
123-
}
124-
let magnitude = BigUInt(words: words)
132+
// Decode magnitude
133+
let words = try [UInt](count: container.count?.advanced(by: -1)) { () -> UInt64? in
134+
guard !container.isAtEnd else { return nil }
135+
return try container.decode(UInt64.self)
136+
}
137+
let magnitude = BigUInt(words: words)
125138

126-
self.init(sign: sign, magnitude: magnitude)
139+
self.init(sign: sign, magnitude: magnitude)
140+
}
127141
}
128142

129143
public func encode(to encoder: Encoder) throws {

Tests/BigIntTests/BigIntTests.swift

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -663,6 +663,44 @@ class BigIntTests: XCTestCase {
663663
XCTAssertEqual(context.debugDescription, "Invalid big integer sign")
664664
}
665665
}
666+
667+
func testDecodableString() {
668+
func test(_ a: BigInt, _ v: String? = nil, file: StaticString = #file, line: UInt = #line) {
669+
do {
670+
let json = try JSONEncoder().encode(v ?? a.description)
671+
let b = try JSONDecoder().decode(BigInt.self, from: json)
672+
XCTAssertEqual(a, b, file: file, line: line)
673+
} catch let error {
674+
XCTFail("Error thrown: \(error.localizedDescription)", file: file, line: line)
675+
}
676+
}
677+
678+
test(1, "1")
679+
test(1, "+1")
680+
test(-1, "-1")
681+
test(0, "+0")
682+
test(0, "-0")
683+
test(15, "0xf")
684+
test(15, "0Xf")
685+
test(15, "0x0f")
686+
test(BigInt(1) << 64)
687+
test(-BigInt(1) << 64)
688+
}
689+
690+
func testDecodableStringError() {
691+
func test(_ v: String, _ m: String) {
692+
XCTAssertThrowsError(try JSONDecoder().decode(BigInt.self, from: try! JSONEncoder().encode(v))) { error in
693+
guard let error = error as? DecodingError else { XCTFail("Expected a decoding error"); return }
694+
guard case .dataCorrupted(let context) = error else { XCTFail("Expected a dataCorrupted error"); return }
695+
XCTAssertEqual(m, context.debugDescription)
696+
}
697+
}
698+
699+
test("124q", "Invalid decimal BigInt string")
700+
test("-124q", "Invalid decimal BigInt string")
701+
test("0xXYZ", "Invalid hexadecimal BigInt string")
702+
}
703+
666704

667705
func testConversionToData() {
668706
func test(_ b: BigInt, _ d: Array<UInt8>, file: StaticString = #file, line: UInt = #line) {

0 commit comments

Comments
 (0)