blob: 42a37f2888c7053faa3c21bccc6a4850fe5e9890 [file] [log] [blame]
James Kuszmaul8e62b022022-03-22 09:33:25 -07001/*
2 * Copyright 2021 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
17import 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.
22public 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
60 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.
200 public mutating func finish() {
201 _verifier.finish()
202 }
203}