Skip to content

Commit

Permalink
Simplify Double parsing logic
Browse files Browse the repository at this point in the history
  • Loading branch information
Joannis committed Sep 27, 2022
1 parent f7228d1 commit ad809db
Show file tree
Hide file tree
Showing 2 changed files with 8 additions and 123 deletions.
127 changes: 4 additions & 123 deletions Sources/IkigaJSON/Core/Bounds.swift
Original file line number Diff line number Diff line change
Expand Up @@ -107,131 +107,12 @@ internal struct Bounds {
/// Falls back to the foundation implementation which makes too many copies for this use case
///
/// Used when the implementation is unsure
func fallback() -> Double? {
let data = Data(bytes: pointer + offset, count: length)

if let string = String(data: data, encoding: .utf8) {
return Double(string)
}

return nil
}

/// If the number is not floating, an integer will be instantiated and converted
if !floating {
guard let int = makeInt(from: pointer) else {
return nil
}

return Double(exactly: int)
}

let end = Int(self.offset) + Int(self.length)
var exponentPow10 = 0
var fullStop = false
var hasExponent = false
var exponentNegative = false
var significand: Int = numericCast(pointer[offset] &- 0x30)
var currentIndex = Int(self.offset &+ 1)

/// Parses the decimal number
loop: while currentIndex < end {
let byte = pointer[currentIndex]
if byte < 0x30 || byte > 0x39 {
defer {
currentIndex = currentIndex &+ 1
}

if byte == .fullStop {
// Starting exponent
fullStop = true
continue loop
} else if byte == .e || byte == .E {
hasExponent = true
continue loop
} else {
break loop
}
}

if hasExponent {
if !exponentNegative {
if byte == .minus {
exponentNegative = true
} else if byte == .plus {
exponentNegative = false
} else {
exponentPow10 = exponentPow10 &- 1
}
} else {
exponentPow10 = exponentPow10 &+ 1
}
} else if fullStop {
exponentPow10 = exponentPow10 &- 1
}

significand = (significand &* 10) &+ numericCast(pointer[currentIndex] &- 0x30)
currentIndex = currentIndex &+ 1

if significand < 0 {
return fallback()
}
let slice = UnsafeBufferPointer(start: pointer + offset, count: length)
if let string = String(bytes: slice, encoding: .utf8) {
return Double(string)
}

func makeExponent() -> Int {
var base = 1

if exponentPow10 < 0 {
for _ in exponentPow10..<0 {
base = base &* 10
}

return -base
} else if exponentPow10 > 0 {
for _ in 0..<exponentPow10 {
base = base &* 10
}
}

return base
}

// If the end of the double is reached
if currentIndex >= end {
let exponent = makeExponent()
if let double = fastDouble(exponent: exponent, significand: Double(significand)) {
return double
}

return fallback()
}

let e = pointer[currentIndex]

// If this is not `e`-notated we're unsure what to expect so we trigger the fallback
// This is necessary, since the end of the double has not been found
guard e == .e || e == .E else {
return fallback()
}

currentIndex = currentIndex &+ 1

// Find the exponent's power
guard let pow10exp = pointer.makeInt(offset: &currentIndex, length: end &- currentIndex) else {
return fallback()
}

// Apply the exponent to the parsed number
exponentPow10 = exponentPow10 &+ pow10exp
let exponent = makeExponent()

// Try the fast double instantiation route
if let double = fastDouble(exponent: exponent, significand: Double(significand)) {
return double
}

// Otherwise use the fallback implementation
return fallback()
return nil
}

internal func makeInt(from pointer: UnsafePointer<UInt8>) -> Int? {
Expand Down
4 changes: 4 additions & 0 deletions Tests/IkigaJSONTests/JSONTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1118,6 +1118,10 @@ final class IkigaJSONTests: XCTestCase {
XCTAssertEqual(object["key"] as? Double, 3.14)
object["key"] = nil

object["key"] = -3.14
XCTAssertEqual(object["key"] as? Double, -3.14)
object["key"] = nil

object["key"] = NSNull()
XCTAssert(object["key"] is NSNull)
object["key"] = nil
Expand Down

0 comments on commit ad809db

Please sign in to comment.