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