• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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