     @inlinable
     public func encode(to encoder: Encoder) throws {
         
-        switch self.type {
+        switch self.base {
         case .null:
             
             var container = encoder.singleValueContainer()
             try container.encodeNil()
             
-        case .boolean:
+        case let .boolean(value):
             
             var container = encoder.singleValueContainer()
-            try container.encode(self.boolValue!)
-            
-        case .string:
+            try container.encode(value)
+            
+        case let .string(value):
             
             var container = encoder.singleValueContainer()
-            try container.encode(self.string!)
-            
-        case .signed:
+            try container.encode(value)
+            
+        case let .signed(value):
             
             var container = encoder.singleValueContainer()
-            try container.encode(self.int64Value!)
-            
-        case .unsigned:
+            try container.encode(value)
+            
+        case let .unsigned(value):
             
             var container = encoder.singleValueContainer()
-            try container.encode(self.uint64Value!)
-            
-        case .number:
+            try container.encode(value)
+            
+        case let .number(value):
             
             var container = encoder.singleValueContainer()
-            try container.encode(self.doubleValue!)
-            
-        case .binary:
+            try container.encode(value)
+            
+        case let .binary(value):
             
             var container = encoder.singleValueContainer()
-            try container.encode(self.binary!)
-            
-        case .uuid:
+            try container.encode(value)
+            
+        case let .uuid(value):
             
             var container = encoder.singleValueContainer()
-            try container.encode(self.uuid!)
-            
-        case .array:
+            try container.encode(value)
+            
+        case let .array(value):
             
             var container = encoder.unkeyedContainer()
-            try container.encode(contentsOf: self.array!)
-            
-        case .dictionary:
+            try container.encode(contentsOf: value)
+            
+        case let .undecoded_array(value):
+            
+            var container = encoder.unkeyedContainer()
+            try container.encode(contentsOf: value)
+            
+        case let .dictionary(value):
             
             var container = encoder.container(keyedBy: CodingKey.self)
             
-            for (key, value) in self.dictionary! {
+            for (key, value) in value {
                 try container.encode(value, forKey: CodingKey(stringValue: key))
             }
-            
-        default:
-            
-            var container = encoder.singleValueContainer()
-            try container.encodeNil()
-        }
-    }
-}
+        }
+    }
+}

    case uuid
    case array
    case dictionary
    case unknown(UInt8)
}

@frozen
public struct DataPack {
    
    @usableFromInline
    let base: Base
    
    @inlinable
    init(_ base: Base) {
        self.base = base.dictionary.map { .dictionary($0) } ?? base
    }
    
    @inlinable
    public init(decode data: Data) {
        self.init(.undecoded(UndecodedObject(data: data)))
    }
    
    @inlinable
    public init(contentsOf url: URL, options: Data.ReadingOptions = []) throws {
        try self.init(decode: Data(contentsOf: url, options: options))
    }
    
    @inlinable
    public init(contentsOfFile path: String, options: Data.ReadingOptions = []) throws {
        try self.init(decode: Data(contentsOf: URL(fileURLWithPath: path), options: options))
    }
    
    @inlinable
    public init(_ value: Bool) {
        self.base = .boolean(value)
    }
    
    @inlinable
    public init(_ value: String) {
        self.base = .string(value)
    }
    
    @inlinable
    public init<S: StringProtocol>(_ value: S) {
        self.base = .string(String(value))
    }
    
    @inlinable
    public init<T: FixedWidthInteger & SignedInteger>(_ value: T) {
        self.base = .signed(Int64(value))
    }
    
    @inlinable
    public init<T: FixedWidthInteger & UnsignedInteger>(_ value: T) {
        self.base = .unsigned(UInt64(value))
    }
    
    @inlinable
    public init<T: BinaryFloatingPoint>(_ value: T) {
        self.base = .number(Double(value))
    }
    
    @inlinable
    public init(_ binary: Data) {
        self.base = .binary(binary)
    }
    
    @inlinable
    public init(_ uuid: UUID) {
        self.base = .uuid(uuid)
    }
    
    @inlinable
    public init<S: Sequence>(_ elements: S) where S.Element == DataPack {
        self.base = .array(Array(elements))
    }
    
    @inlinable
    public init(_ elements: [String: DataPack]) {
        self.base = .dictionary(elements)
    }
}

extension DataPack: ExpressibleByNilLiteral {
    
    @inlinable
    public init(nilLiteral value: Void) {
        self.base = .null
    }
}

extension DataPack: ExpressibleByBooleanLiteral {
    
    @inlinable
    public init(booleanLiteral value: BooleanLiteralType) {
        self.init(value)
    }
}

extension DataPack: ExpressibleByIntegerLiteral {
    
    @inlinable
    public init(integerLiteral value: IntegerLiteralType) {
        self.init(value)
    }
}

extension DataPack: ExpressibleByFloatLiteral {
    
    @inlinable
    public init(floatLiteral value: FloatLiteralType) {
        self.init(value)
    }
}

extension DataPack: ExpressibleByStringLiteral {
    
    @inlinable
    public init(stringLiteral value: StringLiteralType) {
        self.init(value)
    }
}

extension DataPack: ExpressibleByArrayLiteral {
    
    @inlinable
    public init(arrayLiteral elements: DataPack ...) {
        self.init(elements)
    }
}

extension DataPack: ExpressibleByDictionaryLiteral {
    
    @inlinable
    public init(dictionaryLiteral elements: (String, DataPack) ...) {
        self.init(Dictionary(uniqueKeysWithValues: elements))
    }
}

extension DataPack: CustomStringConvertible {
    
    @inlinable
    public var description: String {
        switch type {
        case .null: return "nil"
        case .boolean: return "\(boolValue!)"
        case .string: return "\"\(string!.escaped(asASCII: false))\""
        case .signed: return "\(int64Value!)"
        case .unsigned: return "\(uint64Value!)"
        case .number: return "\(doubleValue!)"
        case .binary: return "\(binary!)"
        case .uuid: return "\(uuid!)"
        case .array: return "\(array!)"
        case .dictionary: return "\(dictionary!)"
        case .unknown: return "unknown"
        }
    }
}

extension DataPack: Hashable {
    
    @inlinable
    public static func == (lhs: DataPack, rhs: DataPack) -> Bool {
        switch (lhs.type, rhs.type) {
        case (.null, .null): return true
        case (.boolean, .boolean): return lhs.boolValue == rhs.boolValue
        case (.string, .string): return lhs.string == rhs.string
        case (.signed, .signed): return lhs.int64Value == rhs.int64Value
        case (.unsigned, .unsigned): return lhs.uint64Value == rhs.uint64Value
        case (.number, .number): return lhs.doubleValue == rhs.doubleValue
        case (.binary, .binary): return lhs.binary == rhs.binary
        case (.uuid, .uuid): return lhs.uuid == rhs.uuid
        case (.array, .array): return lhs.array == rhs.array
        case (.dictionary, .dictionary): return lhs.dictionary == rhs.dictionary
        default: return false
        }
    }
    
    @inlinable
    public func hash(into hasher: inout Hasher) {
        hasher.combine(type)
        switch type {
        case .boolean: hasher.combine(boolValue!)
        case .string: hasher.combine(string!)
        case .signed: hasher.combine(int64Value!)
        case .unsigned: hasher.combine(uint64Value!)
        case .number: hasher.combine(doubleValue!)
        case .binary: hasher.combine(binary!)
        case .uuid: hasher.combine(uuid!)
        case .array: hasher.combine(array!)
        case .dictionary: hasher.combine(dictionary!)
        default: break
        }
    }
}

extension DataPack {
    
    @inlinable
    public func encode() -> Data {
        var result = MappedBuffer<UInt8>()
        base.encode(to: &result)
        return result.data
    }
}

extension DataPack {
    
    @inlinable
    public var type: DataPackType {
        return base.type
    }
    
    @inlinable
    public var isUnknownType: Bool {
        switch type {
        case .unknown: return true
        default: return false
        }
    }
    
    @inlinable
    public var isNil: Bool {
        return type == .null
    }
    
    @inlinable
    public var isBool: Bool {
        return type == .boolean
    }
    
    @inlinable
    public var isString: Bool {
        return type == .string
    }
    
    @inlinable
    public var isArray: Bool {
        return type == .array
    }
    
    @inlinable
    public var isObject: Bool {
        return type == .dictionary
    }
    
    @inlinable
    public var isSigned: Bool {
        return type == .signed
    }
    
    @inlinable
    public var isUnsigned: Bool {
        return type == .unsigned
    }
    
    @inlinable
    public var isNumber: Bool {
        return type == .number
    }
    
    @inlinable
    public var isNumeric: Bool {
        switch type {
        case .signed: return true
        case .unsigned: return true
        case .number: return true
        default: return false
        }
    }
    
    @inlinable
    public var isBinary: Bool {
        return type == .binary
    }
    
    @inlinable
    public var isUUID: Bool {
        return type == .uuid
    }
}

extension DataPack {
    
    @usableFromInline
    enum Base {

    public subscript(index: Int) -> DataPack {
        get {
            guard 0..<count ~= index else { return nil }
            switch self.base {
            case let .array(value): return value[index]
            case let .undecoded_array(value): return value[index]
            default: return nil
            }
        }
        set {
            switch self.base {
            case var .array(value):
                
                if index >= value.count {
                    value.append(contentsOf: repeatElement(nil, count: index - value.count + 1))
                }
                value[index] = newValue
                self = DataPack(value)
                
            case let .undecoded_array(value):
                
                var array = Array(value)
                if index >= array.count {
                    array.append(contentsOf: repeatElement(nil, count: index - array.count + 1))
                }
                array[index] = newValue
                self = DataPack(array)
                
            default: fatalError("Not an array.")
            }
        }
