aboutsummaryrefslogtreecommitdiff
path: root/Sources/WmapParser/Utils.swift
diff options
context:
space:
mode:
Diffstat (limited to 'Sources/WmapParser/Utils.swift')
-rw-r--r--Sources/WmapParser/Utils.swift136
1 files changed, 136 insertions, 0 deletions
diff --git a/Sources/WmapParser/Utils.swift b/Sources/WmapParser/Utils.swift
new file mode 100644
index 0000000..8bea13a
--- /dev/null
+++ b/Sources/WmapParser/Utils.swift
@@ -0,0 +1,136 @@
+/// Check if a byte is a digit
+@inline(__always)
+func isDigit(_ byte: UInt8) -> Bool {
+ byte >= kZero && byte <= kNine
+}
+
+/// Looks for x and y separated by a comma.
+func parseCoordinates<T: Collection>(_ bytes: T, _ start: Int, _ end: Int) -> (
+ MapPoint, Int
+)?
+where T.Element == UInt8, T.Index == Int {
+ var index = skipWhitespace(bytes, end, start)
+
+ guard let (xCoordinate, nextIndex) = parseSingleCoordinate(bytes, index, end) else {
+ return nil
+ }
+ index = nextIndex
+
+ guard expectComma(bytes, &index, end) else { return nil }
+
+ guard let (yCoordinate, nextIndex) = parseSingleCoordinate(bytes, index, end) else {
+ return nil
+ }
+ index = nextIndex
+
+ guard expectClosingParen(bytes, &index, end) else { return nil }
+
+ return (MapPoint(xCoordinate: xCoordinate, yCoordinate: yCoordinate), index)
+}
+
+/// Checks if a candidate for a coordinate is a digit, and parses it.
+@inline(__always)
+func parseSingleCoordinate<T: Collection>(_ bytes: T, _ start: Int, _ end: Int) -> (Double, Int)?
+where T.Element == UInt8, T.Index == Int {
+ var index = start
+ while index < end && (isDigit(bytes[index]) || bytes[index] == 46) { index += 1 }
+ guard index > start else { return nil }
+ guard let value = parseDouble(bytes, start, index) else { return nil }
+ return (value, index)
+}
+
+/// Advances the index until we find a comma.
+@inline(__always)
+func expectComma<T: Collection>(_ bytes: T, _ index: inout Int, _ end: Int) -> Bool
+where T.Element == UInt8, T.Index == Int {
+ index = skipWhitespace(bytes, end, index)
+ guard index < end && bytes[index] == kComma else { return false }
+ index += 1
+ index = skipWhitespace(bytes, end, index)
+ return true
+}
+
+/// Advances the index until we find a closing parenthesis
+@inline(__always)
+func expectClosingParen<T: Collection>(_ bytes: T, _ index: inout Int, _ end: Int) -> Bool
+where T.Element == UInt8, T.Index == Int {
+ index = skipWhitespace(bytes, end, index)
+ guard index < end && bytes[index] == kCloseParenthesis else { return false }
+ index += 1
+ return true
+}
+
+/// Parses a double
+@inline(__always)
+func parseDouble<T: Collection>(_ bytes: T, _ start: Int, _ end: Int) -> Double?
+where T.Element == UInt8, T.Index == Int {
+ var index = skipWhitespace(bytes, end, start)
+
+ let numStart = index
+ while index < end && (isDigit(bytes[index]) || bytes[index] == kPeriod) { index += 1 }
+
+ guard index > numStart else { return nil }
+
+ // Why not String(data:encoding:)? This is much faster.
+ // swiftlint:disable:next optional_data_string_conversion
+ let str = String(decoding: bytes[numStart..<index], as: UTF8.self)
+ return Double(str)
+}
+
+/// Advances an index by looking for whitespace left-to-right
+func skipWhitespace<T: Collection>(_ bytes: T, _ end: Int, _ start: Int) -> Int
+where T.Element == UInt8, T.Index == Int {
+ var index = start
+ while index < end && (bytes[index] == kSpace || bytes[index] == kTab) { index += 1 }
+ return index
+}
+
+/// Decreases an index by looking for whitespace right-to-left
+func shaveWhitespace<T: Collection>(_ bytes: T, _ start: Int, _ end: Int) -> Int
+where T.Element == UInt8, T.Index == Int {
+ var index = end
+ while index > start && (bytes[index - 1] == kSpace || bytes[index - 1] == kTab) { index -= 1 }
+ return index
+}
+
+/// Given the bytes buffer, finds the next non-empty line.
+@inline(__always)
+func extractLine<T: Collection>(_ bytes: T, _ index: inout Int, _ length: Int) -> (
+ Int, Int
+)?
+where T.Element == UInt8, T.Index == Int {
+ while index < length && (bytes[index] == kNewline || bytes[index] == kCarriageReturn) {
+ index += 1
+ }
+ let lineStart = index
+
+ while index < length && bytes[index] != kNewline && bytes[index] != kCarriageReturn { index += 1 }
+ if lineStart == index { return nil }
+
+ var start = lineStart
+ var end = index
+
+ start = skipWhitespace(bytes, end, start)
+ end = shaveWhitespace(bytes, start, end)
+
+ if start == end { return nil }
+
+ return (start, end)
+}
+
+/// Trims a byte collection and returns a string
+@inline(__always)
+func extractTrimmedString<T: Collection>(_ bytes: T, _ start: Int, _ end: Int) -> String?
+where T.Element == UInt8, T.Index == Int {
+ var trimStart = start
+ var trimEnd = end
+
+ trimStart = skipWhitespace(bytes, trimEnd, trimStart)
+ trimEnd = shaveWhitespace(bytes, trimStart, trimEnd)
+
+ guard trimStart < trimEnd else { return nil }
+
+ // Why not String(data:encoding:)? This is much faster.
+ // swiftlint:disable:next optional_data_string_conversion
+ return String(decoding: bytes[trimStart..<trimEnd], as: UTF8.self)
+}