blob: 7ecb454612bc3c4fbb2fbca5ada45c1ff92032a1 [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/// Verifiable is a protocol all swift flatbuffers object should conform to,
24/// since swift is similar to `cpp` and `rust` where the data is read directly
25/// from `unsafeMemory` thus the need to verify if the buffer received is a valid one
26public protocol Verifiable {
27
28 /// Verifies that the current value is which the bounds of the buffer, and if
29 /// the current `Value` is aligned properly
30 /// - Parameters:
31 /// - verifier: Verifier that hosts the buffer
32 /// - position: Current position within the buffer
33 /// - type: The type of the object to be verified
34 /// - Throws: Errors coming from `inBuffer` function
35 static func verify<T>(
36 _ verifier: inout Verifier,
37 at position: Int,
38 of type: T.Type) throws where T: Verifiable
39}
40
41extension Verifiable {
42
43 /// Verifies if the current range to be read is within the bounds of the buffer,
44 /// and if the range is properly aligned
45 /// - Parameters:
46 /// - verifier: Verifier that hosts the buffer
47 /// - position: Current position within the buffer
48 /// - type: The type of the object to be verified
49 /// - Throws: Erros thrown from `isAligned` & `rangeInBuffer`
50 /// - Returns: a tuple of the start position and the count of objects within the range
51 @discardableResult
52 public static func verifyRange<T>(
53 _ verifier: inout Verifier,
54 at position: Int, of type: T.Type) throws -> (start: Int, count: Int)
55 {
56 let len: UOffset = try verifier.getValue(at: position)
57 let intLen = Int(len)
58 let start = Int(clamping: (position &+ MemoryLayout<Int32>.size).magnitude)
59 try verifier.isAligned(position: start, type: type.self)
60 try verifier.rangeInBuffer(position: start, size: intLen)
61 return (start, intLen)
62 }
63}
64
65extension Verifiable where Self: Scalar {
66
67 /// Verifies that the current value is which the bounds of the buffer, and if
68 /// the current `Value` is aligned properly
69 /// - Parameters:
70 /// - verifier: Verifier that hosts the buffer
71 /// - position: Current position within the buffer
72 /// - type: The type of the object to be verified
73 /// - Throws: Errors coming from `inBuffer` function
74 public static func verify<T>(
75 _ verifier: inout Verifier,
76 at position: Int,
77 of type: T.Type) throws where T: Verifiable
78 {
79 try verifier.inBuffer(position: position, of: type.self)
80 }
81}
82
83// MARK: - ForwardOffset
84
85/// ForwardOffset is a container to wrap around the Generic type to be verified
86/// from the flatbuffers object.
87public enum ForwardOffset<U>: Verifiable where U: Verifiable {
88
89 /// Verifies that the current value is which the bounds of the buffer, and if
90 /// the current `Value` is aligned properly
91 /// - Parameters:
92 /// - verifier: Verifier that hosts the buffer
93 /// - position: Current position within the buffer
94 /// - type: The type of the object to be verified
95 /// - Throws: Errors coming from `inBuffer` function
96 public static func verify<T>(
97 _ verifier: inout Verifier,
98 at position: Int,
99 of type: T.Type) throws where T: Verifiable
100 {
101 let offset: UOffset = try verifier.getValue(at: position)
102 let nextOffset = Int(clamping: (Int(offset) &+ position).magnitude)
103 try U.verify(&verifier, at: nextOffset, of: U.self)
104 }
105}
106
107// MARK: - Vector
108
109/// Vector is a container to wrap around the Generic type to be verified
110/// from the flatbuffers object.
111public enum Vector<U, S>: Verifiable where U: Verifiable, S: Verifiable {
112
113 /// Verifies that the current value is which the bounds of the buffer, and if
114 /// the current `Value` is aligned properly
115 /// - Parameters:
116 /// - verifier: Verifier that hosts the buffer
117 /// - position: Current position within the buffer
118 /// - type: The type of the object to be verified
119 /// - Throws: Errors coming from `inBuffer` function
120 public static func verify<T>(
121 _ verifier: inout Verifier,
122 at position: Int,
123 of type: T.Type) throws where T: Verifiable
124 {
125 /// checks if the next verification type S is equal to U of type forwardOffset
126 /// This had to be done since I couldnt find a solution for duplicate call functions
127 /// A fix will be appreciated
128 if U.self is ForwardOffset<S>.Type {
129 let range = try verifyRange(&verifier, at: position, of: UOffset.self)
130 for index in stride(
131 from: range.start,
132 to: Int(clamping: range.start &+ range.count),
133 by: MemoryLayout<UOffset>.size)
134 {
135 try U.verify(&verifier, at: index, of: U.self)
136 }
137 } else {
138 try S.verifyRange(&verifier, at: position, of: S.self)
139 }
140 }
141}
142
143// MARK: - UnionVector
144
145/// UnionVector is a container to wrap around the Generic type to be verified
146/// from the flatbuffers object.
147public enum UnionVector<S> where S: UnionEnum {
148
149 /// Completion handler for the function Verify, that passes the verifier
150 /// enum type and position of union field
151 public typealias Completion = (inout Verifier, S, Int) throws -> Void
152
153 /// Verifies if the current range to be read is within the bounds of the buffer,
154 /// and if the range is properly aligned. It also verifies if the union type is a
155 /// *valid/supported* union type.
156 /// - Parameters:
157 /// - verifier: Verifier that hosts the buffer
158 /// - keyPosition: Current union key position within the buffer
159 /// - fieldPosition: Current union field position within the buffer
160 /// - unionKeyName: Name of key to written if error is presented
161 /// - fieldName: Name of field to written if error is presented
162 /// - completion: Completion is a handler that WILL be called in the generated
163 /// code to verify the actual objects
164 /// - Throws: FlatbuffersErrors
165 public static func verify(
166 _ verifier: inout Verifier,
167 keyPosition: Int,
168 fieldPosition: Int,
169 unionKeyName: String,
170 fieldName: String,
171 completion: @escaping Completion) throws
172 {
173 /// Get offset for union key vectors and offset vectors
174 let keyOffset: UOffset = try verifier.getValue(at: keyPosition)
175 let fieldOffset: UOffset = try verifier.getValue(at: fieldPosition)
176
177 /// Check if values are within the buffer, returns the start position of vectors, and vector counts
178 /// Using &+ is safe since we already verified that the value is within the buffer, where the max is
179 /// going to be 2Gib and swift supports Int64 by default
180 let keysRange = try S.T.verifyRange(
181 &verifier,
182 at: Int(keyOffset) &+ keyPosition,
183 of: S.T.self)
184 let offsetsRange = try UOffset.verifyRange(
185 &verifier,
186 at: Int(fieldOffset) &+ fieldPosition,
187 of: UOffset.self)
188
189 guard keysRange.count == offsetsRange.count else {
190 throw FlatbuffersErrors.unionVectorSize(
191 keyVectorSize: keysRange.count,
192 fieldVectorSize: offsetsRange.count,
193 unionKeyName: unionKeyName,
194 fieldName: fieldName)
195 }
196
197 var count = 0
198 /// Iterate over the vector of keys and offsets.
199 while count < keysRange.count {
200
201 /// index of readable enum value in array
202 let keysIndex = MemoryLayout<S.T>.size * count
203 guard let _enum = try S.init(value: verifier._buffer.read(
204 def: S.T.self,
205 position: keysRange.start + keysIndex)) else
206 {
207 throw FlatbuffersErrors.unknownUnionCase
208 }
209 /// index of readable offset value in array
210 let fieldIndex = MemoryLayout<UOffset>.size * count
211 try completion(&verifier, _enum, offsetsRange.start + fieldIndex)
212 count += 1
213 }
214 }
215}