xref: /aosp_15_r20/external/flatbuffers/swift/Sources/FlatBuffers/TableVerifier.swift (revision 890232f25432b36107d06881e0a25aaa6b473652)
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