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:
parent
d236f8ce4e
commit
f02cb01293
|
@ -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"]
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue