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:
Adam Rocska 2020-05-28 14:23:26 +02:00
parent d7387c8845
commit c7c3ab44db
46 changed files with 646 additions and 1863 deletions

View File

@ -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(

View File

@ -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
}
}

View File

@ -1,6 +1,6 @@
import Foundation
internal extension Data {
public extension Data {
init(_ string: String) { self.init(string.utf8) }

View File

@ -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
),

View File

@ -1,6 +1,6 @@
import Foundation
internal extension Numeric {
public extension Numeric {
init(_ data: Data, sourceEndianness: Endianness) {
var value: Self = .zero

View File

@ -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
}
}
}

View File

@ -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)
}
}

View File

@ -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)
)
}
}

View File

@ -1,5 +1,4 @@
import Foundation
import MaxMindDecoder
import Index
import Metadata

View File

@ -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
}

View File

@ -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)
)
}
}

View File

@ -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
}

View File

@ -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)
}
}
}

View File

@ -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)
}
}

View File

@ -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)
}
}

View File

@ -1,9 +0,0 @@
import Foundation
extension Decoder {
func decodeAsString(_ data: Data) -> OutputData {
return OutputData.utf8String(String(data: data, encoding: .utf8) ?? "")
}
}

View File

@ -1,7 +0,0 @@
import Foundation
enum InputData {
case pointer(bytes: Data, strayBits: UInt8)
case utf8String(bytes: Data)
case double(bytes: Data)
}

View File

@ -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 }
}

View File

@ -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
}
}
}

View File

@ -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
}
}

View File

@ -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)
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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)
}

View File

@ -1,9 +0,0 @@
import Foundation
public protocol PeekableDataSequence {
func peek(at offset: Int) -> ControlByte?
func peek(_ controlByte: ControlByte, at offset: Int) -> Data?
}

View File

@ -1,5 +1,4 @@
import Foundation
import MaxMindDecoder
public struct Metadata: Equatable {

View File

@ -1,5 +1,4 @@
import Foundation
import MaxMindDecoder
public class Reader {

View File

@ -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
)

View File

@ -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))
}

View File

@ -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
}

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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
}

View File

@ -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 }
}

View File

@ -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())
}
}

View File

@ -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
}

View File

@ -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())
}
}

View File

@ -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
)
)
}
}

View File

@ -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))
}
}
}

View File

@ -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])
// }
}

View File

@ -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]
// )
// }
}

View File

@ -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
]
)

View File

@ -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)
}

View File

@ -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
]
)

View File

@ -1,9 +1,8 @@
import Foundation
import XCTest
@testable import Metadata
import MaxMindDecoder
class DecodeFunctionTest: XCTestCase {
class FunctionDecodeTest: XCTestCase {
func testInit_nilIfCantCreateIterator() {
XCTAssertNil(