• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2023 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 #if !os(WASI)
18 import Foundation
19 #else
20 import SwiftOverlayShims
21 #endif
22 
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
26 public 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 
41 extension 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 
65 extension 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.
87 public 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.
111 public 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(
133           clamping: range
134             .start &+ (range.count &* MemoryLayout<Int32>.size)),
135         by: MemoryLayout<UOffset>.size)
136       {
137         try U.verify(&verifier, at: index, of: U.self)
138       }
139     } else {
140       try S.verifyRange(&verifier, at: position, of: S.self)
141     }
142   }
143 }
144 
145 // MARK: - UnionVector
146 
147 /// UnionVector is a container to wrap around the Generic type to be verified
148 /// from the flatbuffers object.
149 public enum UnionVector<S> where S: UnionEnum {
150 
151   /// Completion handler for the function Verify, that passes the verifier
152   /// enum type and position of union field
153   public typealias Completion = (inout Verifier, S, Int) throws -> Void
154 
155   /// Verifies if the current range to be read is within the bounds of the buffer,
156   /// and if the range is properly aligned. It also verifies if the union type is a
157   /// *valid/supported* union type.
158   /// - Parameters:
159   ///   - verifier: Verifier that hosts the buffer
160   ///   - keyPosition: Current union key position within the buffer
161   ///   - fieldPosition: Current union field position within the buffer
162   ///   - unionKeyName: Name of key to written if error is presented
163   ///   - fieldName: Name of field to written if error is presented
164   ///   - completion: Completion is a handler that WILL be called in the generated
165   ///   code to verify the actual objects
166   /// - Throws: FlatbuffersErrors
167   public static func verify(
168     _ verifier: inout Verifier,
169     keyPosition: Int,
170     fieldPosition: Int,
171     unionKeyName: String,
172     fieldName: String,
173     completion: @escaping Completion) throws
174   {
175     /// Get offset for union key vectors and offset vectors
176     let keyOffset: UOffset = try verifier.getValue(at: keyPosition)
177     let fieldOffset: UOffset = try verifier.getValue(at: fieldPosition)
178 
179     /// Check if values are within the buffer, returns the start position of vectors, and vector counts
180     /// Using &+ is safe since we already verified that the value is within the buffer, where the max is
181     /// going to be 2Gib and swift supports Int64 by default
182     let keysRange = try S.T.verifyRange(
183       &verifier,
184       at: Int(keyOffset) &+ keyPosition,
185       of: S.T.self)
186     let offsetsRange = try UOffset.verifyRange(
187       &verifier,
188       at: Int(fieldOffset) &+ fieldPosition,
189       of: UOffset.self)
190 
191     guard keysRange.count == offsetsRange.count else {
192       throw FlatbuffersErrors.unionVectorSize(
193         keyVectorSize: keysRange.count,
194         fieldVectorSize: offsetsRange.count,
195         unionKeyName: unionKeyName,
196         fieldName: fieldName)
197     }
198 
199     var count = 0
200     /// Iterate over the vector of keys and offsets.
201     while count < keysRange.count {
202 
203       /// index of readable enum value in array
204       let keysIndex = MemoryLayout<S.T>.size * count
205       guard let _enum = try S.init(value: verifier._buffer.read(
206         def: S.T.self,
207         position: keysRange.start + keysIndex)) else
208       {
209         throw FlatbuffersErrors.unknownUnionCase
210       }
211       /// index of readable offset value in array
212       let fieldIndex = MemoryLayout<UOffset>.size * count
213       try completion(&verifier, _enum, offsetsRange.start + fieldIndex)
214       count += 1
215     }
216   }
217 }
218