/* * Copyright 2023 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. */ #if !os(WASI) import Foundation #else import SwiftOverlayShims #endif /// `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( 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( 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( 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.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() } }