What happens if you use a bigger look up table, to get rid of the substraction, overflow check, index check, etc. Granted, this bigger table will use more cache, which might be worse over-all, but it's worth playing with it.
I also made some other changes:
- Namespace the functions in an
AsciiUtils enum, rather than prefixing them ascii_
- I extracted the magic
99 to a invalidHexCharacter constant
- I made a local variable
x to reference invalidHexCharacter more succinctly, which has the nice benefit of making the real digits stick out more, visually.
enum AsciiUtils {
static let invalidHexCharacter: UInt8 = 99
/// Returns the ASCII value corresponding to the low nibble of `number`, encoded as hex.
static func getHexDigit(for number: UInt8) -> UInt8 {
let table: StaticString = "0123456789ABCDEF"
return table.withUTF8Buffer { table in
table[Int(number & 0x0F)]
}
}
/// Returns the numerical value of a hex digit, or 99 if the character is not a hex digit.
static func parseHexDigit(fromAscii index: UInt8) -> UInt8 {
let x = invalidHexCharacter
let table: ContiguousArray<UInt8> = [
x, x, x, x, x, x, x, x, x, x, // first 30 ASCII characters
x, x, x, x, x, x, x, x, x, x, //
x, x, x, x, x, x, x, x, x, x, //
x, x, x, x, x, x, x, x, x, x,
x, x, x, x, x, x, x, x,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, // numbers 0-9
x, x, x, x, x, x, x, // 7 invalid chars from ':' to '@'
10, 11, 12, 13, 14, 15, // uppercase A-F
x, x, x, x, x, x, x, x, x, x,
x, x, x, x, x, x, x, x, x, x, // 20 invalid chars G-Z
x, x, x, x, x, x, // 6 invalid chars from '[' to '`'
10, 11, 12, 13, 14, 15, // lowercase a-f
// Remaining possible values, grouped 8 per line after the first line, to reach all 256 possible values
x,
x, x, x, x, x, x, x, x,
x, x, x, x, x, x, x, x,
x, x, x, x, x, x, x, x,
x, x, x, x, x, x, x, x,
x, x, x, x, x, x, x, x,
x, x, x, x, x, x, x, x,
x, x, x, x, x, x, x, x,
x, x, x, x, x, x, x, x,
x, x, x, x, x, x, x, x,
x, x, x, x, x, x, x, x,
x, x, x, x, x, x, x, x,
x, x, x, x, x, x, x, x,
x, x, x, x, x, x, x, x,
x, x, x, x, x, x, x, x,
x, x, x, x, x, x, x, x,
x, x, x, x, x, x, x, x,
x, x, x, x, x, x, x, x,
x, x, x, x, x, x, x, x,
x, x, x, x, x, x, x, x,
]
assert(table.count == (UInt8.min ... UInt8.max).count,
"The table wasn't big enough to support all possible UInt8 values! table.count was: \(table.count)")
return table[Int(index)]
}
}