Ok, the repo is somewhat exploded at this point. BUT IT WORKS! Now I only gotta provide proper tests instead of the trashy ones, put together the reader & serializer, and represent this maxmind php fantasy crap with some nice Structs. And then the saga is over for now.
Signed-off-by: Adam Rocska <adam.rocska@adams.solutions>
This commit is contained in:
parent
d7387c8845
commit
c7c3ab44db
|
@ -17,20 +17,14 @@ let package = Package(
|
|||
],
|
||||
dependencies: [],
|
||||
targets: [
|
||||
.target(
|
||||
name: "MaxMindDecoder",
|
||||
dependencies: [],
|
||||
path: "Sources/MaxMindDecoder"
|
||||
),
|
||||
.target(
|
||||
name: "Index",
|
||||
dependencies: ["MaxMindDecoder", "Metadata"],
|
||||
dependencies: ["Decoder", "Metadata"],
|
||||
path: "Sources/Index"
|
||||
),
|
||||
.target(
|
||||
name: "Metadata",
|
||||
dependencies: ["MaxMindDecoder"],
|
||||
path: "Sources/Metadata"
|
||||
dependencies: ["Decoder"]
|
||||
),
|
||||
.target(
|
||||
name: "Decoder",
|
||||
|
@ -39,13 +33,13 @@ let package = Package(
|
|||
),
|
||||
.target(
|
||||
name: "DataSection",
|
||||
dependencies: ["MaxMindDecoder", "Metadata"],
|
||||
dependencies: ["Decoder", "Metadata"],
|
||||
path: "Sources/DataSection"
|
||||
),
|
||||
|
||||
.target(
|
||||
name: "MaxMindDBReader",
|
||||
dependencies: ["Index", "Metadata", "MaxMindDecoder"],
|
||||
dependencies: ["Index", "Metadata", "Decoder"],
|
||||
path: "Sources/MaxMindDBReader"
|
||||
),
|
||||
.target(
|
||||
|
@ -71,12 +65,7 @@ let package = Package(
|
|||
|
||||
.testTarget(
|
||||
name: "MetadataTests",
|
||||
dependencies: ["Metadata"]
|
||||
),
|
||||
|
||||
.testTarget(
|
||||
name: "MaxMindDecoderTests",
|
||||
dependencies: ["MaxMindDecoder"]
|
||||
dependencies: ["Metadata", "Decoder"]
|
||||
),
|
||||
|
||||
.testTarget(
|
||||
|
|
|
@ -1,18 +1,16 @@
|
|||
import Foundation
|
||||
import Metadata
|
||||
import MaxMindDecoder
|
||||
import Decoder
|
||||
|
||||
public class InMemoryDataSection: DataSection {
|
||||
|
||||
public static let separator = Data(count: 16)
|
||||
|
||||
public let metadata: Metadata
|
||||
private let iterator: MaxMindIterator
|
||||
private let decoder: MaxMindDecoder
|
||||
private let decoder: Decoder
|
||||
|
||||
init(metadata: Metadata, iterator: MaxMindIterator, decoder: MaxMindDecoder) {
|
||||
init(metadata: Metadata, decoder: Decoder) {
|
||||
self.metadata = metadata
|
||||
self.iterator = iterator
|
||||
self.decoder = decoder
|
||||
}
|
||||
|
||||
|
@ -49,16 +47,14 @@ public class InMemoryDataSection: DataSection {
|
|||
|
||||
self.init(
|
||||
metadata: metadata,
|
||||
iterator: MaxMindIterator(dataSectionBinary)!,
|
||||
decoder: MaxMindDecoder(inputEndianness: .big)
|
||||
decoder: Decoder(dataSectionBinary)
|
||||
)
|
||||
}
|
||||
|
||||
public func lookup(pointer: Int) -> [String: Any]? {
|
||||
iterator.seek(to: pointer)
|
||||
guard let mapControlByte = iterator.next() else { return nil }
|
||||
if mapControlByte.type != .map { return nil }
|
||||
return decoder.decode(iterator, size: Int(mapControlByte.payloadSize)) as [String: Any]
|
||||
public func lookup(pointer: Int) -> [String: Payload]? {
|
||||
guard let payload = decoder.read(at: pointer, resolvePointers: true) else { return nil }
|
||||
guard case let Payload.map(result) = payload else { return nil }
|
||||
return result
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import Foundation
|
||||
|
||||
internal extension Data {
|
||||
public extension Data {
|
||||
|
||||
init(_ string: String) { self.init(string.utf8) }
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ public class Decoder {
|
|||
private let data: Data
|
||||
private let controlByteInterpreter: ControlByteInterpreter
|
||||
private let payloadInterpreter: PayloadInterpreter
|
||||
private let sourceEndianness: Endianness = .big
|
||||
|
||||
init(
|
||||
data: Data,
|
||||
|
@ -23,6 +24,30 @@ public class Decoder {
|
|||
self.payloadInterpreter = payloadInterpreter
|
||||
}
|
||||
|
||||
public convenience init(_ data: Data) {
|
||||
self.init(
|
||||
data: data,
|
||||
controlByteInterpreter: ControlByteInterpreter(
|
||||
typeResolver: resolveType,
|
||||
payloadSizeResolver: resolvePayloadSize,
|
||||
definitionSizeResolver: resolveDefinitionSize
|
||||
),
|
||||
payloadInterpreter: PayloadInterpreter(
|
||||
interpretArray: interpretArray,
|
||||
interpretDataCacheContainer: interpretDataCacheContainer,
|
||||
interpretDouble: interpretDouble,
|
||||
interpretFloat: interpretFloat,
|
||||
interpretInt32: interpretInt32,
|
||||
interpretMap: interpretMap,
|
||||
interpretPointer: interpretPointer,
|
||||
interpretUInt16: interpretUInt16,
|
||||
interpretUInt32: interpretUInt32,
|
||||
interpretUInt64: interpretUInt64,
|
||||
interpretUtf8String: interpretUtf8String
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private func rangeOfData(offset: Int, count: Int) -> Range<Data.Index> {
|
||||
let sliceStart = data.index(
|
||||
data.startIndex,
|
||||
|
@ -39,12 +64,12 @@ public class Decoder {
|
|||
return Range(uncheckedBounds: (lower: sliceStart, upper: sliceEnd))
|
||||
}
|
||||
|
||||
func read(at controlByteOffset: Int, resolvePointers: Bool = true) -> Output? {
|
||||
internal func read(at controlByteOffset: Int, resolvePointers: Bool = true) -> Output? {
|
||||
if controlByteOffset >= data.count { return nil }
|
||||
let controlByteCandidate = data.subdata(in: rangeOfData(offset: controlByteOffset, count: 5))
|
||||
guard let controlByteResult = controlByteInterpreter.interpret(
|
||||
bytes: controlByteCandidate,
|
||||
sourceEndianness: .big
|
||||
sourceEndianness: sourceEndianness
|
||||
) else {
|
||||
return nil
|
||||
}
|
||||
|
@ -64,7 +89,7 @@ public class Decoder {
|
|||
input: (
|
||||
bytes: payloadBytes,
|
||||
controlByte: controlByteResult.controlByte,
|
||||
sourceEndianness: .big,
|
||||
sourceEndianness: sourceEndianness,
|
||||
controlStart: controlByteOffset,
|
||||
payloadStart: payloadStart
|
||||
),
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import Foundation
|
||||
|
||||
internal extension Numeric {
|
||||
public extension Numeric {
|
||||
|
||||
init(_ data: Data, sourceEndianness: Endianness) {
|
||||
var value: Self = .zero
|
||||
|
|
|
@ -17,3 +17,26 @@ public enum Payload: Equatable {
|
|||
case boolean(Bool)
|
||||
case float(Float)
|
||||
}
|
||||
|
||||
public extension Payload {
|
||||
func unwrap<T>() -> T? {
|
||||
switch self {
|
||||
case .pointer(let value) where value is T: return (value as! T)
|
||||
case .utf8String(let value) where value is T: return (value as! T)
|
||||
case .double(let value) where value is T: return (value as! T)
|
||||
case .bytes(let value) where value is T: return (value as! T)
|
||||
case .uInt16(let value) where value is T: return (value as! T)
|
||||
case .uInt32(let value) where value is T: return (value as! T)
|
||||
case .map(let value) where value is T: return (value as! T)
|
||||
case .int32(let value) where value is T: return (value as! T)
|
||||
case .uInt64(let value) where value is T: return (value as! T)
|
||||
case .uInt128(let value) where value is T: return (value as! T)
|
||||
case .array(let value) where value is T: return (value as! T)
|
||||
case .dataCacheContainer(let value) where value is T: return (value as! T)
|
||||
case .boolean(let value) where value is T: return (value as! T)
|
||||
case .float(let value) where value is T: return (value as! T)
|
||||
default: return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -10,86 +10,93 @@ class PayloadInterpreter {
|
|||
payloadStart: Data.Index
|
||||
)
|
||||
|
||||
// These function signatures are a hallmark of how the retarded php scripter designed database flaws are piled all together.
|
||||
private let interpretArray: (UInt32, Decoder, Data.Index, Bool) -> InterpretationResult?
|
||||
private let interpretDataCacheContainer: (UInt32, Decoder, Data.Index, Bool) -> InterpretationResult?
|
||||
private let interpretDouble: (Data, Endianness) -> Payload?
|
||||
private let interpretFloat: (Data, Endianness) -> Payload?
|
||||
private let interpretInt32: (Data, Endianness) -> Payload?
|
||||
private let interpretMap: (UInt32, Decoder, Data.Index, Bool) -> InterpretationResult?
|
||||
private let interpretPointer: (Data, UInt32, UInt8, Endianness, Bool, Decoder) -> Payload?
|
||||
private let interpretUInt16: (Data, Endianness) -> Payload?
|
||||
private let interpretUInt32: (Data, Endianness) -> Payload?
|
||||
private let interpretUInt64: (Data, Endianness) -> Payload?
|
||||
private let interpretUtf8String: (Data) -> Payload?
|
||||
|
||||
typealias InterpretationResult = (payload: Payload, definitionSize: UInt32)
|
||||
|
||||
init(
|
||||
interpretArray: @escaping (UInt32, Decoder, Data.Index, Bool) -> InterpretationResult?,
|
||||
interpretDataCacheContainer: @escaping (UInt32, Decoder, Data.Index, Bool) -> InterpretationResult?,
|
||||
interpretDouble: @escaping (Data, Endianness) -> Payload?,
|
||||
interpretFloat: @escaping (Data, Endianness) -> Payload?,
|
||||
interpretInt32: @escaping (Data, Endianness) -> Payload?,
|
||||
interpretMap: @escaping (UInt32, Decoder, Data.Index, Bool) -> InterpretationResult?,
|
||||
interpretPointer: @escaping (Data, UInt32, UInt8, Endianness, Bool, Decoder) -> Payload?,
|
||||
interpretUInt16: @escaping (Data, Endianness) -> Payload?,
|
||||
interpretUInt32: @escaping (Data, Endianness) -> Payload?,
|
||||
interpretUInt64: @escaping (Data, Endianness) -> Payload?,
|
||||
interpretUtf8String: @escaping (Data) -> Payload?
|
||||
) {
|
||||
self.interpretArray = interpretArray
|
||||
self.interpretDataCacheContainer = interpretDataCacheContainer
|
||||
self.interpretDouble = interpretDouble
|
||||
self.interpretFloat = interpretFloat
|
||||
self.interpretInt32 = interpretInt32
|
||||
self.interpretMap = interpretMap
|
||||
self.interpretPointer = interpretPointer
|
||||
self.interpretUInt16 = interpretUInt16
|
||||
self.interpretUInt32 = interpretUInt32
|
||||
self.interpretUInt64 = interpretUInt64
|
||||
self.interpretUtf8String = interpretUtf8String
|
||||
}
|
||||
|
||||
func interpret(input: Input, using decoder: Decoder, resolvePointers: Bool) -> InterpretationResult? {
|
||||
switch input.controlByte {
|
||||
case .pointer(let payloadSize, let strayBits):
|
||||
guard let payload = interpretPointer(
|
||||
bytes: input.bytes,
|
||||
payloadSize: payloadSize,
|
||||
strayBits: strayBits,
|
||||
sourceEndianness: input.sourceEndianness,
|
||||
resolvePointers: resolvePointers,
|
||||
decoder: decoder
|
||||
input.bytes,
|
||||
payloadSize,
|
||||
strayBits,
|
||||
input.sourceEndianness,
|
||||
resolvePointers,
|
||||
decoder
|
||||
) else { return nil }
|
||||
return (payload, payloadSize)
|
||||
case .utf8String(let payloadSize):
|
||||
guard let payload = interpretUtf8String(bytes: input.bytes) else { return nil }
|
||||
guard let payload = interpretUtf8String(input.bytes) else { return nil }
|
||||
return (payload, payloadSize)
|
||||
case .double(let payloadSize):
|
||||
guard let payload = interpretDouble(
|
||||
bytes: input.bytes,
|
||||
sourceEndianness: input.sourceEndianness
|
||||
) else { return nil }
|
||||
guard let payload = interpretDouble(input.bytes, input.sourceEndianness) else { return nil }
|
||||
return (payload, payloadSize)
|
||||
case .bytes(let payloadSize):
|
||||
return (Payload.bytes(input.bytes), payloadSize)
|
||||
case .uInt16(let payloadSize):
|
||||
guard let payload = interpretUInt16(
|
||||
bytes: input.bytes,
|
||||
sourceEndianness: input.sourceEndianness
|
||||
) else { return nil }
|
||||
guard let payload = interpretUInt16(input.bytes, input.sourceEndianness) else { return nil }
|
||||
return (payload, payloadSize)
|
||||
case .uInt32(let payloadSize):
|
||||
guard let payload = interpretUInt32(bytes: input.bytes, sourceEndianness: input.sourceEndianness) else {
|
||||
return nil
|
||||
}
|
||||
guard let payload = interpretUInt32(input.bytes, input.sourceEndianness) else { return nil }
|
||||
return (payload, payloadSize)
|
||||
case .map(let entryCount):
|
||||
return interpretMap(
|
||||
entryCount: entryCount,
|
||||
decoder: decoder,
|
||||
payloadStart: input.payloadStart,
|
||||
resolvePointers: resolvePointers
|
||||
)
|
||||
return interpretMap(entryCount, decoder, input.payloadStart, resolvePointers)
|
||||
case .int32(let payloadSize):
|
||||
guard let payload = interpretInt32(
|
||||
bytes: input.bytes,
|
||||
sourceEndianness: input.sourceEndianness
|
||||
) else { return nil }
|
||||
guard let payload = interpretInt32(input.bytes, input.sourceEndianness) else { return nil }
|
||||
return (payload, payloadSize)
|
||||
case .uInt64(let payloadSize):
|
||||
guard let payload = interpretUInt64(
|
||||
bytes: input.bytes,
|
||||
sourceEndianness: input.sourceEndianness
|
||||
) else { return nil }
|
||||
guard let payload = interpretUInt64(input.bytes, input.sourceEndianness) else { return nil }
|
||||
return (payload, payloadSize)
|
||||
case .uInt128(let payloadSize):
|
||||
return (Payload.uInt128(input.bytes), payloadSize)
|
||||
case .array(let entryCount):
|
||||
return interpretArray(
|
||||
entryCount: entryCount,
|
||||
decoder: decoder,
|
||||
payloadStart: input.payloadStart,
|
||||
resolvePointers: resolvePointers
|
||||
)
|
||||
return interpretArray(entryCount, decoder, input.payloadStart, resolvePointers)
|
||||
case .dataCacheContainer(let entryCount):
|
||||
return interpretDataCacheContainer(
|
||||
entryCount: entryCount,
|
||||
decoder: decoder,
|
||||
payloadStart: input.payloadStart,
|
||||
resolvePointers: resolvePointers
|
||||
)
|
||||
return interpretDataCacheContainer(entryCount, decoder, input.payloadStart, resolvePointers)
|
||||
case .endMarker:
|
||||
return (Payload.endMarker, 0)
|
||||
case .boolean(let value):
|
||||
return (Payload.boolean(value), 0)
|
||||
case .float(let payloadSize):
|
||||
guard let payload = interpretFloat(
|
||||
bytes: input.bytes,
|
||||
sourceEndianness: input.sourceEndianness
|
||||
) else { return nil }
|
||||
guard let payload = interpretFloat(input.bytes, input.sourceEndianness) else { return nil }
|
||||
return (payload, payloadSize)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import Foundation
|
||||
import MaxMindDecoder
|
||||
import Decoder
|
||||
|
||||
struct Node<Record>: Equatable where Record: UnsignedInteger, Record: FixedWidthInteger {
|
||||
|
||||
|
@ -43,10 +43,9 @@ struct Node<Record>: Equatable where Record: UnsignedInteger, Record: FixedWidth
|
|||
rightData = rightNibble + data.subdata(in: rightRange)
|
||||
}
|
||||
|
||||
let decoder = MaxMindDecoder(inputEndianness: .big)
|
||||
self.init(
|
||||
left: decoder.decode(leftData) as Record,
|
||||
right: decoder.decode(rightData) as Record
|
||||
left: Record.init(leftData.padded(for: Record.self, as: .big), sourceEndianness: .big),
|
||||
right: Record.init(rightData.padded(for: Record.self, as: .big), sourceEndianness: .big)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import Foundation
|
||||
import MaxMindDecoder
|
||||
import Index
|
||||
import Metadata
|
||||
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
import Foundation
|
||||
|
||||
protocol AlternatingIterator {
|
||||
|
||||
var isExhausted: Bool { get }
|
||||
|
||||
func rewind()
|
||||
|
||||
func seek(to: Int)
|
||||
|
||||
func next() -> ControlByte?
|
||||
|
||||
func next(_ controlByte: ControlByte) -> Data
|
||||
|
||||
}
|
|
@ -1,93 +0,0 @@
|
|||
import Foundation
|
||||
|
||||
public struct ControlByte: Equatable {
|
||||
|
||||
public let type: DataType
|
||||
public let payloadSize: UInt32
|
||||
var strayBits: UInt8 {
|
||||
get {
|
||||
precondition(type == DataType.pointer)
|
||||
return definition.first! & 0b0000_0111
|
||||
}
|
||||
}
|
||||
let definitionSize: UInt8
|
||||
let definition: Data
|
||||
|
||||
private init(type: DataType, payloadSize: UInt32, definitionSize: UInt8, definition: Data) {
|
||||
self.type = type
|
||||
self.payloadSize = payloadSize
|
||||
self.definitionSize = definitionSize
|
||||
self.definition = definition
|
||||
}
|
||||
|
||||
init?(bytes: Data) {
|
||||
if bytes.count == 0 || bytes.count > 5 { return nil }
|
||||
|
||||
let firstByte = bytes.first!
|
||||
let typeDefinitionOnFirstByte = firstByte &>> 5
|
||||
let isExtendedType = typeDefinitionOnFirstByte == 0b0000_0000
|
||||
|
||||
let payloadSize: UInt32
|
||||
let definitionSize: UInt8
|
||||
|
||||
guard let type = isExtendedType
|
||||
? DataType(rawValue: bytes[bytes.index(after: bytes.startIndex)] + 7)
|
||||
: DataType(rawValue: typeDefinitionOnFirstByte)
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let payloadSizeDefinition = firstByte & 0b0001_1111
|
||||
// Thank this logic to the php scripters that designed the database.
|
||||
// TODO : Try to refactor at some point somehow this pile of 💩
|
||||
if type == DataType.pointer {
|
||||
definitionSize = 1
|
||||
payloadSize = UInt32(payloadSizeDefinition &>> 3) + 1
|
||||
} else if payloadSizeDefinition < 29 {
|
||||
payloadSize = UInt32(payloadSizeDefinition)
|
||||
definitionSize = isExtendedType ? 2 : 1
|
||||
} else {
|
||||
let numberOfAdditionalBytesToRead = Int(payloadSizeDefinition & 0b0000_0011)
|
||||
let lastIndexOfBytes = bytes.index(before: bytes.endIndex)
|
||||
let sliceFrom = bytes.index(
|
||||
bytes.startIndex,
|
||||
offsetBy: isExtendedType ? 2 : 1,
|
||||
limitedBy: lastIndexOfBytes
|
||||
) ?? lastIndexOfBytes
|
||||
let sliceTo = bytes.index(
|
||||
sliceFrom,
|
||||
offsetBy: numberOfAdditionalBytesToRead,
|
||||
limitedBy: lastIndexOfBytes
|
||||
) ?? lastIndexOfBytes
|
||||
let bytesAfterTypeSpecifyingBytes = bytes[sliceFrom...sliceTo]
|
||||
let payloadSizeWholeBytes: Data = bytesAfterTypeSpecifyingBytes + Data(
|
||||
count: 4 - bytesAfterTypeSpecifyingBytes.count
|
||||
)
|
||||
|
||||
let val = (0..<numberOfAdditionalBytesToRead)
|
||||
.map({ Int($0) })
|
||||
.reduce(UInt32(28)) { previous, byteCount in
|
||||
precondition(byteCount <= 4)
|
||||
let payloadSizeBase = Data(repeating: 0b1111_1111, count: byteCount) +
|
||||
Data(count: 4 - byteCount)
|
||||
return (previous + 1) + payloadSizeBase.withUnsafeBytes { $0.load(as: UInt32.self) }
|
||||
}
|
||||
|
||||
payloadSize = UInt32(val) + payloadSizeWholeBytes.withUnsafeBytes { $0.load(as: UInt32.self) }
|
||||
definitionSize = (isExtendedType ? 2 : 1) + payloadSizeDefinition & 0b0000_0011
|
||||
}
|
||||
|
||||
let definitionRange = Range(uncheckedBounds: (
|
||||
lower: bytes.startIndex,
|
||||
upper: bytes.index(bytes.startIndex, offsetBy: Int(definitionSize))
|
||||
))
|
||||
|
||||
self.init(
|
||||
type: type,
|
||||
payloadSize: payloadSize,
|
||||
definitionSize: definitionSize,
|
||||
definition: bytes.subdata(in: definitionRange)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
import Foundation
|
||||
|
||||
public 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
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
import Foundation
|
||||
|
||||
class Decoder {
|
||||
|
||||
public enum Endianness { case big, little }
|
||||
|
||||
let input: Endianness
|
||||
|
||||
public init(inputEndianness: Endianness) { self.input = inputEndianness }
|
||||
|
||||
func decode(_ input: InputData) -> OutputData {
|
||||
switch input {
|
||||
case .pointer(let bytes, let strayBits):
|
||||
return decodeAsPointer(bytes, strayBits: strayBits)
|
||||
case .utf8String(let bytes):
|
||||
return decodeAsString(bytes)
|
||||
case .double(let bytes):
|
||||
return decodeAsDouble(bytes: bytes)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
import Foundation
|
||||
|
||||
extension Decoder {
|
||||
|
||||
func decodeAsDouble(bytes: Data) -> OutputData {
|
||||
precondition(bytes.count == MemoryLayout<Double>.size, "Only 64bit data can be decoded as Double.")
|
||||
return OutputData.double(5)
|
||||
}
|
||||
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
import Foundation
|
||||
|
||||
extension Decoder {
|
||||
|
||||
func decodeAsPointer(_ data: Data, strayBits: UInt8) -> OutputData {
|
||||
let dataSize = data.count
|
||||
precondition(dataSize <= 4 && dataSize >= 1, "Pointer size must be at least 1 byte & at most 4. Got \(dataSize)")
|
||||
|
||||
var pointerBinary: Data
|
||||
if dataSize < 4 {
|
||||
pointerBinary = Data(count: MemoryLayout<UInt32>.size - dataSize - 1) +
|
||||
Data([strayBits]) +
|
||||
data
|
||||
} else {
|
||||
pointerBinary = data
|
||||
}
|
||||
|
||||
let pointerBase = input == .big
|
||||
? UnsafeRawPointer(&pointerBinary).load(as: UInt32.self).bigEndian
|
||||
: UnsafeRawPointer(&pointerBinary).load(as: UInt32.self).littleEndian
|
||||
|
||||
if dataSize == 2 { return OutputData.pointer(pointerBase + 2048) }
|
||||
if dataSize == 3 { return OutputData.pointer(pointerBase + 526336) }
|
||||
return OutputData.pointer(pointerBase)
|
||||
}
|
||||
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
import Foundation
|
||||
|
||||
extension Decoder {
|
||||
|
||||
func decodeAsString(_ data: Data) -> OutputData {
|
||||
return OutputData.utf8String(String(data: data, encoding: .utf8) ?? "")
|
||||
}
|
||||
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
import Foundation
|
||||
|
||||
enum InputData {
|
||||
case pointer(bytes: Data, strayBits: UInt8)
|
||||
case utf8String(bytes: Data)
|
||||
case double(bytes: Data)
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
import Foundation
|
||||
|
||||
public class MaxMindDecoder {
|
||||
|
||||
public enum Endianness { case big, little }
|
||||
|
||||
let input: Endianness
|
||||
|
||||
public init(inputEndianness: Endianness) { self.input = inputEndianness }
|
||||
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
import Foundation
|
||||
|
||||
public extension MaxMindDecoder {
|
||||
|
||||
// TODO : Create tests for this piece of 💩
|
||||
func decode(_ iterator: MaxMindIterator, as controlByte: ControlByte) -> Any {
|
||||
switch controlByte.type {
|
||||
case .map:
|
||||
return decode(iterator, size: Int(controlByte.payloadSize)) as [String: Any]
|
||||
case .array:
|
||||
return decode(iterator, size: Int(controlByte.payloadSize)) as [Any]
|
||||
default:
|
||||
preconditionFailure("Iterator based control byte decoding can only be done on sequential types.")
|
||||
}
|
||||
}
|
||||
|
||||
// TODO : Finish the implementation & test of this piece of 💩
|
||||
func decode(_ data: Data, as controlByte: ControlByte) -> Any {
|
||||
switch controlByte.type {
|
||||
case .pointer:
|
||||
return 0
|
||||
case .utf8String:
|
||||
return 0
|
||||
case .double:
|
||||
// TODO
|
||||
return 0
|
||||
case .bytes:
|
||||
return data
|
||||
case .uInt16:
|
||||
return decode(data) as UInt16
|
||||
case .uInt32:
|
||||
return decode(data) as UInt32
|
||||
case .map:
|
||||
return decode(data, size: Int(controlByte.payloadSize)) as [String: Any]
|
||||
case .int32:
|
||||
return decode(data) as Int32
|
||||
case .uInt64:
|
||||
return decode(data) as UInt64
|
||||
case .uInt128:
|
||||
// TODO
|
||||
return 0
|
||||
case .array:
|
||||
return decode(data, size: Int(controlByte.payloadSize)) as [Any]
|
||||
case .dataCacheContainer:
|
||||
// TODO
|
||||
return 0
|
||||
case .endMarker:
|
||||
// TODO
|
||||
return 0
|
||||
case .boolean:
|
||||
return controlByte.payloadSize == 1
|
||||
case .float:
|
||||
// TODO
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
import Foundation
|
||||
|
||||
public extension MaxMindDecoder {
|
||||
|
||||
func decode(_ data: Data, size: Int) -> [Any] {
|
||||
guard let iterator = MaxMindIterator(data) else { return [] }
|
||||
return decode(iterator, size: size)
|
||||
}
|
||||
|
||||
func decode(_ iterator: MaxMindIterator, size: Int) -> [Any] {
|
||||
var result: [Any] = []
|
||||
for _ in 0..<size {
|
||||
guard let controlByte = iterator.next() else { break }
|
||||
let binary = iterator.next(controlByte)
|
||||
result.append(decode(binary, as: controlByte))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
import Foundation
|
||||
|
||||
public extension MaxMindDecoder {
|
||||
|
||||
private func resolveKey(_ iterator: MaxMindIterator) -> String? {
|
||||
switch iterator.next() {
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func decode(_ iterator: MaxMindIterator, size: Int) -> [String: Any] {
|
||||
var result: [String: Any] = [:]
|
||||
for _ in 0..<size {
|
||||
guard let key: String = resolveKey(iterator) else { break }
|
||||
|
||||
guard let valueControlByte = iterator.next() else { break }
|
||||
switch valueControlByte.type {
|
||||
case .map, .array:
|
||||
result[key] = decode(iterator, as: valueControlByte)
|
||||
default:
|
||||
let valueBinary = iterator.next(valueControlByte)
|
||||
result[key] = decode(valueBinary, as: valueControlByte)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func decode(_ data: Data, size: Int) -> [String: Any] {
|
||||
guard let iterator = MaxMindIterator(data) else { return [:] }
|
||||
return decode(iterator, size: size)
|
||||
}
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
import Foundation
|
||||
|
||||
public extension MaxMindDecoder {
|
||||
|
||||
private func getLeadingByte(_ data: Data) -> Data.Element? {
|
||||
return input == .big ? data.first : data.last
|
||||
}
|
||||
|
||||
private func isNegative(_ data: Data) -> Bool {
|
||||
guard let leadingByte = getLeadingByte(data) else { return false }
|
||||
return (leadingByte & 0b1000_0000) == 0b1000_0000
|
||||
}
|
||||
|
||||
private func padded<T>(_ data: Data) -> T where T: FixedWidthInteger {
|
||||
let padBytes = Data(
|
||||
repeating: T.isSigned && isNegative(data) ? 0b1111_1111 : 0b0000_0000,
|
||||
count: MemoryLayout<T>.size - data.count
|
||||
)
|
||||
var wellSizedData: Data = input == .big ? padBytes + data : data + padBytes
|
||||
return UnsafeRawPointer(&wellSizedData).load(as: T.self)
|
||||
}
|
||||
|
||||
private func truncated<T>(_ data: Data) -> T where T: FixedWidthInteger {
|
||||
let bounds: (lower: Range<Data.Index>.Bound, upper: Range<Data.Index>.Bound)
|
||||
if input == .big {
|
||||
bounds = (
|
||||
lower: data.index(
|
||||
data.endIndex,
|
||||
offsetBy: -MemoryLayout<T>.size,
|
||||
limitedBy: data.startIndex
|
||||
) ?? data.startIndex,
|
||||
upper: data.endIndex
|
||||
)
|
||||
} else {
|
||||
bounds = (
|
||||
lower: data.startIndex,
|
||||
upper: data.index(
|
||||
data.startIndex,
|
||||
offsetBy: MemoryLayout<T>.size,
|
||||
limitedBy: data.endIndex
|
||||
) ?? data.endIndex
|
||||
)
|
||||
}
|
||||
var wellSizedData: Data = data.subdata(in: Range(uncheckedBounds: bounds))
|
||||
return UnsafeRawPointer(&wellSizedData).load(as: T.self)
|
||||
}
|
||||
|
||||
private func unpack<T>(_ data: Data) -> T where T: FixedWidthInteger {
|
||||
let strayBytes = MemoryLayout<T>.size - data.count
|
||||
if strayBytes > 0 { return padded(data) }
|
||||
if strayBytes < 0 { return truncated(data) }
|
||||
var wellSizedData: Data = data
|
||||
return UnsafeRawPointer(&wellSizedData).load(as: T.self)
|
||||
}
|
||||
|
||||
func decode<T>(_ data: Data) -> T where T: FixedWidthInteger {
|
||||
let unpacked: T = unpack(data)
|
||||
return input == .big
|
||||
? unpacked.bigEndian
|
||||
: unpacked.littleEndian
|
||||
}
|
||||
|
||||
}
|
|
@ -1,87 +0,0 @@
|
|||
import Foundation
|
||||
|
||||
public class MaxMindIterator: PeekableDataSequence, AlternatingIterator {
|
||||
private let data: Data
|
||||
private var pointer: Data.Index
|
||||
var isExhausted: Bool { get { return data.endIndex == pointer } }
|
||||
|
||||
public func rewind() { pointer = data.startIndex }
|
||||
|
||||
public func seek(to: Int) {
|
||||
pointer = data.index(
|
||||
data.startIndex,
|
||||
offsetBy: to,
|
||||
limitedBy: data.index(before: data.endIndex)
|
||||
) ?? data.index(before: data.endIndex)
|
||||
}
|
||||
|
||||
public init?(_ data: Data) {
|
||||
if data.isEmpty { return nil }
|
||||
self.data = data
|
||||
self.pointer = data.startIndex
|
||||
}
|
||||
|
||||
public func next() -> ControlByte? {
|
||||
while !isExhausted {
|
||||
if let controlByte = peek(index: pointer) {
|
||||
pointer = data.index(
|
||||
pointer,
|
||||
offsetBy: Int(controlByte.definitionSize),
|
||||
limitedBy: data.index(before: data.endIndex)
|
||||
) ?? data.index(before: data.endIndex)
|
||||
return controlByte
|
||||
}
|
||||
pointer = data.index(after: pointer)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
private func peek(index: Data.Index) -> ControlByte? {
|
||||
let upperBound = data.index(
|
||||
index,
|
||||
offsetBy: 5,
|
||||
limitedBy: data.index(before: data.endIndex)
|
||||
) ?? data.index(before: data.endIndex)
|
||||
if index == upperBound { return nil }
|
||||
let range = Range(uncheckedBounds: (lower: index, upper: upperBound))
|
||||
return ControlByte(bytes: data.subdata(in: range))
|
||||
}
|
||||
|
||||
private func peek(index: Data.Index, controlByte: ControlByte) -> (data: Data, upperBound: Data.Index) {
|
||||
let upperBound = data.index(
|
||||
index,
|
||||
offsetBy: Int(controlByte.payloadSize),
|
||||
limitedBy: data.endIndex
|
||||
) ?? data.endIndex
|
||||
if controlByte.payloadSize == 0 { return (data: Data(), upperBound: upperBound) }
|
||||
return (
|
||||
data: data.subdata(in: Range(uncheckedBounds: (lower: index, upper: upperBound))),
|
||||
upperBound: upperBound
|
||||
)
|
||||
}
|
||||
|
||||
public func peek(at offset: Int) -> ControlByte? {
|
||||
guard let index = data.index(
|
||||
data.startIndex,
|
||||
offsetBy: offset,
|
||||
limitedBy: data.index(before: data.endIndex)
|
||||
) else { return nil }
|
||||
return peek(index: index)
|
||||
}
|
||||
|
||||
public func peek(_ controlByte: ControlByte, at offset: Int) -> Data? {
|
||||
guard let index = data.index(
|
||||
data.startIndex,
|
||||
offsetBy: offset,
|
||||
limitedBy: data.index(before: data.endIndex)
|
||||
) else { return nil }
|
||||
return peek(index: index, controlByte: controlByte).data
|
||||
}
|
||||
|
||||
public func next(_ controlByte: ControlByte) -> Data {
|
||||
let (payload, upperBound) = peek(index: pointer, controlByte: controlByte)
|
||||
pointer = upperBound
|
||||
return payload
|
||||
}
|
||||
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
import Foundation
|
||||
|
||||
public enum OutputData: Equatable {
|
||||
case pointer(UInt32)
|
||||
case utf8String(String)
|
||||
case double(Double)
|
||||
case bytes(Data)
|
||||
case uInt16(UInt16)
|
||||
case uInt32(UInt32)
|
||||
case map([String: OutputData])
|
||||
case int32(Int32)
|
||||
case uInt64(UInt64)
|
||||
case uInt128(Data)
|
||||
case array([OutputData])
|
||||
case dataCacheContainer([OutputData])
|
||||
case endMarker
|
||||
case boolean(Bool)
|
||||
case float(Float)
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
import Foundation
|
||||
|
||||
public protocol PeekableDataSequence {
|
||||
|
||||
func peek(at offset: Int) -> ControlByte?
|
||||
|
||||
func peek(_ controlByte: ControlByte, at offset: Int) -> Data?
|
||||
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
import Foundation
|
||||
import MaxMindDecoder
|
||||
|
||||
public struct Metadata: Equatable {
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import Foundation
|
||||
import MaxMindDecoder
|
||||
|
||||
public class Reader {
|
||||
|
||||
|
|
|
@ -1,34 +1,32 @@
|
|||
import Foundation
|
||||
import MaxMindDecoder
|
||||
import Decoder
|
||||
|
||||
func decode(_ data: Data, metadataSectionSize: Int, databaseSize: Int) -> Metadata? {
|
||||
guard let iterator = MaxMindIterator(data) else { return nil }
|
||||
guard let mapControlByte = iterator.next() else { return nil }
|
||||
if mapControlByte.type != .map { return nil }
|
||||
let decoder = MaxMindDecoder(inputEndianness: .big)
|
||||
if data.isEmpty { return nil }
|
||||
let decoder = Decoder(data)
|
||||
guard let payload = decoder.read(at: 0) else { return nil }
|
||||
guard case let Payload.map(map) = payload else { return nil }
|
||||
|
||||
let decoded: [String: Any] = decoder.decode(iterator, size: Int(mapControlByte.payloadSize))
|
||||
|
||||
guard let nodeCount = decoded["node_count"] as? UInt32 else { return nil }
|
||||
guard let recordSize = decoded["record_size"] as? UInt16 else { return nil }
|
||||
guard let ipVersion = decoded["ip_version"] as? UInt16 else { return nil }
|
||||
guard let databaseType = decoded["database_type"] as? String else { return nil }
|
||||
guard let languages = decoded["languages"] as? [String] else { return nil }
|
||||
guard let majorVersion = decoded["binary_format_major_version"] as? UInt16 else { return nil }
|
||||
guard let minorVersion = decoded["binary_format_minor_version"] as? UInt16 else { return nil }
|
||||
guard let buildEpoch = decoded["build_epoch"] as? UInt64 else { return nil }
|
||||
guard let description = decoded["description"] as? [String: String] else { return nil }
|
||||
guard let nodeCount = map["node_count"]?.unwrap() as UInt32? else { return nil }
|
||||
guard let recordSize = map["record_size"]?.unwrap() as UInt16? else { return nil }
|
||||
guard let ipVersion = map["ip_version"]?.unwrap() as UInt16? else { return nil }
|
||||
guard let databaseType = map["database_type"]?.unwrap() as String? else { return nil }
|
||||
guard let languages = map["languages"]?.unwrap() as [Payload]? else { return nil }
|
||||
guard let majorVersion = map["binary_format_major_version"]?.unwrap() as UInt16? else { return nil }
|
||||
guard let minorVersion = map["binary_format_minor_version"]?.unwrap() as UInt16? else { return nil }
|
||||
guard let buildEpoch = map["build_epoch"]?.unwrap() as UInt64? else { return nil }
|
||||
guard let description = map["description"]?.unwrap() as [String: Payload]? else { return nil }
|
||||
|
||||
return Metadata(
|
||||
nodeCount: nodeCount,
|
||||
recordSize: recordSize,
|
||||
ipVersion: ipVersion,
|
||||
databaseType: databaseType,
|
||||
languages: languages,
|
||||
languages: languages.compactMap({ $0.unwrap() as String? }),
|
||||
binaryFormatMajorVersion: majorVersion,
|
||||
binaryFormatMinorVersion: minorVersion,
|
||||
buildEpoch: buildEpoch,
|
||||
description: description,
|
||||
description: description.compactMapValues({ $0.unwrap() as String? }),
|
||||
metadataSectionSize: metadataSectionSize,
|
||||
databaseSize: databaseSize
|
||||
)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import Foundation
|
||||
import XCTest
|
||||
import Metadata
|
||||
import MaxMindDecoder
|
||||
import Decoder
|
||||
@testable import DataSection
|
||||
|
||||
class InMemoryDataSectionTest: XCTestCase {
|
||||
|
@ -23,8 +23,7 @@ class InMemoryDataSectionTest: XCTestCase {
|
|||
func testLookup_returnsNilIfIteratorCantResolveNextControlByte() {
|
||||
let dataSection = InMemoryDataSection(
|
||||
metadata: InMemoryDataSectionTest.countryMetadata,
|
||||
iterator: MaxMindIterator(Data([0b0000_0000]))!,
|
||||
decoder: MaxMindDecoder(inputEndianness: .big)
|
||||
decoder: Decoder(Data([0b0000_0000]))
|
||||
)
|
||||
XCTAssertNil(dataSection.lookup(pointer: 100))
|
||||
}
|
||||
|
@ -32,12 +31,9 @@ class InMemoryDataSectionTest: XCTestCase {
|
|||
func testLookup_returnsNilIfIteratorDoesntResolveToMap() {
|
||||
let dataSection = InMemoryDataSection(
|
||||
metadata: InMemoryDataSectionTest.countryMetadata,
|
||||
iterator: MaxMindIterator(
|
||||
Data(
|
||||
[0b0101_1100] + "Hello World Hello World test".data(using: .utf8)!
|
||||
)
|
||||
)!,
|
||||
decoder: MaxMindDecoder(inputEndianness: .big)
|
||||
decoder: Decoder(Data(
|
||||
[0b0101_1100] + "Hello World Hello World test".data(using: .utf8)!
|
||||
))
|
||||
)
|
||||
XCTAssertNil(dataSection.lookup(pointer: 0))
|
||||
}
|
||||
|
|
|
@ -87,6 +87,23 @@ fileprivate class MockControlByteInterpreter: ControlByteInterpreter {
|
|||
}
|
||||
|
||||
fileprivate class MockPayloadInterpreter: PayloadInterpreter {
|
||||
|
||||
init() {
|
||||
super.init(
|
||||
interpretArray: { _, _, _, _ in nil },
|
||||
interpretDataCacheContainer: { _, _, _, _ in nil },
|
||||
interpretDouble: { _, _ in nil },
|
||||
interpretFloat: { _, _ in nil },
|
||||
interpretInt32: { _, _ in nil },
|
||||
interpretMap: { _, _, _, _ in nil },
|
||||
interpretPointer: { _, _, _, _, _, _ in nil },
|
||||
interpretUInt16: { _, _ in nil },
|
||||
interpretUInt32: { _, _ in nil },
|
||||
interpretUInt64: { _, _ in nil },
|
||||
interpretUtf8String: { _ in nil }
|
||||
)
|
||||
}
|
||||
|
||||
override func interpret(input: Input, using decoder: Decoder, resolvePointers: Bool) -> InterpretationResult? {
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -119,6 +119,23 @@ fileprivate class MockControlByteInterpreter: ControlByteInterpreter {
|
|||
}
|
||||
|
||||
fileprivate class MockPayloadInterpreter: PayloadInterpreter {
|
||||
|
||||
init() {
|
||||
super.init(
|
||||
interpretArray: { _, _, _, _ in nil },
|
||||
interpretDataCacheContainer: { _, _, _, _ in nil },
|
||||
interpretDouble: { _, _ in nil },
|
||||
interpretFloat: { _, _ in nil },
|
||||
interpretInt32: { _, _ in nil },
|
||||
interpretMap: { _, _, _, _ in nil },
|
||||
interpretPointer: { _, _, _, _, _, _ in nil },
|
||||
interpretUInt16: { _, _ in nil },
|
||||
interpretUInt32: { _, _ in nil },
|
||||
interpretUInt64: { _, _ in nil },
|
||||
interpretUtf8String: { _ in nil }
|
||||
)
|
||||
}
|
||||
|
||||
override func interpret(
|
||||
input: Input,
|
||||
using decoder: Decoder,
|
||||
|
|
|
@ -123,6 +123,23 @@ fileprivate class MockControlByteInterpreter: ControlByteInterpreter {
|
|||
}
|
||||
|
||||
fileprivate class MockPayloadInterpreter: PayloadInterpreter {
|
||||
|
||||
init() {
|
||||
super.init(
|
||||
interpretArray: { _, _, _, _ in nil },
|
||||
interpretDataCacheContainer: { _, _, _, _ in nil },
|
||||
interpretDouble: { _, _ in nil },
|
||||
interpretFloat: { _, _ in nil },
|
||||
interpretInt32: { _, _ in nil },
|
||||
interpretMap: { _, _, _, _ in nil },
|
||||
interpretPointer: { _, _, _, _, _, _ in nil },
|
||||
interpretUInt16: { _, _ in nil },
|
||||
interpretUInt32: { _, _ in nil },
|
||||
interpretUInt64: { _, _ in nil },
|
||||
interpretUtf8String: { _ in nil }
|
||||
)
|
||||
}
|
||||
|
||||
override func interpret(
|
||||
input: Input,
|
||||
using decoder: Decoder,
|
||||
|
|
|
@ -142,6 +142,23 @@ fileprivate class MockControlByteInterpreter: ControlByteInterpreter {
|
|||
}
|
||||
|
||||
fileprivate class MockPayloadInterpreter: PayloadInterpreter {
|
||||
|
||||
init() {
|
||||
super.init(
|
||||
interpretArray: { _, _, _, _ in nil },
|
||||
interpretDataCacheContainer: { _, _, _, _ in nil },
|
||||
interpretDouble: { _, _ in nil },
|
||||
interpretFloat: { _, _ in nil },
|
||||
interpretInt32: { _, _ in nil },
|
||||
interpretMap: { _, _, _, _ in nil },
|
||||
interpretPointer: { _, _, _, _, _, _ in nil },
|
||||
interpretUInt16: { _, _ in nil },
|
||||
interpretUInt32: { _, _ in nil },
|
||||
interpretUInt64: { _, _ in nil },
|
||||
interpretUtf8String: { _ in nil }
|
||||
)
|
||||
}
|
||||
|
||||
override func interpret(
|
||||
input: Input,
|
||||
using decoder: Decoder,
|
||||
|
|
|
@ -60,6 +60,23 @@ fileprivate class StubControlByteInterpreter: ControlByteInterpreter {
|
|||
}
|
||||
|
||||
fileprivate class StubPayloadInterpreter: PayloadInterpreter {
|
||||
|
||||
init() {
|
||||
super.init(
|
||||
interpretArray: { _, _, _, _ in nil },
|
||||
interpretDataCacheContainer: { _, _, _, _ in nil },
|
||||
interpretDouble: { _, _ in nil },
|
||||
interpretFloat: { _, _ in nil },
|
||||
interpretInt32: { _, _ in nil },
|
||||
interpretMap: { _, _, _, _ in nil },
|
||||
interpretPointer: { _, _, _, _, _, _ in nil },
|
||||
interpretUInt16: { _, _ in nil },
|
||||
interpretUInt32: { _, _ in nil },
|
||||
interpretUInt64: { _, _ in nil },
|
||||
interpretUtf8String: { _ in nil }
|
||||
)
|
||||
}
|
||||
|
||||
override func interpret(input: Input, using decoder: Decoder, resolvePointers: Bool) -> InterpretationResult? {
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -3,4 +3,117 @@ import XCTest
|
|||
@testable import Decoder
|
||||
|
||||
class PayloadInterpreterTest: XCTestCase {
|
||||
|
||||
func testInterpret_endMarker() {
|
||||
let interpreter = PayloadInterpreter(
|
||||
interpretArray: { _, _, _, _ in nil },
|
||||
interpretDataCacheContainer: { _, _, _, _ in nil },
|
||||
interpretDouble: { _, _ in nil },
|
||||
interpretFloat: { _, _ in nil },
|
||||
interpretInt32: { _, _ in nil },
|
||||
interpretMap: { _, _, _, _ in nil },
|
||||
interpretPointer: { _, _, _, _, _, _ in nil },
|
||||
interpretUInt16: { _, _ in nil },
|
||||
interpretUInt32: { _, _ in nil },
|
||||
interpretUInt64: { _, _ in nil },
|
||||
interpretUtf8String: { _ in nil }
|
||||
)
|
||||
guard let (payload, size) = interpreter.interpret(
|
||||
input: (
|
||||
bytes: Data(),
|
||||
controlByte: ControlByte.endMarker,
|
||||
sourceEndianness: Endianness.current,
|
||||
controlStart: 123,
|
||||
payloadStart: 123
|
||||
),
|
||||
using: MockDecoder(payloadInterpreter: interpreter),
|
||||
resolvePointers: false
|
||||
) else {
|
||||
XCTFail("Should always resolve an endmarker type payload.")
|
||||
return
|
||||
}
|
||||
|
||||
XCTAssertEqual(0, size)
|
||||
XCTAssertEqual(Payload.endMarker, payload)
|
||||
}
|
||||
|
||||
func testInterpret_boolean() {
|
||||
let interpreter = PayloadInterpreter(
|
||||
interpretArray: { _, _, _, _ in nil },
|
||||
interpretDataCacheContainer: { _, _, _, _ in nil },
|
||||
interpretDouble: { _, _ in nil },
|
||||
interpretFloat: { _, _ in nil },
|
||||
interpretInt32: { _, _ in nil },
|
||||
interpretMap: { _, _, _, _ in nil },
|
||||
interpretPointer: { _, _, _, _, _, _ in nil },
|
||||
interpretUInt16: { _, _ in nil },
|
||||
interpretUInt32: { _, _ in nil },
|
||||
interpretUInt64: { _, _ in nil },
|
||||
interpretUtf8String: { _ in nil }
|
||||
)
|
||||
let trueInput = (
|
||||
bytes: Data(),
|
||||
controlByte: ControlByte.boolean(payload: true),
|
||||
sourceEndianness: Endianness.current,
|
||||
controlStart: 123,
|
||||
payloadStart: 123
|
||||
)
|
||||
let falseInput = (
|
||||
bytes: Data(),
|
||||
controlByte: ControlByte.boolean(payload: false),
|
||||
sourceEndianness: Endianness.current,
|
||||
controlStart: 123,
|
||||
payloadStart: 123
|
||||
)
|
||||
let decoder = MockDecoder(payloadInterpreter: interpreter)
|
||||
guard let (truePayload, trueSize) = interpreter.interpret(
|
||||
input: trueInput,
|
||||
using: decoder,
|
||||
resolvePointers: false
|
||||
) else {
|
||||
XCTFail("Should always resolve an endmarker type payload.")
|
||||
return
|
||||
}
|
||||
guard let (falsePayload, falseSize) = interpreter.interpret(
|
||||
input: falseInput,
|
||||
using: decoder,
|
||||
resolvePointers: false
|
||||
) else {
|
||||
XCTFail("Should always resolve an endmarker type payload.")
|
||||
return
|
||||
}
|
||||
XCTAssertEqual(Payload.boolean(true), truePayload)
|
||||
XCTAssertEqual(0, trueSize)
|
||||
XCTAssertEqual(Payload.boolean(false), falsePayload)
|
||||
XCTAssertEqual(0, falseSize)
|
||||
}
|
||||
|
||||
// TODO : Write the rest of these dumb tests, to make refactoring safer for the future. I will most probably attempt a
|
||||
// refactor of this crap at some point.
|
||||
|
||||
}
|
||||
|
||||
fileprivate class MockDecoder: Decoder {
|
||||
|
||||
init(payloadInterpreter: PayloadInterpreter) {
|
||||
super.init(
|
||||
data: Data([0xFF]),
|
||||
controlByteInterpreter: MockControlByteInterpreter(),
|
||||
payloadInterpreter: payloadInterpreter
|
||||
)
|
||||
}
|
||||
|
||||
override func read(at controlByteOffset: Int, resolvePointers: Bool) -> Decoder.Output? { return nil }
|
||||
}
|
||||
|
||||
fileprivate class MockControlByteInterpreter: ControlByteInterpreter {
|
||||
init() {
|
||||
super.init(
|
||||
typeResolver: { _, _ in nil },
|
||||
payloadSizeResolver: { _, _, _ in nil },
|
||||
definitionSizeResolver: { _, _, _ in nil }
|
||||
)
|
||||
}
|
||||
|
||||
override func interpret(bytes: Data, sourceEndianness: Endianness) -> InterpretationResult? { return nil }
|
||||
}
|
|
@ -0,0 +1,302 @@
|
|||
import Foundation
|
||||
import XCTest
|
||||
@testable import Decoder
|
||||
|
||||
class PayloadTest: XCTestCase {
|
||||
|
||||
func testUnwrap_pointer() {
|
||||
let expectedList: [UInt32] = [
|
||||
123,
|
||||
123456,
|
||||
123456789,
|
||||
1234567890,
|
||||
]
|
||||
for expected in expectedList {
|
||||
XCTAssertEqual(expected, Payload.pointer(expected).unwrap() as UInt32?)
|
||||
XCTAssertNil(Payload.pointer(expected).unwrap() as String?)
|
||||
XCTAssertNil(Payload.pointer(expected).unwrap() as Double?)
|
||||
XCTAssertNil(Payload.pointer(expected).unwrap() as UInt16?)
|
||||
XCTAssertNil(Payload.pointer(expected).unwrap() as [String: Payload]?)
|
||||
XCTAssertNil(Payload.pointer(expected).unwrap() as Int32?)
|
||||
XCTAssertNil(Payload.pointer(expected).unwrap() as UInt64?)
|
||||
XCTAssertNil(Payload.pointer(expected).unwrap() as Data?)
|
||||
XCTAssertNil(Payload.pointer(expected).unwrap() as [Payload]?)
|
||||
XCTAssertNil(Payload.pointer(expected).unwrap() as Bool?)
|
||||
XCTAssertNil(Payload.pointer(expected).unwrap() as Float?)
|
||||
}
|
||||
}
|
||||
|
||||
func testUnwrap_utf8String() {
|
||||
let expectedList: [String] = [
|
||||
"test",
|
||||
"test string"
|
||||
]
|
||||
for expected in expectedList {
|
||||
XCTAssertNil(Payload.utf8String(expected).unwrap() as UInt32?)
|
||||
XCTAssertEqual(expected, Payload.utf8String(expected).unwrap() as String?)
|
||||
XCTAssertNil(Payload.utf8String(expected).unwrap() as Double?)
|
||||
XCTAssertNil(Payload.utf8String(expected).unwrap() as Data?)
|
||||
XCTAssertNil(Payload.utf8String(expected).unwrap() as UInt16?)
|
||||
XCTAssertNil(Payload.utf8String(expected).unwrap() as [String: Payload]?)
|
||||
XCTAssertNil(Payload.utf8String(expected).unwrap() as Int32?)
|
||||
XCTAssertNil(Payload.utf8String(expected).unwrap() as UInt64?)
|
||||
XCTAssertNil(Payload.utf8String(expected).unwrap() as [Payload]?)
|
||||
XCTAssertNil(Payload.utf8String(expected).unwrap() as Bool?)
|
||||
XCTAssertNil(Payload.utf8String(expected).unwrap() as Float?)
|
||||
}
|
||||
}
|
||||
|
||||
func testUnwrap_double() {
|
||||
let expectedList: [Double] = [
|
||||
123,
|
||||
123.456,
|
||||
123456.7890,
|
||||
]
|
||||
for expected in expectedList {
|
||||
XCTAssertNil(Payload.double(expected).unwrap() as UInt32?)
|
||||
XCTAssertNil(Payload.double(expected).unwrap() as String?)
|
||||
XCTAssertEqual(expected, Payload.double(expected).unwrap() as Double?)
|
||||
XCTAssertNil(Payload.double(expected).unwrap() as Data?)
|
||||
XCTAssertNil(Payload.double(expected).unwrap() as UInt16?)
|
||||
XCTAssertNil(Payload.double(expected).unwrap() as [String: Payload]?)
|
||||
XCTAssertNil(Payload.double(expected).unwrap() as Int32?)
|
||||
XCTAssertNil(Payload.double(expected).unwrap() as UInt64?)
|
||||
XCTAssertNil(Payload.double(expected).unwrap() as [Payload]?)
|
||||
XCTAssertNil(Payload.double(expected).unwrap() as Bool?)
|
||||
XCTAssertNil(Payload.double(expected).unwrap() as Float?)
|
||||
}
|
||||
}
|
||||
|
||||
func testUnwrap_bytes() {
|
||||
let expectedList: [Data] = [
|
||||
Data(repeating: 0xFF, count: 1),
|
||||
Data(repeating: 0xFF, count: 10),
|
||||
Data(repeating: 0xFF, count: 100),
|
||||
]
|
||||
for expected in expectedList {
|
||||
XCTAssertNil(Payload.bytes(expected).unwrap() as UInt32?)
|
||||
XCTAssertNil(Payload.bytes(expected).unwrap() as String?)
|
||||
XCTAssertNil(Payload.bytes(expected).unwrap() as Double?)
|
||||
XCTAssertEqual(expected, Payload.bytes(expected).unwrap() as Data?)
|
||||
XCTAssertNil(Payload.bytes(expected).unwrap() as UInt16?)
|
||||
XCTAssertNil(Payload.bytes(expected).unwrap() as [String: Payload]?)
|
||||
XCTAssertNil(Payload.bytes(expected).unwrap() as Int32?)
|
||||
XCTAssertNil(Payload.bytes(expected).unwrap() as UInt64?)
|
||||
XCTAssertNil(Payload.bytes(expected).unwrap() as [Payload]?)
|
||||
XCTAssertNil(Payload.bytes(expected).unwrap() as Bool?)
|
||||
XCTAssertNil(Payload.bytes(expected).unwrap() as Float?)
|
||||
}
|
||||
}
|
||||
|
||||
func testUnwrap_uInt16() {
|
||||
let expectedList: [UInt16] = [123, 1234]
|
||||
for expected in expectedList {
|
||||
XCTAssertNil(Payload.uInt16(expected).unwrap() as UInt32?)
|
||||
XCTAssertNil(Payload.uInt16(expected).unwrap() as String?)
|
||||
XCTAssertNil(Payload.uInt16(expected).unwrap() as Double?)
|
||||
XCTAssertNil(Payload.uInt16(expected).unwrap() as Data?)
|
||||
XCTAssertEqual(expected, Payload.uInt16(expected).unwrap() as UInt16?)
|
||||
XCTAssertNil(Payload.uInt16(expected).unwrap() as [String: Payload]?)
|
||||
XCTAssertNil(Payload.uInt16(expected).unwrap() as Int32?)
|
||||
XCTAssertNil(Payload.uInt16(expected).unwrap() as UInt64?)
|
||||
XCTAssertNil(Payload.uInt16(expected).unwrap() as [Payload]?)
|
||||
XCTAssertNil(Payload.uInt16(expected).unwrap() as Bool?)
|
||||
XCTAssertNil(Payload.uInt16(expected).unwrap() as Float?)
|
||||
}
|
||||
}
|
||||
|
||||
func testUnwrap_uInt32() {
|
||||
let expectedList: [UInt32] = [123, 123456, 123456789]
|
||||
for expected in expectedList {
|
||||
XCTAssertEqual(expected, Payload.uInt32(expected).unwrap() as UInt32?)
|
||||
XCTAssertNil(Payload.uInt32(expected).unwrap() as String?)
|
||||
XCTAssertNil(Payload.uInt32(expected).unwrap() as Double?)
|
||||
XCTAssertNil(Payload.uInt32(expected).unwrap() as Data?)
|
||||
XCTAssertNil(Payload.uInt32(expected).unwrap() as UInt16?)
|
||||
XCTAssertNil(Payload.uInt32(expected).unwrap() as [String: Payload]?)
|
||||
XCTAssertNil(Payload.uInt32(expected).unwrap() as Int32?)
|
||||
XCTAssertNil(Payload.uInt32(expected).unwrap() as UInt64?)
|
||||
XCTAssertNil(Payload.uInt32(expected).unwrap() as [Payload]?)
|
||||
XCTAssertNil(Payload.uInt32(expected).unwrap() as Bool?)
|
||||
XCTAssertNil(Payload.uInt32(expected).unwrap() as Float?)
|
||||
}
|
||||
}
|
||||
|
||||
func testUnwrap_map() {
|
||||
let expected: [String: Payload] = [
|
||||
"dumb1": Payload.pointer(444),
|
||||
"dumb2": Payload.array([Payload.int32(1), Payload.int32(1), Payload.int32(1)]),
|
||||
"dumb3": Payload.endMarker,
|
||||
]
|
||||
XCTAssertNil(Payload.map(expected).unwrap() as UInt32?)
|
||||
XCTAssertNil(Payload.map(expected).unwrap() as String?)
|
||||
XCTAssertNil(Payload.map(expected).unwrap() as Double?)
|
||||
XCTAssertNil(Payload.map(expected).unwrap() as Data?)
|
||||
XCTAssertNil(Payload.map(expected).unwrap() as UInt16?)
|
||||
XCTAssertEqual(expected, Payload.map(expected).unwrap() as [String: Payload]?)
|
||||
XCTAssertNil(Payload.map(expected).unwrap() as Int32?)
|
||||
XCTAssertNil(Payload.map(expected).unwrap() as UInt64?)
|
||||
XCTAssertNil(Payload.map(expected).unwrap() as [Payload]?)
|
||||
XCTAssertNil(Payload.map(expected).unwrap() as Bool?)
|
||||
XCTAssertNil(Payload.map(expected).unwrap() as Float?)
|
||||
}
|
||||
|
||||
func testUnwrap_int32() {
|
||||
let expectedList: [Int32] = [123, 123456, 123456789]
|
||||
for expected in expectedList {
|
||||
XCTAssertNil(Payload.int32(expected).unwrap() as UInt32?)
|
||||
XCTAssertNil(Payload.int32(expected).unwrap() as String?)
|
||||
XCTAssertNil(Payload.int32(expected).unwrap() as Double?)
|
||||
XCTAssertNil(Payload.int32(expected).unwrap() as Data?)
|
||||
XCTAssertNil(Payload.int32(expected).unwrap() as UInt16?)
|
||||
XCTAssertNil(Payload.int32(expected).unwrap() as [String: Payload]?)
|
||||
XCTAssertEqual(expected, Payload.int32(expected).unwrap() as Int32?)
|
||||
XCTAssertNil(Payload.int32(expected).unwrap() as UInt64?)
|
||||
XCTAssertNil(Payload.int32(expected).unwrap() as [Payload]?)
|
||||
XCTAssertNil(Payload.int32(expected).unwrap() as Bool?)
|
||||
XCTAssertNil(Payload.int32(expected).unwrap() as Float?)
|
||||
}
|
||||
}
|
||||
|
||||
func testUnwrap_uInt64() {
|
||||
let expectedList: [UInt64] = [123, 456, 789]
|
||||
for expected in expectedList {
|
||||
XCTAssertNil(Payload.uInt64(expected).unwrap() as UInt32?)
|
||||
XCTAssertNil(Payload.uInt64(expected).unwrap() as String?)
|
||||
XCTAssertNil(Payload.uInt64(expected).unwrap() as Double?)
|
||||
XCTAssertNil(Payload.uInt64(expected).unwrap() as Data?)
|
||||
XCTAssertNil(Payload.uInt64(expected).unwrap() as UInt16?)
|
||||
XCTAssertNil(Payload.uInt64(expected).unwrap() as [String: Payload]?)
|
||||
XCTAssertNil(Payload.uInt64(expected).unwrap() as Int32?)
|
||||
XCTAssertEqual(expected, Payload.uInt64(expected).unwrap() as UInt64?)
|
||||
XCTAssertNil(Payload.uInt64(expected).unwrap() as [Payload]?)
|
||||
XCTAssertNil(Payload.uInt64(expected).unwrap() as Bool?)
|
||||
XCTAssertNil(Payload.uInt64(expected).unwrap() as Float?)
|
||||
}
|
||||
}
|
||||
|
||||
func testUnwrap_uInt128() {
|
||||
let expectedList: [Data] = [
|
||||
Data(repeating: 0x00, count: 128),
|
||||
Data(repeating: 0xFF, count: 128),
|
||||
Data(repeating: 0x99, count: 128),
|
||||
]
|
||||
for expected in expectedList {
|
||||
XCTAssertNil(Payload.uInt128(expected).unwrap() as UInt32?)
|
||||
XCTAssertNil(Payload.uInt128(expected).unwrap() as String?)
|
||||
XCTAssertNil(Payload.uInt128(expected).unwrap() as Double?)
|
||||
XCTAssertEqual(expected, Payload.uInt128(expected).unwrap() as Data?)
|
||||
XCTAssertNil(Payload.uInt128(expected).unwrap() as UInt16?)
|
||||
XCTAssertNil(Payload.uInt128(expected).unwrap() as [String: Payload]?)
|
||||
XCTAssertNil(Payload.uInt128(expected).unwrap() as Int32?)
|
||||
XCTAssertNil(Payload.uInt128(expected).unwrap() as UInt64?)
|
||||
XCTAssertNil(Payload.uInt128(expected).unwrap() as [Payload]?)
|
||||
XCTAssertNil(Payload.uInt128(expected).unwrap() as Bool?)
|
||||
XCTAssertNil(Payload.uInt128(expected).unwrap() as Float?)
|
||||
}
|
||||
}
|
||||
|
||||
func testUnwrap_array() {
|
||||
let expected: [Payload] = [
|
||||
Payload.int32(123),
|
||||
Payload.utf8String("Php scripters put random shit in collections. We support it."),
|
||||
Payload.uInt32(1234),
|
||||
Payload.array([
|
||||
Payload.utf8String(
|
||||
"Oh yes, in their world it's normal to create a mixed list of things with mixed list of things containing mixed maps of things."
|
||||
),
|
||||
Payload.array([Payload.int32(1), Payload.int32(1), Payload.int32(1)]),
|
||||
Payload.map([
|
||||
"dumb1": Payload.pointer(444),
|
||||
"dumb2": Payload.array([Payload.int32(1), Payload.int32(1), Payload.int32(1)]),
|
||||
"dumb3": Payload.endMarker,
|
||||
])
|
||||
])
|
||||
]
|
||||
XCTAssertNil(Payload.array(expected).unwrap() as UInt32?)
|
||||
XCTAssertNil(Payload.array(expected).unwrap() as String?)
|
||||
XCTAssertNil(Payload.array(expected).unwrap() as Double?)
|
||||
XCTAssertNil(Payload.array(expected).unwrap() as Data?)
|
||||
XCTAssertNil(Payload.array(expected).unwrap() as UInt16?)
|
||||
XCTAssertNil(Payload.array(expected).unwrap() as [String: Payload]?)
|
||||
XCTAssertNil(Payload.array(expected).unwrap() as Int32?)
|
||||
XCTAssertNil(Payload.array(expected).unwrap() as UInt64?)
|
||||
XCTAssertEqual(expected, Payload.array(expected).unwrap() as [Payload]?)
|
||||
XCTAssertNil(Payload.array(expected).unwrap() as Bool?)
|
||||
XCTAssertNil(Payload.array(expected).unwrap() as Float?)
|
||||
}
|
||||
|
||||
func testUnwrap_dataCacheContainer() {
|
||||
let expected: [Payload] = [
|
||||
Payload.pointer(123),
|
||||
Payload.utf8String("Hello"),
|
||||
Payload.double(123),
|
||||
Payload.bytes(Data(repeating: 0xFF, count: 1234)),
|
||||
Payload.uInt16(123),
|
||||
Payload.uInt32(123),
|
||||
Payload.map(["test": Payload.utf8String("hey")]),
|
||||
Payload.int32(123),
|
||||
Payload.uInt64(123),
|
||||
Payload.uInt128(Data(repeating: 0xFF, count: 128)),
|
||||
Payload.array([Payload.utf8String("item")]),
|
||||
Payload.dataCacheContainer([Payload.utf8String("cache item")]),
|
||||
Payload.endMarker,
|
||||
Payload.boolean(true),
|
||||
Payload.float(123)
|
||||
]
|
||||
XCTAssertNil(Payload.dataCacheContainer(expected).unwrap() as UInt32?)
|
||||
XCTAssertNil(Payload.dataCacheContainer(expected).unwrap() as String?)
|
||||
XCTAssertNil(Payload.dataCacheContainer(expected).unwrap() as Double?)
|
||||
XCTAssertNil(Payload.dataCacheContainer(expected).unwrap() as Data?)
|
||||
XCTAssertNil(Payload.dataCacheContainer(expected).unwrap() as UInt16?)
|
||||
XCTAssertNil(Payload.dataCacheContainer(expected).unwrap() as [String: Payload]?)
|
||||
XCTAssertNil(Payload.dataCacheContainer(expected).unwrap() as Int32?)
|
||||
XCTAssertNil(Payload.dataCacheContainer(expected).unwrap() as UInt64?)
|
||||
XCTAssertEqual(expected, Payload.dataCacheContainer(expected).unwrap() as [Payload]?)
|
||||
XCTAssertNil(Payload.dataCacheContainer(expected).unwrap() as Bool?)
|
||||
XCTAssertNil(Payload.dataCacheContainer(expected).unwrap() as Float?)
|
||||
}
|
||||
|
||||
func testUnwrap_boolean() {
|
||||
let expectedList: [Bool] = [true, false]
|
||||
for expected in expectedList {
|
||||
XCTAssertNil(Payload.boolean(expected).unwrap() as UInt32?)
|
||||
XCTAssertNil(Payload.boolean(expected).unwrap() as String?)
|
||||
XCTAssertNil(Payload.boolean(expected).unwrap() as Double?)
|
||||
XCTAssertNil(Payload.boolean(expected).unwrap() as Data?)
|
||||
XCTAssertNil(Payload.boolean(expected).unwrap() as UInt16?)
|
||||
XCTAssertNil(Payload.boolean(expected).unwrap() as [String: Payload]?)
|
||||
XCTAssertNil(Payload.boolean(expected).unwrap() as Int32?)
|
||||
XCTAssertNil(Payload.boolean(expected).unwrap() as UInt64?)
|
||||
XCTAssertNil(Payload.boolean(expected).unwrap() as [Payload]?)
|
||||
XCTAssertEqual(expected, Payload.boolean(expected).unwrap() as Bool?)
|
||||
XCTAssertNil(Payload.boolean(expected).unwrap() as Float?)
|
||||
}
|
||||
}
|
||||
|
||||
func testUnwrap_float() {
|
||||
let expectedList: [Float] = [
|
||||
123,
|
||||
123.456,
|
||||
123456.7890
|
||||
]
|
||||
for expected in expectedList {
|
||||
XCTAssertNil(Payload.float(expected).unwrap() as UInt32?)
|
||||
XCTAssertNil(Payload.float(expected).unwrap() as String?)
|
||||
XCTAssertNil(Payload.float(expected).unwrap() as Double?)
|
||||
XCTAssertNil(Payload.float(expected).unwrap() as Data?)
|
||||
XCTAssertNil(Payload.float(expected).unwrap() as UInt16?)
|
||||
XCTAssertNil(Payload.float(expected).unwrap() as [String: Payload]?)
|
||||
XCTAssertNil(Payload.float(expected).unwrap() as Int32?)
|
||||
XCTAssertNil(Payload.float(expected).unwrap() as UInt64?)
|
||||
XCTAssertNil(Payload.float(expected).unwrap() as [Payload]?)
|
||||
XCTAssertNil(Payload.float(expected).unwrap() as Bool?)
|
||||
XCTAssertEqual(expected, Payload.float(expected).unwrap() as Float?)
|
||||
}
|
||||
}
|
||||
|
||||
func testUnwrap_endMarker() {
|
||||
XCTAssertNil(Payload.endMarker.unwrap())
|
||||
}
|
||||
|
||||
}
|
|
@ -1,199 +0,0 @@
|
|||
import Foundation
|
||||
import XCTest
|
||||
@testable import MaxMindDecoder
|
||||
|
||||
class ControlByteTest: XCTestCase {
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
continueAfterFailure = false
|
||||
}
|
||||
|
||||
override class func setUp() {
|
||||
super.setUp()
|
||||
precondition(nonExtendedRawValues.count > 0, "nonExtendedRawValues can't be empty.")
|
||||
precondition(extendedRawValues.count > 0, "extendedRawValues can't be empty.")
|
||||
}
|
||||
|
||||
func testInit_nilIfEmpty() {
|
||||
XCTAssertNil(ControlByte(bytes: Data()))
|
||||
}
|
||||
|
||||
func testInit_nilIfBiggerThanFive() {
|
||||
XCTAssertNil(ControlByte(bytes: Data([0b0100_0000]) + Data(count: 5)))
|
||||
XCTAssertNil(ControlByte(bytes: Data([0b0100_0000]) + Data(count: 6)))
|
||||
XCTAssertNil(ControlByte(bytes: Data([0b0100_0000]) + Data(count: 7)))
|
||||
XCTAssertNil(ControlByte(bytes: Data([0b0100_0000]) + Data(count: 10)))
|
||||
XCTAssertNil(ControlByte(bytes: Data([0b0100_0000]) + Data(count: 100)))
|
||||
XCTAssertNil(ControlByte(bytes: Data([0b0100_0000]) + Data(count: 10_000)))
|
||||
}
|
||||
|
||||
func testInit_dataTypeIdentification() {
|
||||
for value in nonExtendedRawValues {
|
||||
for data in (0...4).map({ Data([value &<< 5]) + Data(count: $0) }) {
|
||||
XCTAssertEqual(
|
||||
DataType(rawValue: value),
|
||||
ControlByte(bytes: data)?.type
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
for value in extendedRawValues {
|
||||
for data in (0...3).map({ Data([0b0000_0000, value - 7]) + Data(count: $0) }) {
|
||||
XCTAssertEqual(
|
||||
DataType(rawValue: value),
|
||||
ControlByte(bytes: data)?.type
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testInit_nilIfDataTypeNotRecognized() {
|
||||
XCTAssertNil(ControlByte(bytes: Data([0b0000_1111, 0b0000_1111, 0b0000_0000])))
|
||||
}
|
||||
|
||||
func testInit_payloadSizeDefinition_lessThan29() {
|
||||
let nonExtended: [ByteSizesTestDefinition] = nonExtendedRawValues
|
||||
.reduce([]) { byteSequence, typeDefinition in
|
||||
byteSequence + (0..<29).map({
|
||||
(
|
||||
payloadSize: UInt32($0),
|
||||
definitionSize: 1,
|
||||
input: Data([$0 | (typeDefinition << 5)]))
|
||||
})
|
||||
}
|
||||
let extended: [ByteSizesTestDefinition] = extendedRawValues
|
||||
.reduce([]) { byteSequence, typeDefinition in
|
||||
byteSequence + (0..<29).map({
|
||||
(
|
||||
payloadSize: UInt32($0),
|
||||
definitionSize: 2,
|
||||
input: Data([$0, typeDefinition - 7])
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
for (payloadSize, definitionSize, input) in (nonExtended + extended) {
|
||||
let controlByte = ControlByte(bytes: input)
|
||||
XCTAssertEqual(payloadSize, controlByte?.payloadSize)
|
||||
XCTAssertEqual(definitionSize, controlByte?.definitionSize)
|
||||
XCTAssertEqual(input[..<definitionSize], controlByte?.definition)
|
||||
}
|
||||
}
|
||||
|
||||
func testInit_payloadSizeDefinition_greaterThan28() {
|
||||
for (payloadSize, definitionSize, input) in payloadSizeTestDefinitions {
|
||||
let controlByte = ControlByte(bytes: input)
|
||||
XCTAssertEqual(
|
||||
payloadSize,
|
||||
controlByte?.payloadSize,
|
||||
"Expected a payload size of \(payloadSize), but instead got \(String(describing: controlByte?.payloadSize))"
|
||||
)
|
||||
XCTAssertEqual(definitionSize, controlByte?.definitionSize)
|
||||
XCTAssertEqual(input[..<definitionSize], controlByte?.definition)
|
||||
}
|
||||
}
|
||||
|
||||
func testInit_pointer() {
|
||||
for pointerByte in 0...0b0001_1111 {
|
||||
for strayBytes in 0...4 {
|
||||
let definitionByte = UInt8(pointerByte | 0b0010_0000)
|
||||
let definition = Data([definitionByte])
|
||||
let noise = Data(count: strayBytes)
|
||||
let bytes = definition + noise
|
||||
guard let controlByte = ControlByte(bytes: bytes) else {
|
||||
XCTFail("Should have been able to create a control byte.")
|
||||
return
|
||||
}
|
||||
XCTAssertEqual(DataType.pointer, controlByte.type)
|
||||
XCTAssertEqual(UInt32((definitionByte & 0b0001_1000) &>> 3) + 1, controlByte.payloadSize)
|
||||
XCTAssertEqual(1, controlByte.definitionSize)
|
||||
XCTAssertEqual(definition, controlByte.definition)
|
||||
XCTAssertEqual(definitionByte & 0b0000_0111, controlByte.strayBits)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// MARK: Utilities for the effective tests of this unit.
|
||||
|
||||
fileprivate let dataTypes: [DataType] = (1...255)
|
||||
.compactMap({ DataType(rawValue: $0) })
|
||||
.filter({ $0 != DataType.pointer })
|
||||
fileprivate let nonExtendedRawValues: [DataType.RawValue] = dataTypes
|
||||
.filter({ $0.rawValue <= 7 })
|
||||
.map({ $0.rawValue })
|
||||
fileprivate let extendedRawValues: [DataType.RawValue] = dataTypes
|
||||
.filter({ $0.rawValue > 7 })
|
||||
.map({ $0.rawValue })
|
||||
|
||||
fileprivate typealias PayloadSpec = (
|
||||
validValueRange: Range<Int>,
|
||||
addedValue: Int,
|
||||
sizeDefinition: UInt8
|
||||
)
|
||||
|
||||
fileprivate typealias ByteSizesTestDefinition = (
|
||||
payloadSize: UInt32,
|
||||
definitionSize: UInt8,
|
||||
input: Data
|
||||
)
|
||||
|
||||
/// TODO: Ugly piece of code, but it works. I could revisit in the future to refactor it.
|
||||
fileprivate func payloadSizeTestDefiner(
|
||||
rawValues: [DataType.RawValue],
|
||||
baseDefinitionSize: UInt8,
|
||||
createLeadingBytes: @escaping (UInt8, UInt8) -> Data
|
||||
) -> (PayloadSpec) -> [ByteSizesTestDefinition] {
|
||||
return { payloadSpec in
|
||||
rawValues
|
||||
.reduce([]) { byteSequence, typeDefinition in
|
||||
byteSequence + payloadSpec.validValueRange.map({ expectedByteCount in
|
||||
var byteCountDefinition = expectedByteCount - payloadSpec.addedValue
|
||||
let addedSizeDefinition = payloadSpec.sizeDefinition & 0b0000_0011
|
||||
return (
|
||||
payloadSize: UInt32(expectedByteCount),
|
||||
definitionSize: addedSizeDefinition + baseDefinitionSize,
|
||||
input: createLeadingBytes(
|
||||
payloadSpec.sizeDefinition,
|
||||
typeDefinition
|
||||
) + Data(
|
||||
bytes: &byteCountDefinition,
|
||||
count: Int(addedSizeDefinition)
|
||||
)
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate let createNonExtended = payloadSizeTestDefiner(
|
||||
rawValues: nonExtendedRawValues,
|
||||
baseDefinitionSize: 1,
|
||||
createLeadingBytes: { Data([UInt8($0) | ($1 << 5)]) }
|
||||
)
|
||||
|
||||
fileprivate let createExtended = payloadSizeTestDefiner(
|
||||
rawValues: extendedRawValues,
|
||||
baseDefinitionSize: 2,
|
||||
createLeadingBytes: { Data([UInt8($0), $1 - 7]) }
|
||||
)
|
||||
|
||||
fileprivate let payloadSpecs: [PayloadSpec] = [
|
||||
(validValueRange: 29..<285, addedValue: 29, sizeDefinition: 29),
|
||||
(validValueRange: 285..<1000, addedValue: 285, sizeDefinition: 30),
|
||||
(validValueRange: 25_000..<30_000, addedValue: 285, sizeDefinition: 30),
|
||||
(validValueRange: 64_000..<65_821, addedValue: 285, sizeDefinition: 30),
|
||||
(validValueRange: 65_821..<66_000, addedValue: 65_821, sizeDefinition: 31),
|
||||
(validValueRange: 2_000_000..<2_000_500, addedValue: 65_821, sizeDefinition: 31),
|
||||
(validValueRange: 16_843_000..<16_843_037, addedValue: 65_821, sizeDefinition: 31)
|
||||
]
|
||||
|
||||
fileprivate let payloadSizeTestDefinitions: [ByteSizesTestDefinition] =
|
||||
payloadSpecs
|
||||
.map({ (extended: createExtended($0), nonExtended: createNonExtended($0)) })
|
||||
.reduce([]) { result, testDefinitions in
|
||||
result + testDefinitions.extended + testDefinitions.nonExtended
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
import Foundation
|
||||
import XCTest
|
||||
@testable import MaxMindDecoder
|
||||
|
||||
class DecoderDoubleTest: XCTestCase {
|
||||
|
||||
// TODO : Little Endian case coverage
|
||||
private let decoder = Decoder(inputEndianness: .big)
|
||||
|
||||
func testDecodeAsDouble() {
|
||||
decoder.decodeAsDouble(bytes: Data())
|
||||
}
|
||||
|
||||
}
|
|
@ -1,124 +0,0 @@
|
|||
import Foundation
|
||||
import XCTest
|
||||
@testable import MaxMindDecoder
|
||||
|
||||
class DecoderPointerTest: XCTestCase {
|
||||
// TODO : Little endian decoding also deserves coverage
|
||||
private let bigEndianDecoder = Decoder(inputEndianness: .big)
|
||||
|
||||
func testDecodeAsPointerpointerSize1() {
|
||||
XCTAssertEqual(
|
||||
OutputData.pointer(1),
|
||||
bigEndianDecoder.decodeAsPointer(Data([0b0000_0001]), strayBits: 0)
|
||||
)
|
||||
XCTAssertEqual(
|
||||
OutputData.pointer(1793),
|
||||
bigEndianDecoder.decodeAsPointer(Data([0b0000_0001]), strayBits: 0b0000_0111)
|
||||
)
|
||||
XCTAssertEqual(
|
||||
OutputData.pointer(1993),
|
||||
bigEndianDecoder.decodeAsPointer(Data([0b1100_1001]), strayBits: 0b0000_0111)
|
||||
)
|
||||
}
|
||||
|
||||
func testDecodeAsPointerpointerSize2() {
|
||||
XCTAssertEqual(
|
||||
OutputData.pointer(2049),
|
||||
bigEndianDecoder.decodeAsPointer(
|
||||
Data([0b0000_0000, 0b0000_0001]),
|
||||
strayBits: 0
|
||||
)
|
||||
)
|
||||
XCTAssertEqual(
|
||||
OutputData.pointer(2304),
|
||||
bigEndianDecoder.decodeAsPointer(
|
||||
Data([0b0000_0001, 0b0000_0000]),
|
||||
strayBits: 0
|
||||
)
|
||||
)
|
||||
XCTAssertEqual(
|
||||
OutputData.pointer(460801),
|
||||
bigEndianDecoder.decodeAsPointer(
|
||||
Data([0b0000_0000, 0b0000_0001]),
|
||||
strayBits: 0b0000_0111
|
||||
)
|
||||
)
|
||||
XCTAssertEqual(
|
||||
OutputData.pointer(461056),
|
||||
bigEndianDecoder.decodeAsPointer(
|
||||
Data([0b0000_0001, 0b0000_0000]),
|
||||
strayBits: 0b0000_0111
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
func testDecodeAsPointerpointerSize3() {
|
||||
XCTAssertEqual(
|
||||
OutputData.pointer(526337),
|
||||
bigEndianDecoder.decodeAsPointer(
|
||||
Data([0b0000_0000, 0b0000_0000, 0b0000_0001]),
|
||||
strayBits: 0
|
||||
)
|
||||
)
|
||||
XCTAssertEqual(
|
||||
OutputData.pointer(591872),
|
||||
bigEndianDecoder.decodeAsPointer(
|
||||
Data([0b0000_0001, 0b0000_0000, 0b0000_0000]),
|
||||
strayBits: 0
|
||||
)
|
||||
)
|
||||
XCTAssertEqual(
|
||||
OutputData.pointer(117966849),
|
||||
bigEndianDecoder.decodeAsPointer(
|
||||
Data([0b0000_0000, 0b0000_0000, 0b0000_0001]),
|
||||
strayBits: 0b0000_0111
|
||||
)
|
||||
)
|
||||
XCTAssertEqual(
|
||||
OutputData.pointer(118032384),
|
||||
bigEndianDecoder.decodeAsPointer(
|
||||
Data([0b0000_0001, 0b0000_0000, 0b0000_0000]),
|
||||
strayBits: 0b0000_0111
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
func testDecodeAsPointerpointerSize4() {
|
||||
XCTAssertEqual(
|
||||
OutputData.pointer(1),
|
||||
bigEndianDecoder.decodeAsPointer(
|
||||
Data([0b0000_0000, 0b0000_0000, 0b0000_0000, 0b0000_0001]),
|
||||
strayBits: 0
|
||||
)
|
||||
)
|
||||
XCTAssertEqual(
|
||||
OutputData.pointer(16777216),
|
||||
bigEndianDecoder.decodeAsPointer(
|
||||
Data([0b0000_0001, 0b0000_0000, 0b0000_0000, 0b0000_0000]),
|
||||
strayBits: 0
|
||||
)
|
||||
)
|
||||
XCTAssertEqual(
|
||||
OutputData.pointer(1),
|
||||
bigEndianDecoder.decodeAsPointer(
|
||||
Data([0b0000_0000, 0b0000_0000, 0b0000_0000, 0b0000_0001]),
|
||||
strayBits: 0b0000_0111
|
||||
)
|
||||
)
|
||||
XCTAssertEqual(
|
||||
OutputData.pointer(16777216),
|
||||
bigEndianDecoder.decodeAsPointer(
|
||||
Data([0b0000_0001, 0b0000_0000, 0b0000_0000, 0b0000_0000]),
|
||||
strayBits: 0b0000_0111
|
||||
)
|
||||
)
|
||||
XCTAssertEqual(
|
||||
OutputData.pointer(4294967295),
|
||||
bigEndianDecoder.decodeAsPointer(
|
||||
Data([0b1111_1111, 0b1111_1111, 0b1111_1111, 0b1111_1111]),
|
||||
strayBits: 0b0000_0111
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
import Foundation
|
||||
import XCTest
|
||||
@testable import MaxMindDecoder
|
||||
|
||||
class DecoderStringTest: XCTestCase {
|
||||
/// TODO: though MaxMindDB is "big endian" as per the docs, it'd be nice to prepare it for little endian utf-8
|
||||
private let decoder = Decoder(inputEndianness: .big)
|
||||
|
||||
func testDecodeAsString() {
|
||||
let testStrings = [
|
||||
"some test string",
|
||||
"another test string",
|
||||
"test"
|
||||
]
|
||||
for (input, expected) in testStrings.map({ ($0.data(using: .utf8)!, $0) }) {
|
||||
XCTAssertEqual(OutputData.utf8String(expected), decoder.decodeAsString(input))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,85 +0,0 @@
|
|||
import Foundation
|
||||
import XCTest
|
||||
@testable import MaxMindDecoder
|
||||
|
||||
class MaxMindDecoderAnyTest: XCTestCase {
|
||||
|
||||
// TODO : little endian cases to be covered
|
||||
private let bigEndianDecoder = MaxMindDecoder(inputEndianness: .big)
|
||||
private let littleEndianDecoder = MaxMindDecoder(inputEndianness: .little)
|
||||
|
||||
// func testDecode_any_asString_fromData() {
|
||||
// let string28BytesLong = ControlByte(bytes: Data([0b0101_1100]))!
|
||||
// let expectedString = "Hello World Hello World test"
|
||||
// let decoded = bigEndianDecoder.decode(
|
||||
// expectedString.data(using: .utf8)!,
|
||||
// as: string28BytesLong
|
||||
// )
|
||||
// XCTAssertEqual(expectedString, decoded as? String)
|
||||
// }
|
||||
|
||||
func testDecode_any_asBytes_fromData() {
|
||||
let bytes5BytesLong = ControlByte(bytes: Data([0b1000_0101]))!
|
||||
let expectedBytes = Data(repeating: 0b1111_1111, count: 5)
|
||||
let decoded = bigEndianDecoder.decode(expectedBytes, as: bytes5BytesLong)
|
||||
XCTAssertEqual(expectedBytes, decoded as? Data)
|
||||
}
|
||||
|
||||
func testDecode_any_asUInt16_fromData() {
|
||||
let uInt16_1ByteLong = ControlByte(bytes: Data([0b1010_0001]))!
|
||||
let encoded = Data([0b0111_1011])
|
||||
let decoded = bigEndianDecoder.decode(encoded, as: uInt16_1ByteLong)
|
||||
XCTAssertEqual(UInt16(123), decoded as? UInt16)
|
||||
}
|
||||
|
||||
func testDecode_any_asUInt32_fromData() {
|
||||
let uInt32_1ByteLong = ControlByte(bytes: Data([0b1100_0001]))!
|
||||
let encoded = Data([0b0111_1011])
|
||||
let decoded = bigEndianDecoder.decode(encoded, as: uInt32_1ByteLong)
|
||||
XCTAssertEqual(UInt32(123), decoded as? UInt32)
|
||||
}
|
||||
|
||||
func testDecode_any_asUInt64_fromData() {
|
||||
let uInt64_1ByteLong = ControlByte(bytes: Data([0b0000_0001, 0b0000_0010]))!
|
||||
let encoded = Data([0b0111_1011])
|
||||
let decoded = bigEndianDecoder.decode(encoded, as: uInt64_1ByteLong)
|
||||
XCTAssertEqual(UInt64(123), decoded as? UInt64)
|
||||
}
|
||||
|
||||
func testDecode_any_asInt32_fromData() {
|
||||
let int32_1ByteLong = ControlByte(bytes: Data([0b0000_0001, 0b0000_0001]))!
|
||||
let encoded = Data([0b1000_0100])
|
||||
let decoded = bigEndianDecoder.decode(encoded, as: int32_1ByteLong)
|
||||
XCTAssertEqual(Int32(-124), decoded as? Int32)
|
||||
}
|
||||
|
||||
func testDecode_any_asBool_fromData() {
|
||||
let bool_1ByteLong = ControlByte(bytes: Data([0b0000_0001, 0b0000_0111]))!
|
||||
let encoded = Data([])
|
||||
let decoded = bigEndianDecoder.decode(encoded, as: bool_1ByteLong)
|
||||
XCTAssertEqual(true, decoded as? Bool)
|
||||
}
|
||||
|
||||
// func testDecode_any_asArray_fromData() {
|
||||
// let array = ControlByte(bytes: Data([0b00001000, 0b00000100]))!
|
||||
// let encoded = Data(
|
||||
// [0b01000010, 0b01100100, 0b01100101, 0b01000010,
|
||||
// 0b01100101, 0b01101110, 0b01000010, 0b01100101,
|
||||
// 0b01110011, 0b01000010, 0b01100110, 0b01110010,
|
||||
// 0b01000010, 0b01101010, 0b01100001, 0b01000101,
|
||||
// 0b01110000, 0b01110100, 0b00101101, 0b01000010,
|
||||
// 0b01010010, 0b01000010, 0b01110010, 0b01110101,
|
||||
// 0b01000101, 0b01111010, 0b01101000, 0b00101101,
|
||||
// 0b01000011, 0b01001110
|
||||
// ]
|
||||
// )
|
||||
// let expectedValues = ["de", "en", "es", "fr", "ja", "pt-BR", "ru", "zh-CN"]
|
||||
// let decodedViaAny = bigEndianDecoder.decode(encoded, as: array) as? [Any]
|
||||
// let decodedViaDirect: [Any] = bigEndianDecoder.decode(encoded, size: Int(array.payloadSize))
|
||||
//
|
||||
// XCTAssertEqual(expectedValues, decodedViaAny as? [String])
|
||||
// XCTAssertEqual(expectedValues, decodedViaDirect as? [String])
|
||||
// XCTAssertEqual(decodedViaAny as? [String], decodedViaDirect as? [String])
|
||||
// }
|
||||
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
import Foundation
|
||||
import XCTest
|
||||
@testable import MaxMindDecoder
|
||||
|
||||
class MaxMindDecoderArrayTest: XCTestCase {
|
||||
|
||||
private let bigEndianDecoder = MaxMindDecoder(inputEndianness: .big)
|
||||
|
||||
// func testDecode_array_byData() {
|
||||
// let rawData = Data(
|
||||
// [0b01000010, 0b01100100, 0b01100101, 0b01000010,
|
||||
// 0b01100101, 0b01101110, 0b01000010, 0b01100101,
|
||||
// 0b01110011, 0b01000010, 0b01100110, 0b01110010,
|
||||
// 0b01000010, 0b01101010, 0b01100001, 0b01000101,
|
||||
// 0b01110000, 0b01110100, 0b00101101, 0b01000010,
|
||||
// 0b01010010, 0b01000010, 0b01110010, 0b01110101,
|
||||
// 0b01000101, 0b01111010, 0b01101000, 0b00101101,
|
||||
// 0b01000011, 0b01001110
|
||||
// ]
|
||||
// )
|
||||
// let anies: [Any] = bigEndianDecoder.decode(rawData, size: 8)
|
||||
// XCTAssertEqual(
|
||||
// ["de", "en", "es", "fr", "ja", "pt-BR", "ru", "zh-CN"],
|
||||
// anies as? [String]
|
||||
// )
|
||||
// }
|
||||
|
||||
// func testDecode_array_byIterator() {
|
||||
// let rawData = Data(
|
||||
// [0b01000010, 0b01100100, 0b01100101, 0b01000010,
|
||||
// 0b01100101, 0b01101110, 0b01000010, 0b01100101,
|
||||
// 0b01110011, 0b01000010, 0b01100110, 0b01110010,
|
||||
// 0b01000010, 0b01101010, 0b01100001, 0b01000101,
|
||||
// 0b01110000, 0b01110100, 0b00101101, 0b01000010,
|
||||
// 0b01010010, 0b01000010, 0b01110010, 0b01110101,
|
||||
// 0b01000101, 0b01111010, 0b01101000, 0b00101101,
|
||||
// 0b01000011, 0b01001110
|
||||
// ]
|
||||
// )
|
||||
// let iterator = MaxMindIterator(rawData)!
|
||||
// let anies: [Any] = bigEndianDecoder.decode(iterator, size: 8)
|
||||
// XCTAssertEqual(
|
||||
// ["de", "en", "es", "fr", "ja", "pt-BR", "ru", "zh-CN"],
|
||||
// anies as? [String]
|
||||
// )
|
||||
// }
|
||||
|
||||
}
|
|
@ -1,197 +0,0 @@
|
|||
import Foundation
|
||||
import XCTest
|
||||
@testable import MaxMindDecoder
|
||||
|
||||
class MaxMindDecoderMapTest: XCTestCase {
|
||||
|
||||
private let bigEndianDecoder = MaxMindDecoder(inputEndianness: .big)
|
||||
|
||||
func assertMetadataEquality(
|
||||
_ expected: [String: Any],
|
||||
_ actual: [String: Any],
|
||||
|
||||
file: StaticString = #file,
|
||||
line: UInt = #line
|
||||
) {
|
||||
for (key, _) in expected {
|
||||
XCTAssert(
|
||||
actual.keys.contains(key),
|
||||
"Parsed binary dictionary should have had an entry with key \"\(key)\"",
|
||||
file: file,
|
||||
line: line
|
||||
)
|
||||
}
|
||||
|
||||
XCTAssertEqual(
|
||||
expected["record_size"] as? UInt16,
|
||||
actual["record_size"] as? UInt16,
|
||||
"record_size: \(expected["record_size"] ?? "nil") ≠ \(actual["record_size"] ?? "nil")",
|
||||
file: file,
|
||||
line: line
|
||||
)
|
||||
XCTAssertEqual(
|
||||
expected["languages"] as? [String],
|
||||
actual["languages"] as? [String],
|
||||
"languages: \(expected["languages"] ?? "nil") ≠ \(actual["languages"] ?? "nil")",
|
||||
file: file,
|
||||
line: line
|
||||
)
|
||||
XCTAssertEqual(
|
||||
expected["database_type"] as? String,
|
||||
actual["database_type"] as? String,
|
||||
"database_type: \(expected["database_type"] ?? "nil") ≠ \(actual["database_type"] ?? "nil")",
|
||||
file: file,
|
||||
line: line
|
||||
)
|
||||
XCTAssertEqual(
|
||||
expected["ip_version"] as? UInt16,
|
||||
actual["ip_version"] as? UInt16,
|
||||
"ip_version: \(expected["ip_version"] ?? "nil") ≠ \(actual["ip_version"] ?? "nil")",
|
||||
file: file,
|
||||
line: line
|
||||
)
|
||||
XCTAssertEqual(
|
||||
expected["build_epoch"] as? UInt64,
|
||||
actual["build_epoch"] as? UInt64,
|
||||
"build_epoch: \(expected["build_epoch"] ?? "nil") ≠ \(actual["build_epoch"] ?? "nil")",
|
||||
file: file,
|
||||
line: line
|
||||
)
|
||||
XCTAssertEqual(
|
||||
expected["node_count"] as? UInt32,
|
||||
actual["node_count"] as? UInt32,
|
||||
"node_count: \(expected["node_count"] ?? "nil") ≠ \(actual["node_count"] ?? "nil")",
|
||||
file: file,
|
||||
line: line
|
||||
)
|
||||
XCTAssertEqual(
|
||||
expected["binary_format_minor_version"] as? UInt16,
|
||||
actual["binary_format_minor_version"] as? UInt16,
|
||||
"binary_format_minor_version: \(expected["binary_format_minor_version"] ?? "nil") ≠ \(actual["binary_format_minor_version"] ?? "nil")",
|
||||
file: file,
|
||||
line: line
|
||||
)
|
||||
XCTAssertEqual(
|
||||
expected["description"] as? [String: String],
|
||||
actual["description"] as? [String: String],
|
||||
"description: \(expected["description"] ?? "nil") ≠ \(actual["description"] ?? "nil")",
|
||||
file: file,
|
||||
line: line
|
||||
)
|
||||
XCTAssertEqual(
|
||||
expected["binary_format_major_version"] as? UInt16,
|
||||
actual["binary_format_major_version"] as? UInt16,
|
||||
"binary_format_major_version: \(expected["binary_format_major_version"] ?? "nil") ≠ \(actual["binary_format_major_version"] ?? "nil")",
|
||||
file: file,
|
||||
line: line
|
||||
)
|
||||
}
|
||||
|
||||
// func testDecode_dataToDictionary() {
|
||||
// let actualMaxMindMetaDataDictionary: [String: Any] = bigEndianDecoder.decode(maxMindMetaData, size: 9)
|
||||
// assertMetadataEquality(
|
||||
// expectedMaxMindMetaDataDictionary,
|
||||
// actualMaxMindMetaDataDictionary
|
||||
// )
|
||||
// }
|
||||
|
||||
// func testDecode_iteratorToDictionary() {
|
||||
// guard let iterator = MaxMindIterator(maxMindMetaData) else {
|
||||
// XCTFail("MaxMind Meta Data is a valid input, yet iterator was not created.")
|
||||
// return
|
||||
// }
|
||||
// let actualMaxMindMetaDataDictionary: [String: Any] = bigEndianDecoder.decode(iterator, size: 9)
|
||||
// assertMetadataEquality(
|
||||
// expectedMaxMindMetaDataDictionary,
|
||||
// actualMaxMindMetaDataDictionary
|
||||
// )
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
fileprivate let expectedMaxMindMetaDataDictionary: [String: Any] = [
|
||||
"record_size": UInt16(24),
|
||||
"languages": [
|
||||
"de",
|
||||
"en",
|
||||
"es",
|
||||
"fr",
|
||||
"ja",
|
||||
"pt-BR",
|
||||
"ru",
|
||||
"zh-CN"
|
||||
],
|
||||
"database_type": "GeoLite2-Country",
|
||||
"ip_version": UInt16(6),
|
||||
"build_epoch": UInt64(1587472614),
|
||||
"node_count": UInt32(618459),
|
||||
"binary_format_minor_version": UInt16(0),
|
||||
"description": [
|
||||
"en": "GeoLite2 Country database"
|
||||
],
|
||||
"binary_format_major_version": UInt16(2)
|
||||
]
|
||||
|
||||
fileprivate let maxMindMetaData = Data(
|
||||
[
|
||||
0b01011011, 0b01100010, 0b01101001, 0b01101110,
|
||||
0b01100001, 0b01110010, 0b01111001, 0b01011111,
|
||||
0b01100110, 0b01101111, 0b01110010, 0b01101101,
|
||||
0b01100001, 0b01110100, 0b01011111, 0b01101101,
|
||||
0b01100001, 0b01101010, 0b01101111, 0b01110010,
|
||||
0b01011111, 0b01110110, 0b01100101, 0b01110010,
|
||||
0b01110011, 0b01101001, 0b01101111, 0b01101110,
|
||||
0b10100001, 0b00000010, 0b01011011, 0b01100010,
|
||||
0b01101001, 0b01101110, 0b01100001, 0b01110010,
|
||||
0b01111001, 0b01011111, 0b01100110, 0b01101111,
|
||||
0b01110010, 0b01101101, 0b01100001, 0b01110100,
|
||||
0b01011111, 0b01101101, 0b01101001, 0b01101110,
|
||||
0b01101111, 0b01110010, 0b01011111, 0b01110110,
|
||||
0b01100101, 0b01110010, 0b01110011, 0b01101001,
|
||||
0b01101111, 0b01101110, 0b10100000, 0b01001011,
|
||||
0b01100010, 0b01110101, 0b01101001, 0b01101100,
|
||||
0b01100100, 0b01011111, 0b01100101, 0b01110000,
|
||||
0b01101111, 0b01100011, 0b01101000, 0b00000100,
|
||||
0b00000010, 0b01011110, 0b10011110, 0b11101000,
|
||||
0b11100110, 0b01001101, 0b01100100, 0b01100001,
|
||||
0b01110100, 0b01100001, 0b01100010, 0b01100001,
|
||||
0b01110011, 0b01100101, 0b01011111, 0b01110100,
|
||||
0b01111001, 0b01110000, 0b01100101, 0b01010000,
|
||||
0b01000111, 0b01100101, 0b01101111, 0b01001100,
|
||||
0b01101001, 0b01110100, 0b01100101, 0b00110010,
|
||||
0b00101101, 0b01000011, 0b01101111, 0b01110101,
|
||||
0b01101110, 0b01110100, 0b01110010, 0b01111001,
|
||||
0b01001011, 0b01100100, 0b01100101, 0b01110011,
|
||||
0b01100011, 0b01110010, 0b01101001, 0b01110000,
|
||||
0b01110100, 0b01101001, 0b01101111, 0b01101110,
|
||||
0b11100001, 0b01000010, 0b01100101, 0b01101110,
|
||||
0b01011001, 0b01000111, 0b01100101, 0b01101111,
|
||||
0b01001100, 0b01101001, 0b01110100, 0b01100101,
|
||||
0b00110010, 0b00100000, 0b01000011, 0b01101111,
|
||||
0b01110101, 0b01101110, 0b01110100, 0b01110010,
|
||||
0b01111001, 0b00100000, 0b01100100, 0b01100001,
|
||||
0b01110100, 0b01100001, 0b01100010, 0b01100001,
|
||||
0b01110011, 0b01100101, 0b01001010, 0b01101001,
|
||||
0b01110000, 0b01011111, 0b01110110, 0b01100101,
|
||||
0b01110010, 0b01110011, 0b01101001, 0b01101111,
|
||||
0b01101110, 0b10100001, 0b00000110, 0b01001001,
|
||||
0b01101100, 0b01100001, 0b01101110, 0b01100111,
|
||||
0b01110101, 0b01100001, 0b01100111, 0b01100101,
|
||||
0b01110011, 0b00001000, 0b00000100, 0b01000010,
|
||||
0b01100100, 0b01100101, 0b01000010, 0b01100101,
|
||||
0b01101110, 0b01000010, 0b01100101, 0b01110011,
|
||||
0b01000010, 0b01100110, 0b01110010, 0b01000010,
|
||||
0b01101010, 0b01100001, 0b01000101, 0b01110000,
|
||||
0b01110100, 0b00101101, 0b01000010, 0b01010010,
|
||||
0b01000010, 0b01110010, 0b01110101, 0b01000101,
|
||||
0b01111010, 0b01101000, 0b00101101, 0b01000011,
|
||||
0b01001110, 0b01001010, 0b01101110, 0b01101111,
|
||||
0b01100100, 0b01100101, 0b01011111, 0b01100011,
|
||||
0b01101111, 0b01110101, 0b01101110, 0b01110100,
|
||||
0b11000011, 0b00001001, 0b01101111, 0b11011011,
|
||||
0b01001011, 0b01110010, 0b01100101, 0b01100011,
|
||||
0b01101111, 0b01110010, 0b01100100, 0b01011111,
|
||||
0b01110011, 0b01101001, 0b01111010, 0b01100101,
|
||||
0b10100001, 0b00011000
|
||||
]
|
||||
)
|
|
@ -1,139 +0,0 @@
|
|||
import Foundation
|
||||
import XCTest
|
||||
@testable import MaxMindDecoder
|
||||
|
||||
class MaxMindDecoderNumericTest: XCTestCase {
|
||||
|
||||
private let bigEndianDecoder = MaxMindDecoder(inputEndianness: .big)
|
||||
private let littleEndianDecoder = MaxMindDecoder(inputEndianness: .little)
|
||||
|
||||
func testDecode_uInt16() {
|
||||
for (expected, input) in testSpecs_uInt16 {
|
||||
assertDecodedValue(expected, bigEndianDecoder.decode, data: input)
|
||||
}
|
||||
for (expected, input) in testSpecs_uInt16 {
|
||||
assertDecodedValue(
|
||||
expected,
|
||||
littleEndianDecoder.decode,
|
||||
data: Data(input.reversed())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func testDecode_uInt32() {
|
||||
for (expected, input) in testSpecs_uInt32 {
|
||||
assertDecodedValue(expected, bigEndianDecoder.decode, data: input)
|
||||
}
|
||||
for (expected, input) in testSpecs_uInt32 {
|
||||
assertDecodedValue(
|
||||
expected,
|
||||
littleEndianDecoder.decode,
|
||||
data: Data(input.reversed())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func testDecode_uInt64() {
|
||||
for (expected, input) in testSpecs_uInt64 {
|
||||
assertDecodedValue(expected, bigEndianDecoder.decode, data: input)
|
||||
}
|
||||
for (expected, input) in testSpecs_uInt64 {
|
||||
assertDecodedValue(
|
||||
expected,
|
||||
littleEndianDecoder.decode,
|
||||
data: Data(input.reversed())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func testDecode_int32() {
|
||||
for (expected, input) in testSpecs_int32 {
|
||||
assertDecodedValue(expected, bigEndianDecoder.decode, data: input)
|
||||
}
|
||||
for (expected, input) in testSpecs_int32 {
|
||||
assertDecodedValue(
|
||||
expected,
|
||||
littleEndianDecoder.decode,
|
||||
data: Data(input.reversed())
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate typealias TestSpec<T> = (expected: T, input: Data)
|
||||
|
||||
fileprivate let testSpecs_uInt16: [TestSpec<UInt16>] = [
|
||||
(expected: 0, input: Data()),
|
||||
(expected: 0, input: Data(count: 1)),
|
||||
(expected: 0, input: Data(count: 2)),
|
||||
(expected: 0, input: Data(count: 3)),
|
||||
(expected: 0, input: Data(count: 4)),
|
||||
(expected: 0, input: Data(count: 5)),
|
||||
(expected: 255, input: Data([0b1111_1111])),
|
||||
(expected: 255, input: Data(count: 1) + Data([0b1111_1111])),
|
||||
(expected: 14200, input: Data([0b0011_0111, 0b0111_1000])),
|
||||
(expected: 14200, input: Data(count: 14) + Data([0b0011_0111, 0b0111_1000]))
|
||||
]
|
||||
|
||||
fileprivate let testSpecs_uInt32: [TestSpec<UInt32>] = [
|
||||
(expected: 0, input: Data()),
|
||||
(expected: 0, input: Data(count: 1)),
|
||||
(expected: 0, input: Data(count: 2)),
|
||||
(expected: 0, input: Data(count: 3)),
|
||||
(expected: 0, input: Data(count: 4)),
|
||||
(expected: 0, input: Data(count: 5)),
|
||||
(expected: 4294967295, input: Data([0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF])),
|
||||
(expected: 4294967295, input: Data(count: 1) + Data([0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF])),
|
||||
(expected: 14200, input: Data([0b0011_0111, 0b0111_1000])),
|
||||
(expected: 14200, input: Data(count: 14) + Data([0b0011_0111, 0b0111_1000])),
|
||||
(expected: 16702650, input: Data([0xFE, 0xDC, 0xBA])),
|
||||
(expected: 16702650, input: Data(count: 14) + Data([0xFE, 0xDC, 0xBA]))
|
||||
]
|
||||
|
||||
fileprivate let testSpecs_int32: [TestSpec<Int32>] = [
|
||||
(expected: 0, input: Data()),
|
||||
(expected: 0, input: Data(count: 1)),
|
||||
(expected: 0, input: Data(count: 2)),
|
||||
(expected: 0, input: Data(count: 3)),
|
||||
(expected: 0, input: Data(count: 4)),
|
||||
(expected: 0, input: Data(count: 5)),
|
||||
(expected: 127, input: Data([0b0111_1111])),
|
||||
(expected: -127, input: Data([0b1111_1111, 0b1111_1111, 0b1000_0001])),
|
||||
(expected: 32767, input: Data([0b0111_1111, 0b1111_1111])),
|
||||
(expected: -32767, input: Data([0b1111_1111, 0b1000_0000, 0b0000_0001])),
|
||||
(expected: -2147483647, input: Data([0x80, 0x00, 0x00, 0x01])),
|
||||
(expected: 2147483647, input: Data([0x7F, 0xFF, 0xFF, 0xFF])),
|
||||
(expected: 2147483647, input: Data(count: 1) + Data([0x00, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF])),
|
||||
(expected: 14200, input: Data([0b0011_0111, 0b0111_1000])),
|
||||
(expected: 14200, input: Data(count: 14) + Data([0b0011_0111, 0b0111_1000])),
|
||||
(expected: -14200, input: Data([0b1111_1111, 0b1100_1000, 0b1000_1000])),
|
||||
(expected: 16702650, input: Data([0x00, 0xFE, 0xDC, 0xBA])),
|
||||
(expected: 16702650, input: Data(count: 14) + Data([0x00, 0xFE, 0xDC, 0xBA]))
|
||||
]
|
||||
|
||||
fileprivate let testSpecs_uInt64: [TestSpec<UInt64>] = [
|
||||
(expected: 0, input: Data()),
|
||||
(expected: 0, input: Data(count: 1)),
|
||||
(expected: 0, input: Data(count: 2)),
|
||||
(expected: 0, input: Data(count: 3)),
|
||||
(expected: 0, input: Data(count: 4)),
|
||||
(expected: 0, input: Data(count: 5)),
|
||||
(expected: 255, input: Data([0b1111_1111])),
|
||||
(expected: 255, input: Data(count: 1) + Data([0b1111_1111])),
|
||||
(expected: 14200, input: Data([0b0011_0111, 0b0111_1000])),
|
||||
(expected: 14200, input: Data(count: 14) + Data([0b0011_0111, 0b0111_1000])),
|
||||
(expected: 16702650, input: Data([0xFE, 0xDC, 0xBA])),
|
||||
(expected: 16702650, input: Data(count: 14) + Data([0xFE, 0xDC, 0xBA])),
|
||||
(expected: 280223976814164, input: Data([0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54])),
|
||||
(expected: 280223976814164, input: Data(count: 14) + Data([0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54]))
|
||||
]
|
||||
|
||||
fileprivate func assertDecodedValue<T>(
|
||||
_ expected: T,
|
||||
_ decode: (Data) -> T,
|
||||
data: Data,
|
||||
file: StaticString = #file,
|
||||
line: UInt = #line
|
||||
) where T: Equatable {
|
||||
XCTAssertEqual(expected, decode(data), file: file, line: line)
|
||||
}
|
|
@ -1,419 +0,0 @@
|
|||
import Foundation
|
||||
import XCTest
|
||||
@testable import MaxMindDecoder
|
||||
|
||||
class MaxMindIteratorTest: XCTestCase {
|
||||
|
||||
private let decoder = MaxMindDecoder(inputEndianness: .big)
|
||||
|
||||
func testInit_returnsNilIfDataIsEmpty() {
|
||||
XCTAssertNil(MaxMindIterator(Data()))
|
||||
}
|
||||
|
||||
func assertNext<T>(
|
||||
_ iterator: MaxMindIterator,
|
||||
type: DataType,
|
||||
payloadSize: UInt32,
|
||||
definitionSize: UInt8,
|
||||
expectedValue: T,
|
||||
decoder decode: (Data) -> T,
|
||||
file: StaticString = #file,
|
||||
line: UInt = #line
|
||||
) where T: Equatable {
|
||||
guard
|
||||
let controlByte = iterator.next()
|
||||
else {
|
||||
XCTFail(
|
||||
"Should have returned a control byte of Type:\(type) Payload:\(payloadSize) DefinitionSize:\(definitionSize).",
|
||||
file: file,
|
||||
line: line
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
XCTAssertEqual(type, controlByte.type, file: file, line: line)
|
||||
XCTAssertEqual(definitionSize, controlByte.definitionSize, file: file, line: line)
|
||||
XCTAssertEqual(payloadSize, controlByte.payloadSize, file: file, line: line)
|
||||
let valueBytes = iterator.next(controlByte)
|
||||
|
||||
XCTAssertEqual(expectedValue, decode(valueBytes), file: file, line: line)
|
||||
}
|
||||
|
||||
// private func assertMaxMindMetaData(_ iterator: MaxMindIterator, of data: Data) {
|
||||
// let mainMapByte = iterator.next()
|
||||
// XCTAssertEqual(DataType.map, mainMapByte?.type)
|
||||
// XCTAssertEqual(1, mainMapByte?.definitionSize)
|
||||
// XCTAssertEqual(9, mainMapByte?.payloadSize)
|
||||
//
|
||||
// assertNext(
|
||||
// iterator,
|
||||
// type: .utf8String,
|
||||
// payloadSize: 27,
|
||||
// definitionSize: 1,
|
||||
// expectedValue: "binary_format_major_version",
|
||||
// decoder: decoder.decode
|
||||
// )
|
||||
//
|
||||
// assertNext(
|
||||
// iterator,
|
||||
// type: .uInt16,
|
||||
// payloadSize: 1,
|
||||
// definitionSize: 1,
|
||||
// expectedValue: 2,
|
||||
// decoder: decoder.decode
|
||||
// )
|
||||
//
|
||||
// assertNext(
|
||||
// iterator,
|
||||
// type: .utf8String,
|
||||
// payloadSize: 27,
|
||||
// definitionSize: 1,
|
||||
// expectedValue: "binary_format_minor_version",
|
||||
// decoder: decoder.decode
|
||||
// )
|
||||
//
|
||||
// assertNext(
|
||||
// iterator,
|
||||
// type: .uInt16,
|
||||
// payloadSize: 0,
|
||||
// definitionSize: 1,
|
||||
// expectedValue: 0,
|
||||
// decoder: decoder.decode
|
||||
// )
|
||||
//
|
||||
// assertNext(
|
||||
// iterator,
|
||||
// type: .utf8String,
|
||||
// payloadSize: 11,
|
||||
// definitionSize: 1,
|
||||
// expectedValue: "build_epoch",
|
||||
// decoder: decoder.decode
|
||||
// )
|
||||
//
|
||||
// assertNext(
|
||||
// iterator,
|
||||
// type: .uInt64,
|
||||
// payloadSize: 4,
|
||||
// definitionSize: 2,
|
||||
// expectedValue: UInt64(1587472614),
|
||||
// decoder: decoder.decode
|
||||
// )
|
||||
//
|
||||
// assertNext(
|
||||
// iterator,
|
||||
// type: .utf8String,
|
||||
// payloadSize: 13,
|
||||
// definitionSize: 1,
|
||||
// expectedValue: "database_type",
|
||||
// decoder: decoder.decode
|
||||
// )
|
||||
//
|
||||
// assertNext(
|
||||
// iterator,
|
||||
// type: .utf8String,
|
||||
// payloadSize: 16,
|
||||
// definitionSize: 1,
|
||||
// expectedValue: "GeoLite2-Country",
|
||||
// decoder: decoder.decode
|
||||
// )
|
||||
//
|
||||
// assertNext(
|
||||
// iterator,
|
||||
// type: .utf8String,
|
||||
// payloadSize: 11,
|
||||
// definitionSize: 1,
|
||||
// expectedValue: "description",
|
||||
// decoder: decoder.decode
|
||||
// )
|
||||
//
|
||||
// let descriptionMapByte = iterator.next()
|
||||
// XCTAssertEqual(DataType.map, descriptionMapByte?.type)
|
||||
// XCTAssertEqual(1, descriptionMapByte?.definitionSize)
|
||||
// XCTAssertEqual(1, descriptionMapByte?.payloadSize)
|
||||
//
|
||||
// assertNext(
|
||||
// iterator,
|
||||
// type: .utf8String,
|
||||
// payloadSize: 2,
|
||||
// definitionSize: 1,
|
||||
// expectedValue: "en",
|
||||
// decoder: decoder.decode
|
||||
// )
|
||||
//
|
||||
// assertNext(
|
||||
// iterator,
|
||||
// type: .utf8String,
|
||||
// payloadSize: 25,
|
||||
// definitionSize: 1,
|
||||
// expectedValue: "GeoLite2 Country database",
|
||||
// decoder: decoder.decode
|
||||
// )
|
||||
//
|
||||
// assertNext(
|
||||
// iterator,
|
||||
// type: .utf8String,
|
||||
// payloadSize: 10,
|
||||
// definitionSize: 1,
|
||||
// expectedValue: "ip_version",
|
||||
// decoder: decoder.decode
|
||||
// )
|
||||
//
|
||||
// assertNext(
|
||||
// iterator,
|
||||
// type: .uInt16,
|
||||
// payloadSize: 1,
|
||||
// definitionSize: 1,
|
||||
// expectedValue: 6,
|
||||
// decoder: decoder.decode
|
||||
// )
|
||||
//
|
||||
// assertNext(
|
||||
// iterator,
|
||||
// type: .utf8String,
|
||||
// payloadSize: 9,
|
||||
// definitionSize: 1,
|
||||
// expectedValue: "languages",
|
||||
// decoder: decoder.decode
|
||||
// )
|
||||
//
|
||||
// let languagesArrayByte = iterator.next()
|
||||
// XCTAssertEqual(DataType.array, languagesArrayByte?.type)
|
||||
// XCTAssertEqual(2, languagesArrayByte?.definitionSize)
|
||||
// XCTAssertEqual(8, languagesArrayByte?.payloadSize)
|
||||
//
|
||||
// assertNext(
|
||||
// iterator,
|
||||
// type: .utf8String,
|
||||
// payloadSize: 2,
|
||||
// definitionSize: 1,
|
||||
// expectedValue: "de",
|
||||
// decoder: decoder.decode
|
||||
// )
|
||||
// assertNext(
|
||||
// iterator,
|
||||
// type: .utf8String,
|
||||
// payloadSize: 2,
|
||||
// definitionSize: 1,
|
||||
// expectedValue: "en",
|
||||
// decoder: decoder.decode
|
||||
// )
|
||||
// assertNext(
|
||||
// iterator,
|
||||
// type: .utf8String,
|
||||
// payloadSize: 2,
|
||||
// definitionSize: 1,
|
||||
// expectedValue: "es",
|
||||
// decoder: decoder.decode
|
||||
// )
|
||||
// assertNext(
|
||||
// iterator,
|
||||
// type: .utf8String,
|
||||
// payloadSize: 2,
|
||||
// definitionSize: 1,
|
||||
// expectedValue: "fr",
|
||||
// decoder: decoder.decode
|
||||
// )
|
||||
// assertNext(
|
||||
// iterator,
|
||||
// type: .utf8String,
|
||||
// payloadSize: 2,
|
||||
// definitionSize: 1,
|
||||
// expectedValue: "ja",
|
||||
// decoder: decoder.decode
|
||||
// )
|
||||
// assertNext(
|
||||
// iterator,
|
||||
// type: .utf8String,
|
||||
// payloadSize: 5,
|
||||
// definitionSize: 1,
|
||||
// expectedValue: "pt-BR",
|
||||
// decoder: decoder.decode
|
||||
// )
|
||||
// assertNext(
|
||||
// iterator,
|
||||
// type: .utf8String,
|
||||
// payloadSize: 2,
|
||||
// definitionSize: 1,
|
||||
// expectedValue: "ru",
|
||||
// decoder: decoder.decode
|
||||
// )
|
||||
// assertNext(
|
||||
// iterator,
|
||||
// type: .utf8String,
|
||||
// payloadSize: 5,
|
||||
// definitionSize: 1,
|
||||
// expectedValue: "zh-CN",
|
||||
// decoder: decoder.decode
|
||||
// )
|
||||
// assertNext(
|
||||
// iterator,
|
||||
// type: .utf8String,
|
||||
// payloadSize: 10,
|
||||
// definitionSize: 1,
|
||||
// expectedValue: "node_count",
|
||||
// decoder: decoder.decode
|
||||
// )
|
||||
// assertNext(
|
||||
// iterator,
|
||||
// type: .uInt32,
|
||||
// payloadSize: 3,
|
||||
// definitionSize: 1,
|
||||
// expectedValue: 618459,
|
||||
// decoder: decoder.decode
|
||||
// )
|
||||
// assertNext(
|
||||
// iterator,
|
||||
// type: .utf8String,
|
||||
// payloadSize: 11,
|
||||
// definitionSize: 1,
|
||||
// expectedValue: "record_size",
|
||||
// decoder: decoder.decode
|
||||
// )
|
||||
// assertNext(
|
||||
// iterator,
|
||||
// type: .uInt16,
|
||||
// payloadSize: 1,
|
||||
// definitionSize: 1,
|
||||
// expectedValue: 24,
|
||||
// decoder: decoder.decode
|
||||
// )
|
||||
// }
|
||||
|
||||
// func testNext() {
|
||||
// guard let iterator = MaxMindIterator(maxMindMetaData) else {
|
||||
// XCTFail("Iterator should have been creatable.")
|
||||
// return
|
||||
// }
|
||||
// assertMaxMindMetaData(iterator, of: maxMindMetaData)
|
||||
// XCTAssertNil(iterator.next())
|
||||
// XCTAssertTrue(iterator.isExhausted)
|
||||
// iterator.rewind()
|
||||
// XCTAssertFalse(iterator.isExhausted)
|
||||
// assertMaxMindMetaData(iterator, of: maxMindMetaData)
|
||||
// XCTAssertTrue(iterator.isExhausted)
|
||||
// }
|
||||
|
||||
// func testNext_skipsUnrecognizedDataType() {
|
||||
// guard let iterator = MaxMindIterator(Data([0]) + maxMindMetaData) else {
|
||||
// XCTFail("Iterator should have been creatable.")
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// assertMaxMindMetaData(iterator, of: maxMindMetaData)
|
||||
// XCTAssertNil(iterator.next())
|
||||
// XCTAssertTrue(iterator.isExhausted)
|
||||
// iterator.rewind()
|
||||
// XCTAssertFalse(iterator.isExhausted)
|
||||
// assertMaxMindMetaData(iterator, of: maxMindMetaData)
|
||||
// XCTAssertTrue(iterator.isExhausted)
|
||||
// iterator.rewind()
|
||||
// }
|
||||
|
||||
// func testSeek() {
|
||||
// let padData = Data([
|
||||
// 0b0000_0000, 0b0000_0000, 0b0000_0000, 0b0000_0000,
|
||||
// 0b0000_0000, 0b0000_0000, 0b0000_0000, 0b0000_0000,
|
||||
// 0b0000_0000, 0b0000_0000, 0b0000_0000, 0b0000_0000,
|
||||
// 0b0000_0000, 0b0000_0000, 0b0000_0000, 0b0000_0000
|
||||
// ])
|
||||
//
|
||||
// guard let iterator = MaxMindIterator(padData + maxMindMetaData) else {
|
||||
// XCTFail("Iterator should have been creatable.")
|
||||
// return
|
||||
// }
|
||||
// iterator.seek(to: padData.count)
|
||||
// assertMaxMindMetaData(iterator, of: maxMindMetaData)
|
||||
// XCTAssertNil(iterator.next())
|
||||
// XCTAssertTrue(iterator.isExhausted)
|
||||
// iterator.rewind()
|
||||
// iterator.seek(to: padData.count)
|
||||
// XCTAssertFalse(iterator.isExhausted)
|
||||
// assertMaxMindMetaData(iterator, of: maxMindMetaData)
|
||||
// XCTAssertTrue(iterator.isExhausted)
|
||||
//
|
||||
// }
|
||||
|
||||
// func testPeek() {
|
||||
// let controlByteBinary = Data([0b0101_1100])
|
||||
// let stringBinary = "Hello World Hello World test".data(using: .utf8)!
|
||||
// let iterator = MaxMindIterator(controlByteBinary + stringBinary)!
|
||||
// XCTAssertEqual(iterator.peek(at: 0), iterator.peek(at: 0))
|
||||
// XCTAssertEqual(ControlByte(bytes: controlByteBinary), iterator.peek(at: 0))
|
||||
// let controlByte = iterator.next()!
|
||||
// XCTAssertEqual(controlByte, iterator.peek(at: 0))
|
||||
// XCTAssertEqual(stringBinary, iterator.peek(controlByte, at: 1))
|
||||
// XCTAssertEqual(iterator.peek(controlByte, at: 1), iterator.peek(controlByte, at: 1))
|
||||
// XCTAssertEqual(iterator.peek(controlByte, at: 1), iterator.next(controlByte))
|
||||
// XCTAssertTrue(iterator.isExhausted)
|
||||
// XCTAssertEqual(ControlByte(bytes: controlByteBinary), iterator.peek(at: 0))
|
||||
// XCTAssertEqual(stringBinary, iterator.peek(controlByte, at: 1))
|
||||
//
|
||||
// XCTAssertNil(iterator.peek(at: 9999999))
|
||||
// XCTAssertNil(iterator.peek(controlByte, at: 9999999))
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
fileprivate let maxMindMetaData = Data(
|
||||
[
|
||||
0b11101001, 0b01011011, 0b01100010, 0b01101001,
|
||||
0b01101110, 0b01100001, 0b01110010, 0b01111001,
|
||||
0b01011111, 0b01100110, 0b01101111, 0b01110010,
|
||||
0b01101101, 0b01100001, 0b01110100, 0b01011111,
|
||||
0b01101101, 0b01100001, 0b01101010, 0b01101111,
|
||||
0b01110010, 0b01011111, 0b01110110, 0b01100101,
|
||||
0b01110010, 0b01110011, 0b01101001, 0b01101111,
|
||||
0b01101110, 0b10100001, 0b00000010, 0b01011011,
|
||||
0b01100010, 0b01101001, 0b01101110, 0b01100001,
|
||||
0b01110010, 0b01111001, 0b01011111, 0b01100110,
|
||||
0b01101111, 0b01110010, 0b01101101, 0b01100001,
|
||||
0b01110100, 0b01011111, 0b01101101, 0b01101001,
|
||||
0b01101110, 0b01101111, 0b01110010, 0b01011111,
|
||||
0b01110110, 0b01100101, 0b01110010, 0b01110011,
|
||||
0b01101001, 0b01101111, 0b01101110, 0b10100000,
|
||||
0b01001011, 0b01100010, 0b01110101, 0b01101001,
|
||||
0b01101100, 0b01100100, 0b01011111, 0b01100101,
|
||||
0b01110000, 0b01101111, 0b01100011, 0b01101000,
|
||||
0b00000100, 0b00000010, 0b01011110, 0b10011110,
|
||||
0b11101000, 0b11100110, 0b01001101, 0b01100100,
|
||||
0b01100001, 0b01110100, 0b01100001, 0b01100010,
|
||||
0b01100001, 0b01110011, 0b01100101, 0b01011111,
|
||||
0b01110100, 0b01111001, 0b01110000, 0b01100101,
|
||||
0b01010000, 0b01000111, 0b01100101, 0b01101111,
|
||||
0b01001100, 0b01101001, 0b01110100, 0b01100101,
|
||||
0b00110010, 0b00101101, 0b01000011, 0b01101111,
|
||||
0b01110101, 0b01101110, 0b01110100, 0b01110010,
|
||||
0b01111001, 0b01001011, 0b01100100, 0b01100101,
|
||||
0b01110011, 0b01100011, 0b01110010, 0b01101001,
|
||||
0b01110000, 0b01110100, 0b01101001, 0b01101111,
|
||||
0b01101110, 0b11100001, 0b01000010, 0b01100101,
|
||||
0b01101110, 0b01011001, 0b01000111, 0b01100101,
|
||||
0b01101111, 0b01001100, 0b01101001, 0b01110100,
|
||||
0b01100101, 0b00110010, 0b00100000, 0b01000011,
|
||||
0b01101111, 0b01110101, 0b01101110, 0b01110100,
|
||||
0b01110010, 0b01111001, 0b00100000, 0b01100100,
|
||||
0b01100001, 0b01110100, 0b01100001, 0b01100010,
|
||||
0b01100001, 0b01110011, 0b01100101, 0b01001010,
|
||||
0b01101001, 0b01110000, 0b01011111, 0b01110110,
|
||||
0b01100101, 0b01110010, 0b01110011, 0b01101001,
|
||||
0b01101111, 0b01101110, 0b10100001, 0b00000110,
|
||||
0b01001001, 0b01101100, 0b01100001, 0b01101110,
|
||||
0b01100111, 0b01110101, 0b01100001, 0b01100111,
|
||||
0b01100101, 0b01110011, 0b00001000, 0b00000100,
|
||||
0b01000010, 0b01100100, 0b01100101, 0b01000010,
|
||||
0b01100101, 0b01101110, 0b01000010, 0b01100101,
|
||||
0b01110011, 0b01000010, 0b01100110, 0b01110010,
|
||||
0b01000010, 0b01101010, 0b01100001, 0b01000101,
|
||||
0b01110000, 0b01110100, 0b00101101, 0b01000010,
|
||||
0b01010010, 0b01000010, 0b01110010, 0b01110101,
|
||||
0b01000101, 0b01111010, 0b01101000, 0b00101101,
|
||||
0b01000011, 0b01001110, 0b01001010, 0b01101110,
|
||||
0b01101111, 0b01100100, 0b01100101, 0b01011111,
|
||||
0b01100011, 0b01101111, 0b01110101, 0b01101110,
|
||||
0b01110100, 0b11000011, 0b00001001, 0b01101111,
|
||||
0b11011011, 0b01001011, 0b01110010, 0b01100101,
|
||||
0b01100011, 0b01101111, 0b01110010, 0b01100100,
|
||||
0b01011111, 0b01110011, 0b01101001, 0b01111010,
|
||||
0b01100101, 0b10100001, 0b00011000
|
||||
]
|
||||
)
|
|
@ -1,9 +1,8 @@
|
|||
import Foundation
|
||||
import XCTest
|
||||
@testable import Metadata
|
||||
import MaxMindDecoder
|
||||
|
||||
class DecodeFunctionTest: XCTestCase {
|
||||
class FunctionDecodeTest: XCTestCase {
|
||||
|
||||
func testInit_nilIfCantCreateIterator() {
|
||||
XCTAssertNil(
|
Loading…
Reference in New Issue