1*890232f2SAndroid Build Coastguard Worker /* 2*890232f2SAndroid Build Coastguard Worker * Copyright 2021 Google Inc. All rights reserved. 3*890232f2SAndroid Build Coastguard Worker * 4*890232f2SAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License"); 5*890232f2SAndroid Build Coastguard Worker * you may not use this file except in compliance with the License. 6*890232f2SAndroid Build Coastguard Worker * You may obtain a copy of the License at 7*890232f2SAndroid Build Coastguard Worker * 8*890232f2SAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0 9*890232f2SAndroid Build Coastguard Worker * 10*890232f2SAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software 11*890232f2SAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS, 12*890232f2SAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*890232f2SAndroid Build Coastguard Worker * See the License for the specific language governing permissions and 14*890232f2SAndroid Build Coastguard Worker * limitations under the License. 15*890232f2SAndroid Build Coastguard Worker */ 16*890232f2SAndroid Build Coastguard Worker 17*890232f2SAndroid Build Coastguard Worker #if !os(WASI) 18*890232f2SAndroid Build Coastguard Worker import Foundation 19*890232f2SAndroid Build Coastguard Worker #else 20*890232f2SAndroid Build Coastguard Worker import SwiftOverlayShims 21*890232f2SAndroid Build Coastguard Worker #endif 22*890232f2SAndroid Build Coastguard Worker 23*890232f2SAndroid Build Coastguard Worker /// `TableVerifier` verifies a table object is within a provided memory. 24*890232f2SAndroid Build Coastguard Worker /// It checks if all the objects for a specific generated table, are within 25*890232f2SAndroid Build Coastguard Worker /// the bounds of the buffer, aligned. 26*890232f2SAndroid Build Coastguard Worker public struct TableVerifier { 27*890232f2SAndroid Build Coastguard Worker 28*890232f2SAndroid Build Coastguard Worker /// position of current table in `ByteBuffer` 29*890232f2SAndroid Build Coastguard Worker fileprivate var _position: Int 30*890232f2SAndroid Build Coastguard Worker 31*890232f2SAndroid Build Coastguard Worker /// Current VTable position 32*890232f2SAndroid Build Coastguard Worker fileprivate var _vtable: Int 33*890232f2SAndroid Build Coastguard Worker 34*890232f2SAndroid Build Coastguard Worker /// Length of current VTable 35*890232f2SAndroid Build Coastguard Worker fileprivate var _vtableLength: Int 36*890232f2SAndroid Build Coastguard Worker 37*890232f2SAndroid Build Coastguard Worker /// `Verifier` object created in the base verifable call. 38*890232f2SAndroid Build Coastguard Worker fileprivate var _verifier: Verifier 39*890232f2SAndroid Build Coastguard Worker 40*890232f2SAndroid Build Coastguard Worker /// Creates a `TableVerifier` verifier that allows the Flatbuffer object 41*890232f2SAndroid Build Coastguard Worker /// to verify the buffer before accessing any of the data. 42*890232f2SAndroid Build Coastguard Worker /// 43*890232f2SAndroid Build Coastguard Worker /// - Parameters: 44*890232f2SAndroid Build Coastguard Worker /// - position: Current table Position 45*890232f2SAndroid Build Coastguard Worker /// - vtable: Current `VTable` position 46*890232f2SAndroid Build Coastguard Worker /// - vtableLength: Current `VTable` length 47*890232f2SAndroid Build Coastguard Worker /// - verifier: `Verifier` Object that caches the data of the verifiable object 48*890232f2SAndroid Build Coastguard Worker internal init( 49*890232f2SAndroid Build Coastguard Worker position: Int, 50*890232f2SAndroid Build Coastguard Worker vtable: Int, 51*890232f2SAndroid Build Coastguard Worker vtableLength: Int, 52*890232f2SAndroid Build Coastguard Worker verifier: inout Verifier) 53*890232f2SAndroid Build Coastguard Worker { 54*890232f2SAndroid Build Coastguard Worker _position = position 55*890232f2SAndroid Build Coastguard Worker _vtable = vtable 56*890232f2SAndroid Build Coastguard Worker _vtableLength = vtableLength 57*890232f2SAndroid Build Coastguard Worker _verifier = verifier 58*890232f2SAndroid Build Coastguard Worker } 59*890232f2SAndroid Build Coastguard Worker 60*890232f2SAndroid Build Coastguard Worker /// Dereference the current object position from the `VTable` 61*890232f2SAndroid Build Coastguard Worker /// - Parameter field: Current VTable refrence to position. 62*890232f2SAndroid Build Coastguard Worker /// - Throws: A `FlatbuffersErrors` incase the voffset is not aligned/outOfBounds/apparentSizeTooLarge 63*890232f2SAndroid Build Coastguard Worker /// - Returns: An optional position for current field dereferencenull64*890232f2SAndroid Build Coastguard Worker internal mutating func dereference(_ field: VOffset) throws -> Int? { 65*890232f2SAndroid Build Coastguard Worker if field >= _vtableLength { 66*890232f2SAndroid Build Coastguard Worker return nil 67*890232f2SAndroid Build Coastguard Worker } 68*890232f2SAndroid Build Coastguard Worker 69*890232f2SAndroid Build Coastguard Worker /// Reading the offset for the field needs to be read. 70*890232f2SAndroid Build Coastguard Worker let offset: VOffset = try _verifier.getValue( 71*890232f2SAndroid Build Coastguard Worker at: Int(clamping: _vtable &+ Int(field))) 72*890232f2SAndroid Build Coastguard Worker 73*890232f2SAndroid Build Coastguard Worker if offset > 0 { 74*890232f2SAndroid Build Coastguard Worker return Int(clamping: _position &+ Int(offset)) 75*890232f2SAndroid Build Coastguard Worker } 76*890232f2SAndroid Build Coastguard Worker return nil 77*890232f2SAndroid Build Coastguard Worker } 78*890232f2SAndroid Build Coastguard Worker 79*890232f2SAndroid Build Coastguard Worker /// Visits all the fields within the table to validate the integrity 80*890232f2SAndroid Build Coastguard Worker /// of the data 81*890232f2SAndroid Build Coastguard Worker /// - Parameters: 82*890232f2SAndroid Build Coastguard Worker /// - field: voffset of the current field to be read 83*890232f2SAndroid Build Coastguard Worker /// - fieldName: fieldname to report data Errors. 84*890232f2SAndroid Build Coastguard Worker /// - required: If the field has to be available in the buffer 85*890232f2SAndroid Build Coastguard Worker /// - type: Type of field to be read 86*890232f2SAndroid Build Coastguard Worker /// - Throws: A `FlatbuffersErrors` where the field is corrupt 87*890232f2SAndroid Build Coastguard Worker public mutating func visit<T>( 88*890232f2SAndroid Build Coastguard Worker field: VOffset, 89*890232f2SAndroid Build Coastguard Worker fieldName: String, 90*890232f2SAndroid Build Coastguard Worker required: Bool, 91*890232f2SAndroid Build Coastguard Worker type: T.Type) throws where T: Verifiable 92*890232f2SAndroid Build Coastguard Worker { 93*890232f2SAndroid Build Coastguard Worker let derefValue = try dereference(field) 94*890232f2SAndroid Build Coastguard Worker 95*890232f2SAndroid Build Coastguard Worker if let value = derefValue { 96*890232f2SAndroid Build Coastguard Worker try T.verify(&_verifier, at: value, of: T.self) 97*890232f2SAndroid Build Coastguard Worker return 98*890232f2SAndroid Build Coastguard Worker } 99*890232f2SAndroid Build Coastguard Worker if required { 100*890232f2SAndroid Build Coastguard Worker throw FlatbuffersErrors.requiredFieldDoesntExist( 101*890232f2SAndroid Build Coastguard Worker position: field, 102*890232f2SAndroid Build Coastguard Worker name: fieldName) 103*890232f2SAndroid Build Coastguard Worker } 104*890232f2SAndroid Build Coastguard Worker } 105*890232f2SAndroid Build Coastguard Worker 106*890232f2SAndroid Build Coastguard Worker /// Visits all the fields for a union object within the table to 107*890232f2SAndroid Build Coastguard Worker /// validate the integrity of the data 108*890232f2SAndroid Build Coastguard Worker /// - Parameters: 109*890232f2SAndroid Build Coastguard Worker /// - key: Current Key Voffset 110*890232f2SAndroid Build Coastguard Worker /// - field: Current field Voffset 111*890232f2SAndroid Build Coastguard Worker /// - unionKeyName: Union key name 112*890232f2SAndroid Build Coastguard Worker /// - fieldName: Field key name 113*890232f2SAndroid Build Coastguard Worker /// - required: indicates if an object is required to be present 114*890232f2SAndroid Build Coastguard Worker /// - completion: Completion is a handler that WILL be called in the generated 115*890232f2SAndroid Build Coastguard Worker /// - Throws: A `FlatbuffersErrors` where the field is corrupt 116*890232f2SAndroid Build Coastguard Worker public mutating func visit<T>( 117*890232f2SAndroid Build Coastguard Worker unionKey key: VOffset, 118*890232f2SAndroid Build Coastguard Worker unionField field: VOffset, 119*890232f2SAndroid Build Coastguard Worker unionKeyName: String, 120*890232f2SAndroid Build Coastguard Worker fieldName: String, 121*890232f2SAndroid Build Coastguard Worker required: Bool, 122*890232f2SAndroid Build Coastguard Worker completion: @escaping (inout Verifier, T, Int) throws -> Void) throws 123*890232f2SAndroid Build Coastguard Worker where T: UnionEnum 124*890232f2SAndroid Build Coastguard Worker { 125*890232f2SAndroid Build Coastguard Worker let keyPos = try dereference(key) 126*890232f2SAndroid Build Coastguard Worker let valPos = try dereference(field) 127*890232f2SAndroid Build Coastguard Worker 128*890232f2SAndroid Build Coastguard Worker if keyPos == nil && valPos == nil { 129*890232f2SAndroid Build Coastguard Worker if required { 130*890232f2SAndroid Build Coastguard Worker throw FlatbuffersErrors.requiredFieldDoesntExist( 131*890232f2SAndroid Build Coastguard Worker position: key, 132*890232f2SAndroid Build Coastguard Worker name: unionKeyName) 133*890232f2SAndroid Build Coastguard Worker } 134*890232f2SAndroid Build Coastguard Worker return 135*890232f2SAndroid Build Coastguard Worker } 136*890232f2SAndroid Build Coastguard Worker 137*890232f2SAndroid Build Coastguard Worker if let _key = keyPos, 138*890232f2SAndroid Build Coastguard Worker let _val = valPos 139*890232f2SAndroid Build Coastguard Worker { 140*890232f2SAndroid Build Coastguard Worker /// verifiying that the key is within the buffer 141*890232f2SAndroid Build Coastguard Worker try T.T.verify(&_verifier, at: _key, of: T.T.self) 142*890232f2SAndroid Build Coastguard Worker guard let _enum = try T.init(value: _verifier._buffer.read( 143*890232f2SAndroid Build Coastguard Worker def: T.T.self, 144*890232f2SAndroid Build Coastguard Worker position: _key)) else 145*890232f2SAndroid Build Coastguard Worker { 146*890232f2SAndroid Build Coastguard Worker throw FlatbuffersErrors.unknownUnionCase 147*890232f2SAndroid Build Coastguard Worker } 148*890232f2SAndroid Build Coastguard Worker /// we are assuming that Unions will always be of type Uint8 149*890232f2SAndroid Build Coastguard Worker try completion( 150*890232f2SAndroid Build Coastguard Worker &_verifier, 151*890232f2SAndroid Build Coastguard Worker _enum, 152*890232f2SAndroid Build Coastguard Worker _val) 153*890232f2SAndroid Build Coastguard Worker return 154*890232f2SAndroid Build Coastguard Worker } 155*890232f2SAndroid Build Coastguard Worker throw FlatbuffersErrors.valueNotFound( 156*890232f2SAndroid Build Coastguard Worker key: keyPos, 157*890232f2SAndroid Build Coastguard Worker keyName: unionKeyName, 158*890232f2SAndroid Build Coastguard Worker field: valPos, 159*890232f2SAndroid Build Coastguard Worker fieldName: fieldName) 160*890232f2SAndroid Build Coastguard Worker } 161*890232f2SAndroid Build Coastguard Worker 162*890232f2SAndroid Build Coastguard Worker /// Visits and validates all the objects within a union vector 163*890232f2SAndroid Build Coastguard Worker /// - Parameters: 164*890232f2SAndroid Build Coastguard Worker /// - key: Current Key Voffset 165*890232f2SAndroid Build Coastguard Worker /// - field: Current field Voffset 166*890232f2SAndroid Build Coastguard Worker /// - unionKeyName: Union key name 167*890232f2SAndroid Build Coastguard Worker /// - fieldName: Field key name 168*890232f2SAndroid Build Coastguard Worker /// - required: indicates if an object is required to be present 169*890232f2SAndroid Build Coastguard Worker /// - completion: Completion is a handler that WILL be called in the generated 170*890232f2SAndroid Build Coastguard Worker /// - Throws: A `FlatbuffersErrors` where the field is corrupt 171*890232f2SAndroid Build Coastguard Worker public mutating func visitUnionVector<T>( 172*890232f2SAndroid Build Coastguard Worker unionKey key: VOffset, 173*890232f2SAndroid Build Coastguard Worker unionField field: VOffset, 174*890232f2SAndroid Build Coastguard Worker unionKeyName: String, 175*890232f2SAndroid Build Coastguard Worker fieldName: String, 176*890232f2SAndroid Build Coastguard Worker required: Bool, 177*890232f2SAndroid Build Coastguard Worker completion: @escaping (inout Verifier, T, Int) throws -> Void) throws 178*890232f2SAndroid Build Coastguard Worker where T: UnionEnum 179*890232f2SAndroid Build Coastguard Worker { 180*890232f2SAndroid Build Coastguard Worker let keyVectorPosition = try dereference(key) 181*890232f2SAndroid Build Coastguard Worker let offsetVectorPosition = try dereference(field) 182*890232f2SAndroid Build Coastguard Worker 183*890232f2SAndroid Build Coastguard Worker if let keyPos = keyVectorPosition, 184*890232f2SAndroid Build Coastguard Worker let valPos = offsetVectorPosition 185*890232f2SAndroid Build Coastguard Worker { 186*890232f2SAndroid Build Coastguard Worker try UnionVector<T>.verify( 187*890232f2SAndroid Build Coastguard Worker &_verifier, 188*890232f2SAndroid Build Coastguard Worker keyPosition: keyPos, 189*890232f2SAndroid Build Coastguard Worker fieldPosition: valPos, 190*890232f2SAndroid Build Coastguard Worker unionKeyName: unionKeyName, 191*890232f2SAndroid Build Coastguard Worker fieldName: fieldName, 192*890232f2SAndroid Build Coastguard Worker completion: completion) 193*890232f2SAndroid Build Coastguard Worker return 194*890232f2SAndroid Build Coastguard Worker } 195*890232f2SAndroid Build Coastguard Worker if required { 196*890232f2SAndroid Build Coastguard Worker throw FlatbuffersErrors.requiredFieldDoesntExist( 197*890232f2SAndroid Build Coastguard Worker position: field, 198*890232f2SAndroid Build Coastguard Worker name: fieldName) 199*890232f2SAndroid Build Coastguard Worker } 200*890232f2SAndroid Build Coastguard Worker } 201*890232f2SAndroid Build Coastguard Worker 202*890232f2SAndroid Build Coastguard Worker /// Finishs the current Table verifier, and subtracts the current 203*890232f2SAndroid Build Coastguard Worker /// table from the incremented depth. finishnull204*890232f2SAndroid Build Coastguard Worker public mutating func finish() { 205*890232f2SAndroid Build Coastguard Worker _verifier.finish() 206*890232f2SAndroid Build Coastguard Worker } 207*890232f2SAndroid Build Coastguard Worker } 208