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