| //===----- ByteTreeDeserialization.swift - Reading the ByteTree format ----===// |
| // |
| // This source file is part of the Swift.org open source project |
| // |
| // Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors |
| // Licensed under Apache License v2.0 with Runtime Library Exception |
| // |
| // See https://swift.org/LICENSE.txt for license information |
| // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors |
| // |
| //===----------------------------------------------------------------------===// |
| |
| import Foundation |
| |
| // MARK: - ByteTree decoder protocols |
| |
| /// A type that can be deserialized from ByteTree into a scalar value that |
| /// doesn't have any child nodes |
| protocol ByteTreeScalarDecodable { |
| /// Construct the scalar value from the given serialized data |
| /// |
| /// - Parameters: |
| /// - pointer: The pointer pointing to the start of the serialized data |
| /// - size: The length of the serialized data in bytes |
| /// - Returns: The deserialized value |
| static func read(from pointer: UnsafeRawPointer, size: Int) -> Self |
| } |
| |
| /// A type that can be deserialized from ByteTree into an object with child |
| /// nodes |
| protocol ByteTreeObjectDecodable { |
| /// Construct a object from the serialized ByteTree object. |
| /// |
| /// The fields can be read from `reader` using the corresponding `readField` |
| /// methods. |
| /// |
| /// - Parameters: |
| /// - reader: The reader from which the object's field's values can be read |
| /// - numFields: The number of fields that are present in the serialized |
| /// object |
| /// - Returns: The deserialized object |
| static func read(from reader: ByteTreeObjectReader, numFields: Int) -> Self |
| } |
| |
| // MARK: - Reader objects |
| |
| /// Helper object for reading objects out a ByteTree. Keeps track that fields |
| /// are not read out of order and discards all trailing fields that were present |
| /// in the binary format but were not handled when reading the object. |
| class ByteTreeObjectReader { |
| /// The reader that holds a reference to the data from which the object is |
| /// read |
| private let reader: ByteTreeReader |
| |
| /// The number of fields this object is expected to have |
| private let numFields: Int |
| |
| /// The index of the field that is expected to be read next. |
| private var nextIndex: Int = 0 |
| |
| fileprivate init(reader: ByteTreeReader, numFields: Int) { |
| self.reader = reader |
| self.numFields = numFields |
| } |
| |
| private func advanceAndValidateIndex(_ index: Int) { |
| assert(index == nextIndex, "Reading fields out of order") |
| assert(index < numFields) |
| nextIndex += 1 |
| } |
| |
| |
| /// Read the field at the given index as the specified type. All indicies must |
| /// be read in order starting with 0. Skipping an index will result in a |
| /// runtime assertion error. To discard a field use `discardField(:)`. |
| /// |
| /// - Parameters: |
| /// - objectType: The type as which this field should be read |
| /// - index: The index of this field |
| /// - Returns: The decoded field |
| func readField<FieldType: ByteTreeScalarDecodable>( |
| _ objectType: FieldType.Type, index: Int |
| ) -> FieldType { |
| advanceAndValidateIndex(index) |
| return reader.read(objectType) |
| } |
| |
| /// Read the field at the given index as the specified type. All indicies must |
| /// be read in order starting with 0. Skipping an index will result in a |
| /// runtime assertion error. To discard a field use `discardField(:)`. |
| /// |
| /// - Parameters: |
| /// - objectType: The type as which this field should be read |
| /// - index: The index of this field |
| /// - Returns: The decoded field |
| func readField<FieldType: ByteTreeObjectDecodable>( |
| _ objectType: FieldType.Type, index: Int |
| ) -> FieldType { |
| advanceAndValidateIndex(index) |
| return reader.read(objectType) |
| } |
| |
| /// Read and immediately discard the field at the specified index. This |
| /// advances the reader by one field so that the next field can be read. |
| /// |
| /// - Parameter index: The index of the field that shall be discarded |
| func discardField(index: Int) { |
| advanceAndValidateIndex(index) |
| reader.discardField() |
| } |
| |
| deinit { |
| // Discard all fields that have not been read |
| while nextIndex < numFields { |
| discardField(index: nextIndex) |
| } |
| } |
| } |
| |
| /// Reader for reading the ByteTree format into Swift objects |
| class ByteTreeReader { |
| /// The type as which the protocol version is encoded in ByteTree |
| typealias ProtocolVersion = UInt32 |
| |
| /// A pointer pointing to the next byte of serialized data to be read |
| private var pointer: UnsafeRawPointer |
| |
| private init(pointer: UnsafeRawPointer) { |
| self.pointer = pointer |
| } |
| |
| // MARK: Public entrance function |
| |
| /// Deserialize an object tree from the ByteTree data at the given memory |
| /// location. |
| /// |
| /// - Parameters: |
| /// - rootObjectType: The type of the root object in the deserialized tree |
| /// - pointer: The memory location at which the serialized data resides |
| /// - protocolVerisonValidation: A callback to determine if the data can be |
| /// read, based on the format's protocol version. If the callback |
| /// returns `false`, `nil` will be returned and reading aborded. |
| /// - Returns: The deserialized tree or `nil` if protocol version validation |
| /// failed |
| static func read<T: ByteTreeObjectDecodable>( |
| _ rootObjectType: T.Type, from pointer: UnsafeRawPointer, |
| protocolVerisonValidation: (ProtocolVersion) -> Bool |
| ) -> T? { |
| let reader = ByteTreeReader(pointer: pointer) |
| if !reader.readAndValidateProtocolVersion(protocolVerisonValidation) { |
| return nil |
| } |
| return reader.read(rootObjectType) |
| } |
| |
| // MARK: Internal read functions |
| |
| /// Cast the current pointer location to the given type and advance `pointer` |
| /// to point behind the just read bytes. |
| /// |
| /// - Parameter type: The type as which the current data should be read |
| /// - Returns: The read value |
| private func readRaw<T>(_ type: T.Type) -> T { |
| let result = pointer.bindMemory(to: T.self, capacity: 1).pointee |
| pointer = pointer.advanced(by: MemoryLayout<T>.size) |
| return result |
| } |
| |
| /// Read the number of fields in an object or the binary length of a scalar |
| /// field. |
| /// |
| /// - Returns: The read value |
| private func readFieldLength() -> Int { |
| return Int(UInt32(littleEndian: readRaw(UInt32.self))) |
| } |
| |
| /// Read the protocol version and validate that it can be read using the given |
| /// callback. |
| /// |
| /// - Parameter validationCallback: A callback that determines if the given |
| /// protocol version can be read |
| private func readAndValidateProtocolVersion( |
| _ validationCallback: (ProtocolVersion) -> Bool |
| ) -> Bool { |
| let protocolVersion = ProtocolVersion(littleEndian: |
| readRaw(ProtocolVersion.self)) |
| let result = validationCallback(protocolVersion) |
| pointer = pointer.advanced(by: MemoryLayout<ProtocolVersion>.size) |
| return result |
| } |
| |
| /// Read the next field in the tree as an object of the specified type. |
| /// |
| /// - Parameter objectType: The type as which the next field shall be read |
| /// - Returns: The deserialized object |
| fileprivate func read<T: ByteTreeObjectDecodable>( |
| _ objectType: T.Type |
| ) -> T { |
| let numFields = readFieldLength() |
| let objectReader = ByteTreeObjectReader(reader: self, |
| numFields: numFields) |
| return T.read(from: objectReader, numFields: numFields) |
| } |
| |
| /// Read the next field in the tree as a scalar of the specified type. |
| /// |
| /// - Parameter scalarType: The type as which the field shall be read |
| /// - Returns: The deserialized scalar |
| fileprivate func read<T: ByteTreeScalarDecodable>( |
| _ scalarType: T.Type |
| ) -> T { |
| let fieldSize = readFieldLength() |
| defer { |
| pointer = pointer.advanced(by: fieldSize) |
| } |
| return T.read(from: pointer, size: fieldSize) |
| } |
| |
| /// Discard the next scalar field, advancing the pointer to the next field |
| fileprivate func discardField() { |
| // FIXME: This can currently only discard scalar fields. Object fields |
| // should also be discardable |
| let fieldSize = readFieldLength() |
| pointer = pointer.advanced(by: fieldSize) |
| } |
| } |
| |
| // MARK: - Common scalar type conformances |
| |
| // Implemenation for reading an integer from memory to be shared between |
| // multiple types |
| extension ByteTreeScalarDecodable where Self : FixedWidthInteger { |
| static func read(from pointer: UnsafeRawPointer, size: Int) -> Self { |
| assert(size == MemoryLayout<Self>.size) |
| return pointer.bindMemory(to: Self.self, capacity: 1).pointee |
| } |
| } |
| |
| extension UInt8: ByteTreeScalarDecodable {} |
| extension UInt16: ByteTreeScalarDecodable {} |
| extension UInt32: ByteTreeScalarDecodable {} |
| |
| extension String: ByteTreeScalarDecodable { |
| static func read(from pointer: UnsafeRawPointer, size: Int) -> String { |
| let data = Data(bytes: pointer, count: size) |
| return String(data: data, encoding: .utf8)! |
| } |
| } |
| |
| extension Optional: ByteTreeObjectDecodable |
| where |
| Wrapped: ByteTreeObjectDecodable { |
| static func read(from reader: ByteTreeObjectReader, numFields: Int) -> |
| Optional<Wrapped> { |
| if numFields == 0 { |
| return nil |
| } else { |
| return Wrapped.read(from: reader, numFields: numFields) |
| } |
| } |
| } |
| |
| extension Array: ByteTreeObjectDecodable |
| where |
| Element: ByteTreeObjectDecodable { |
| static func read(from reader: ByteTreeObjectReader, numFields: Int) -> |
| Array<Element> { |
| return (0..<numFields).map { |
| return reader.readField(Element.self, index: $0) |
| } |
| } |
| } |