Gotta redesign the whole decoder layer. Managed to sketch a way to tackle with these php scripter fantasies in the MaxMind DB spec on the whiteboard.

Signed-off-by: Adam Rocska <adam.rocska@adams.solutions>
This commit is contained in:
Adam Rocska 2020-05-16 15:09:22 +02:00
parent d236f8ce4e
commit f02cb01293
7 changed files with 171 additions and 0 deletions

View File

@ -32,6 +32,11 @@ let package = Package(
dependencies: ["MaxMindDecoder"],
path: "Sources/Metadata"
),
.target(
name: "Decoder",
dependencies: [],
path: "Sources/Decoder"
),
.target(
name: "DataSection",
@ -75,6 +80,11 @@ let package = Package(
dependencies: ["MaxMindDecoder"]
),
.testTarget(
name: "DecoderTests",
dependencies: ["Decoder"]
),
.testTarget(
name: "DataSectionTests",
dependencies: ["DataSection", "Metadata"]

View File

@ -0,0 +1,43 @@
import Foundation
enum ControlByte {
case pointer(payloadSize: UInt32, strayBits: UInt8)
case utf8String(payloadSize: UInt32)
case double(payloadSize: UInt32)
case bytes(payloadSize: UInt32)
case uInt16(payloadSize: UInt32)
case uInt32(payloadSize: UInt32)
case map(payloadSize: UInt32)
case int32(payloadSize: UInt32)
case uInt64(payloadSize: UInt32)
case uInt128(payloadSize: UInt32)
case array(payloadSize: UInt32)
case dataCacheContainer(payloadSize: UInt32)
case endMarker(payloadSize: UInt32)
case boolean(payloadSize: UInt32)
case float(payloadSize: UInt32)
}
extension ControlByte {
var type: DataType {
switch self {
case .pointer: return DataType.pointer
case .utf8String: return DataType.utf8String
case .double: return DataType.double
case .bytes: return DataType.bytes
case .uInt16: return DataType.uInt16
case .uInt32: return DataType.uInt32
case .map: return DataType.map
case .int32: return DataType.int32
case .uInt64: return DataType.uInt64
case .uInt128: return DataType.uInt128
case .array: return DataType.array
case .dataCacheContainer: return DataType.dataCacheContainer
case .endMarker: return DataType.endMarker
case .boolean: return DataType.boolean
case .float: return DataType.float
}
}
}

View File

@ -0,0 +1,19 @@
import Foundation
enum Data {
case pointer(UInt32)
case utf8String(String)
case double(Double)
case bytes(Foundation.Data)
case uInt16(UInt16)
case uInt32(UInt32)
indirect case map([String: Data])
case int32(Int32)
case uInt64(UInt64)
case uInt128(Foundation.Data)
indirect case array([Data])
indirect case dataCacheContainer([Data])
case endMarker
case boolean(Bool)
case float(Float)
}

View File

@ -0,0 +1,19 @@
import Foundation
enum DataType: UInt8 {
case pointer = 1
case utf8String = 2
case double = 3
case bytes = 4
case uInt16 = 5
case uInt32 = 6
case map = 7
case int32 = 8
case uInt64 = 9
case uInt128 = 10
case array = 11
case dataCacheContainer = 12
case endMarker = 13
case boolean = 14
case float = 15
}

View File

@ -5,6 +5,28 @@ public extension MaxMindDecoder {
private func resolveKey(_ iterator: MaxMindIterator) -> String? {
switch iterator.next() {
case .some(let cb) where cb.type == .pointer:
let resolver = MaxMindPointerResolver(dataSequence: iterator, decoder: self)
// print(resolver.resolve(MaxMindPointer(1)))
print(resolver.resolve(MaxMindPointer(719)))
// print(resolver.resolve(MaxMindPointer(12)))
// print(resolver.resolve(MaxMindPointer(122)))
// print(resolver.resolve(MaxMindPointer(20)))
// print(resolver.resolve(MaxMindPointer(2144)))
// print(resolver.resolve(MaxMindPointer(35)))
let pointer = decode(iterator.next(cb), strayBits: cb.strayBits)
print(pointer)
guard let resolvedValue = resolver.resolve(pointer) else { return nil }
if resolvedValue.controlByte.type == .utf8String {
return resolvedValue.value as! String
}
return nil
case .some(let cb) where cb.type == .utf8String:
return decode(iterator.next(cb))
@ -20,6 +42,13 @@ public extension MaxMindDecoder {
guard let valueControlByte = iterator.next() else { break }
switch valueControlByte.type {
case .pointer:
if valueControlByte.payloadSize == 0 {print("büdös szakmunkás parasztok")}
let resolver = MaxMindPointerResolver(dataSequence: iterator, decoder: self)
let valueBinary = iterator.next(valueControlByte)
let mindPointer = decode(valueBinary, strayBits: valueControlByte.strayBits)
print(mindPointer)
result[key] = resolver.resolve(mindPointer)?.value
case .map, .array:
result[key] = decode(iterator, as: valueControlByte)
default:

View File

@ -0,0 +1,26 @@
import Foundation
class MaxMindPointerResolver {
typealias ResolvedValue = (controlByte: ControlByte, value: Any)
private let dataSequence: PeekableDataSequence
private let decoder: MaxMindDecoder
init(dataSequence: PeekableDataSequence, decoder: MaxMindDecoder) {
self.dataSequence = dataSequence
self.decoder = decoder
}
func resolve(_ pointer: MaxMindPointer) -> ResolvedValue? {
let pointerPosition = Int(pointer)
guard let controlByte = dataSequence.peek(at: pointerPosition) else { return nil }
let controlByteOffset = Int(controlByte.definitionSize)
guard let data = dataSequence.peek(controlByte, at: pointerPosition + controlByteOffset) else { return nil }
return (
controlByte: controlByte,
value: decoder.decode(data, as: controlByte)
)
}
}

View File

@ -0,0 +1,25 @@
import Foundation
import XCTest
@testable import Decoder
class ControlByteTest: XCTestCase {
func testTypeResolution() {
XCTAssertEqual(DataType.pointer, ControlByte.pointer(payloadSize: 123, strayBits: 0b0000_0110).type)
XCTAssertEqual(DataType.utf8String, ControlByte.utf8String(payloadSize: 123).type)
XCTAssertEqual(DataType.double, ControlByte.double(payloadSize: 123).type)
XCTAssertEqual(DataType.bytes, ControlByte.bytes(payloadSize: 123).type)
XCTAssertEqual(DataType.uInt16, ControlByte.uInt16(payloadSize: 123).type)
XCTAssertEqual(DataType.uInt32, ControlByte.uInt32(payloadSize: 123).type)
XCTAssertEqual(DataType.map, ControlByte.map(payloadSize: 123).type)
XCTAssertEqual(DataType.int32, ControlByte.int32(payloadSize: 123).type)
XCTAssertEqual(DataType.uInt64, ControlByte.uInt64(payloadSize: 123).type)
XCTAssertEqual(DataType.uInt128, ControlByte.uInt128(payloadSize: 123).type)
XCTAssertEqual(DataType.array, ControlByte.array(payloadSize: 123).type)
XCTAssertEqual(DataType.dataCacheContainer, ControlByte.dataCacheContainer(payloadSize: 123).type)
XCTAssertEqual(DataType.endMarker, ControlByte.endMarker(payloadSize: 123).type)
XCTAssertEqual(DataType.boolean, ControlByte.boolean(payloadSize: 123).type)
XCTAssertEqual(DataType.float, ControlByte.float(payloadSize: 123).type)
}
}