| /* |
| * Copyright 2021 Google Inc. All rights reserved. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| import Foundation |
| |
| /// `TableVerifier` verifies a table object is within a provided memory. |
| /// It checks if all the objects for a specific generated table, are within |
| /// the bounds of the buffer, aligned. |
| public struct TableVerifier { |
| |
| /// position of current table in `ByteBuffer` |
| fileprivate var _position: Int |
| |
| /// Current VTable position |
| fileprivate var _vtable: Int |
| |
| /// Length of current VTable |
| fileprivate var _vtableLength: Int |
| |
| /// `Verifier` object created in the base verifable call. |
| fileprivate var _verifier: Verifier |
| |
| /// Creates a `TableVerifier` verifier that allows the Flatbuffer object |
| /// to verify the buffer before accessing any of the data. |
| /// |
| /// - Parameters: |
| /// - position: Current table Position |
| /// - vtable: Current `VTable` position |
| /// - vtableLength: Current `VTable` length |
| /// - verifier: `Verifier` Object that caches the data of the verifiable object |
| internal init( |
| position: Int, |
| vtable: Int, |
| vtableLength: Int, |
| verifier: inout Verifier) |
| { |
| _position = position |
| _vtable = vtable |
| _vtableLength = vtableLength |
| _verifier = verifier |
| } |
| |
| /// Dereference the current object position from the `VTable` |
| /// - Parameter field: Current VTable refrence to position. |
| /// - Throws: A `FlatbuffersErrors` incase the voffset is not aligned/outOfBounds/apparentSizeTooLarge |
| /// - Returns: An optional position for current field |
| internal mutating func dereference(_ field: VOffset) throws -> Int? { |
| if field >= _vtableLength { |
| return nil |
| } |
| |
| /// Reading the offset for the field needs to be read. |
| let offset: VOffset = try _verifier.getValue( |
| at: Int(clamping: _vtable &+ Int(field))) |
| |
| if offset > 0 { |
| return Int(clamping: _position &+ Int(offset)) |
| } |
| return nil |
| } |
| |
| /// Visits all the fields within the table to validate the integrity |
| /// of the data |
| /// - Parameters: |
| /// - field: voffset of the current field to be read |
| /// - fieldName: fieldname to report data Errors. |
| /// - required: If the field has to be available in the buffer |
| /// - type: Type of field to be read |
| /// - Throws: A `FlatbuffersErrors` where the field is corrupt |
| public mutating func visit<T>( |
| field: VOffset, |
| fieldName: String, |
| required: Bool, |
| type: T.Type) throws where T: Verifiable |
| { |
| let derefValue = try dereference(field) |
| |
| if let value = derefValue { |
| try T.verify(&_verifier, at: value, of: T.self) |
| return |
| } |
| if required { |
| throw FlatbuffersErrors.requiredFieldDoesntExist( |
| position: field, |
| name: fieldName) |
| } |
| } |
| |
| /// Visits all the fields for a union object within the table to |
| /// validate the integrity of the data |
| /// - Parameters: |
| /// - key: Current Key Voffset |
| /// - field: Current field Voffset |
| /// - unionKeyName: Union key name |
| /// - fieldName: Field key name |
| /// - required: indicates if an object is required to be present |
| /// - completion: Completion is a handler that WILL be called in the generated |
| /// - Throws: A `FlatbuffersErrors` where the field is corrupt |
| public mutating func visit<T>( |
| unionKey key: VOffset, |
| unionField field: VOffset, |
| unionKeyName: String, |
| fieldName: String, |
| required: Bool, |
| completion: @escaping (inout Verifier, T, Int) throws -> Void) throws |
| where T: UnionEnum |
| { |
| let keyPos = try dereference(key) |
| let valPos = try dereference(field) |
| |
| if keyPos == nil && valPos == nil { |
| if required { |
| throw FlatbuffersErrors.requiredFieldDoesntExist( |
| position: key, |
| name: unionKeyName) |
| } |
| return |
| } |
| |
| if let _key = keyPos, |
| let _val = valPos |
| { |
| /// verifiying that the key is within the buffer |
| try T.T.verify(&_verifier, at: _key, of: T.T.self) |
| guard let _enum = try T.init(value: _verifier._buffer.read( |
| def: T.T.self, |
| position: _key)) else |
| { |
| throw FlatbuffersErrors.unknownUnionCase |
| } |
| /// we are assuming that Unions will always be of type Uint8 |
| try completion( |
| &_verifier, |
| _enum, |
| _val) |
| return |
| } |
| throw FlatbuffersErrors.valueNotFound( |
| key: keyPos, |
| keyName: unionKeyName, |
| field: valPos, |
| fieldName: fieldName) |
| } |
| |
| /// Visits and validates all the objects within a union vector |
| /// - Parameters: |
| /// - key: Current Key Voffset |
| /// - field: Current field Voffset |
| /// - unionKeyName: Union key name |
| /// - fieldName: Field key name |
| /// - required: indicates if an object is required to be present |
| /// - completion: Completion is a handler that WILL be called in the generated |
| /// - Throws: A `FlatbuffersErrors` where the field is corrupt |
| public mutating func visitUnionVector<T>( |
| unionKey key: VOffset, |
| unionField field: VOffset, |
| unionKeyName: String, |
| fieldName: String, |
| required: Bool, |
| completion: @escaping (inout Verifier, T, Int) throws -> Void) throws |
| where T: UnionEnum |
| { |
| let keyVectorPosition = try dereference(key) |
| let offsetVectorPosition = try dereference(field) |
| |
| if let keyPos = keyVectorPosition, |
| let valPos = offsetVectorPosition |
| { |
| try UnionVector<T>.verify( |
| &_verifier, |
| keyPosition: keyPos, |
| fieldPosition: valPos, |
| unionKeyName: unionKeyName, |
| fieldName: fieldName, |
| completion: completion) |
| return |
| } |
| if required { |
| throw FlatbuffersErrors.requiredFieldDoesntExist( |
| position: field, |
| name: fieldName) |
| } |
| } |
| |
| /// Finishs the current Table verifier, and subtracts the current |
| /// table from the incremented depth. |
| public mutating func finish() { |
| _verifier.finish() |
| } |
| } |