blob: 0338f0d3257b941c683b390e2c305b0c2d08a3c3 [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
Austin Schuh2dd86a92022-09-14 21:19:23 -070017#if !os(WASI)
James Kuszmaul8e62b022022-03-22 09:33:25 -070018import Foundation
Austin Schuh2dd86a92022-09-14 21:19:23 -070019#else
20import SwiftOverlayShims
21#endif
James Kuszmaul8e62b022022-03-22 09:33:25 -070022
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.
26public 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}