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