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