xref: /aosp_15_r20/external/flatbuffers/swift/Sources/FlatBuffers/Verifiable.swift (revision 890232f25432b36107d06881e0a25aaa6b473652)
1*890232f2SAndroid Build Coastguard Worker /*
2*890232f2SAndroid Build Coastguard Worker  * Copyright 2021 Google Inc. All rights reserved.
3*890232f2SAndroid Build Coastguard Worker  *
4*890232f2SAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*890232f2SAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*890232f2SAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*890232f2SAndroid Build Coastguard Worker  *
8*890232f2SAndroid Build Coastguard Worker  *     http://www.apache.org/licenses/LICENSE-2.0
9*890232f2SAndroid Build Coastguard Worker  *
10*890232f2SAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*890232f2SAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*890232f2SAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*890232f2SAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*890232f2SAndroid Build Coastguard Worker  * limitations under the License.
15*890232f2SAndroid Build Coastguard Worker  */
16*890232f2SAndroid Build Coastguard Worker 
17*890232f2SAndroid Build Coastguard Worker #if !os(WASI)
18*890232f2SAndroid Build Coastguard Worker import Foundation
19*890232f2SAndroid Build Coastguard Worker #else
20*890232f2SAndroid Build Coastguard Worker import SwiftOverlayShims
21*890232f2SAndroid Build Coastguard Worker #endif
22*890232f2SAndroid Build Coastguard Worker 
23*890232f2SAndroid Build Coastguard Worker /// Verifiable is a protocol all swift flatbuffers object should conform to,
24*890232f2SAndroid Build Coastguard Worker /// since swift is similar to `cpp` and `rust` where the data is read directly
25*890232f2SAndroid Build Coastguard Worker /// from `unsafeMemory` thus the need to verify if the buffer received is a valid one
26*890232f2SAndroid Build Coastguard Worker public protocol Verifiable {
27*890232f2SAndroid Build Coastguard Worker 
28*890232f2SAndroid Build Coastguard Worker   /// Verifies that the current value is which the bounds of the buffer, and if
29*890232f2SAndroid Build Coastguard Worker   /// the current `Value` is aligned properly
30*890232f2SAndroid Build Coastguard Worker   /// - Parameters:
31*890232f2SAndroid Build Coastguard Worker   ///   - verifier: Verifier that hosts the buffer
32*890232f2SAndroid Build Coastguard Worker   ///   - position: Current position within the buffer
33*890232f2SAndroid Build Coastguard Worker   ///   - type: The type of the object to be verified
34*890232f2SAndroid Build Coastguard Worker   /// - Throws: Errors coming from `inBuffer` function
35*890232f2SAndroid Build Coastguard Worker   static func verify<T>(
36*890232f2SAndroid Build Coastguard Worker     _ verifier: inout Verifier,
37*890232f2SAndroid Build Coastguard Worker     at position: Int,
38*890232f2SAndroid Build Coastguard Worker     of type: T.Type) throws where T: Verifiable
39*890232f2SAndroid Build Coastguard Worker }
40*890232f2SAndroid Build Coastguard Worker 
41*890232f2SAndroid Build Coastguard Worker extension Verifiable {
42*890232f2SAndroid Build Coastguard Worker 
43*890232f2SAndroid Build Coastguard Worker   /// Verifies if the current range to be read is within the bounds of the buffer,
44*890232f2SAndroid Build Coastguard Worker   /// and if the range is properly aligned
45*890232f2SAndroid Build Coastguard Worker   /// - Parameters:
46*890232f2SAndroid Build Coastguard Worker   ///   - verifier: Verifier that hosts the buffer
47*890232f2SAndroid Build Coastguard Worker   ///   - position: Current position within the buffer
48*890232f2SAndroid Build Coastguard Worker   ///   - type: The type of the object to be verified
49*890232f2SAndroid Build Coastguard Worker   /// - Throws: Erros thrown from `isAligned` & `rangeInBuffer`
50*890232f2SAndroid Build Coastguard Worker   /// - Returns: a tuple of the start position and the count of objects within the range
51*890232f2SAndroid Build Coastguard Worker   @discardableResult
52*890232f2SAndroid Build Coastguard Worker   public static func verifyRange<T>(
53*890232f2SAndroid Build Coastguard Worker     _ verifier: inout Verifier,
54*890232f2SAndroid Build Coastguard Worker     at position: Int, of type: T.Type) throws -> (start: Int, count: Int)
55*890232f2SAndroid Build Coastguard Worker   {
56*890232f2SAndroid Build Coastguard Worker     let len: UOffset = try verifier.getValue(at: position)
57*890232f2SAndroid Build Coastguard Worker     let intLen = Int(len)
58*890232f2SAndroid Build Coastguard Worker     let start = Int(clamping: (position &+ MemoryLayout<Int32>.size).magnitude)
59*890232f2SAndroid Build Coastguard Worker     try verifier.isAligned(position: start, type: type.self)
60*890232f2SAndroid Build Coastguard Worker     try verifier.rangeInBuffer(position: start, size: intLen)
61*890232f2SAndroid Build Coastguard Worker     return (start, intLen)
62*890232f2SAndroid Build Coastguard Worker   }
63*890232f2SAndroid Build Coastguard Worker }
64*890232f2SAndroid Build Coastguard Worker 
65*890232f2SAndroid Build Coastguard Worker extension Verifiable where Self: Scalar {
66*890232f2SAndroid Build Coastguard Worker 
67*890232f2SAndroid Build Coastguard Worker   /// Verifies that the current value is which the bounds of the buffer, and if
68*890232f2SAndroid Build Coastguard Worker   /// the current `Value` is aligned properly
69*890232f2SAndroid Build Coastguard Worker   /// - Parameters:
70*890232f2SAndroid Build Coastguard Worker   ///   - verifier: Verifier that hosts the buffer
71*890232f2SAndroid Build Coastguard Worker   ///   - position: Current position within the buffer
72*890232f2SAndroid Build Coastguard Worker   ///   - type: The type of the object to be verified
73*890232f2SAndroid Build Coastguard Worker   /// - Throws: Errors coming from `inBuffer` function
74*890232f2SAndroid Build Coastguard Worker   public static func verify<T>(
75*890232f2SAndroid Build Coastguard Worker     _ verifier: inout Verifier,
76*890232f2SAndroid Build Coastguard Worker     at position: Int,
77*890232f2SAndroid Build Coastguard Worker     of type: T.Type) throws where T: Verifiable
78*890232f2SAndroid Build Coastguard Worker   {
79*890232f2SAndroid Build Coastguard Worker     try verifier.inBuffer(position: position, of: type.self)
80*890232f2SAndroid Build Coastguard Worker   }
81*890232f2SAndroid Build Coastguard Worker }
82*890232f2SAndroid Build Coastguard Worker 
83*890232f2SAndroid Build Coastguard Worker // MARK: - ForwardOffset
84*890232f2SAndroid Build Coastguard Worker 
85*890232f2SAndroid Build Coastguard Worker /// ForwardOffset is a container to wrap around the Generic type to be verified
86*890232f2SAndroid Build Coastguard Worker /// from the flatbuffers object.
87*890232f2SAndroid Build Coastguard Worker public enum ForwardOffset<U>: Verifiable where U: Verifiable {
88*890232f2SAndroid Build Coastguard Worker 
89*890232f2SAndroid Build Coastguard Worker   /// Verifies that the current value is which the bounds of the buffer, and if
90*890232f2SAndroid Build Coastguard Worker   /// the current `Value` is aligned properly
91*890232f2SAndroid Build Coastguard Worker   /// - Parameters:
92*890232f2SAndroid Build Coastguard Worker   ///   - verifier: Verifier that hosts the buffer
93*890232f2SAndroid Build Coastguard Worker   ///   - position: Current position within the buffer
94*890232f2SAndroid Build Coastguard Worker   ///   - type: The type of the object to be verified
95*890232f2SAndroid Build Coastguard Worker   /// - Throws: Errors coming from `inBuffer` function
96*890232f2SAndroid Build Coastguard Worker   public static func verify<T>(
97*890232f2SAndroid Build Coastguard Worker     _ verifier: inout Verifier,
98*890232f2SAndroid Build Coastguard Worker     at position: Int,
99*890232f2SAndroid Build Coastguard Worker     of type: T.Type) throws where T: Verifiable
100*890232f2SAndroid Build Coastguard Worker   {
101*890232f2SAndroid Build Coastguard Worker     let offset: UOffset = try verifier.getValue(at: position)
102*890232f2SAndroid Build Coastguard Worker     let nextOffset = Int(clamping: (Int(offset) &+ position).magnitude)
103*890232f2SAndroid Build Coastguard Worker     try U.verify(&verifier, at: nextOffset, of: U.self)
104*890232f2SAndroid Build Coastguard Worker   }
105*890232f2SAndroid Build Coastguard Worker }
106*890232f2SAndroid Build Coastguard Worker 
107*890232f2SAndroid Build Coastguard Worker // MARK: - Vector
108*890232f2SAndroid Build Coastguard Worker 
109*890232f2SAndroid Build Coastguard Worker /// Vector is a container to wrap around the Generic type to be verified
110*890232f2SAndroid Build Coastguard Worker /// from the flatbuffers object.
111*890232f2SAndroid Build Coastguard Worker public enum Vector<U, S>: Verifiable where U: Verifiable, S: Verifiable {
112*890232f2SAndroid Build Coastguard Worker 
113*890232f2SAndroid Build Coastguard Worker   /// Verifies that the current value is which the bounds of the buffer, and if
114*890232f2SAndroid Build Coastguard Worker   /// the current `Value` is aligned properly
115*890232f2SAndroid Build Coastguard Worker   /// - Parameters:
116*890232f2SAndroid Build Coastguard Worker   ///   - verifier: Verifier that hosts the buffer
117*890232f2SAndroid Build Coastguard Worker   ///   - position: Current position within the buffer
118*890232f2SAndroid Build Coastguard Worker   ///   - type: The type of the object to be verified
119*890232f2SAndroid Build Coastguard Worker   /// - Throws: Errors coming from `inBuffer` function
120*890232f2SAndroid Build Coastguard Worker   public static func verify<T>(
121*890232f2SAndroid Build Coastguard Worker     _ verifier: inout Verifier,
122*890232f2SAndroid Build Coastguard Worker     at position: Int,
123*890232f2SAndroid Build Coastguard Worker     of type: T.Type) throws where T: Verifiable
124*890232f2SAndroid Build Coastguard Worker   {
125*890232f2SAndroid Build Coastguard Worker     /// checks if the next verification type S is equal to U of type forwardOffset
126*890232f2SAndroid Build Coastguard Worker     /// This had to be done since I couldnt find a solution for duplicate call functions
127*890232f2SAndroid Build Coastguard Worker     /// A fix will be appreciated
128*890232f2SAndroid Build Coastguard Worker     if U.self is ForwardOffset<S>.Type {
129*890232f2SAndroid Build Coastguard Worker       let range = try verifyRange(&verifier, at: position, of: UOffset.self)
130*890232f2SAndroid Build Coastguard Worker       for index in stride(
131*890232f2SAndroid Build Coastguard Worker         from: range.start,
132*890232f2SAndroid Build Coastguard Worker         to: Int(clamping: range.start &+ range.count),
133*890232f2SAndroid Build Coastguard Worker         by: MemoryLayout<UOffset>.size)
134*890232f2SAndroid Build Coastguard Worker       {
135*890232f2SAndroid Build Coastguard Worker         try U.verify(&verifier, at: index, of: U.self)
136*890232f2SAndroid Build Coastguard Worker       }
137*890232f2SAndroid Build Coastguard Worker     } else {
138*890232f2SAndroid Build Coastguard Worker       try S.verifyRange(&verifier, at: position, of: S.self)
139*890232f2SAndroid Build Coastguard Worker     }
140*890232f2SAndroid Build Coastguard Worker   }
141*890232f2SAndroid Build Coastguard Worker }
142*890232f2SAndroid Build Coastguard Worker 
143*890232f2SAndroid Build Coastguard Worker // MARK: - UnionVector
144*890232f2SAndroid Build Coastguard Worker 
145*890232f2SAndroid Build Coastguard Worker /// UnionVector is a container to wrap around the Generic type to be verified
146*890232f2SAndroid Build Coastguard Worker /// from the flatbuffers object.
147*890232f2SAndroid Build Coastguard Worker public enum UnionVector<S> where S: UnionEnum {
148*890232f2SAndroid Build Coastguard Worker 
149*890232f2SAndroid Build Coastguard Worker   /// Completion handler for the function Verify, that passes the verifier
150*890232f2SAndroid Build Coastguard Worker   /// enum type and position of union field
151*890232f2SAndroid Build Coastguard Worker   public typealias Completion = (inout Verifier, S, Int) throws -> Void
152*890232f2SAndroid Build Coastguard Worker 
153*890232f2SAndroid Build Coastguard Worker   /// Verifies if the current range to be read is within the bounds of the buffer,
154*890232f2SAndroid Build Coastguard Worker   /// and if the range is properly aligned. It also verifies if the union type is a
155*890232f2SAndroid Build Coastguard Worker   /// *valid/supported* union type.
156*890232f2SAndroid Build Coastguard Worker   /// - Parameters:
157*890232f2SAndroid Build Coastguard Worker   ///   - verifier: Verifier that hosts the buffer
158*890232f2SAndroid Build Coastguard Worker   ///   - keyPosition: Current union key position within the buffer
159*890232f2SAndroid Build Coastguard Worker   ///   - fieldPosition: Current union field position within the buffer
160*890232f2SAndroid Build Coastguard Worker   ///   - unionKeyName: Name of key to written if error is presented
161*890232f2SAndroid Build Coastguard Worker   ///   - fieldName: Name of field to written if error is presented
162*890232f2SAndroid Build Coastguard Worker   ///   - completion: Completion is a handler that WILL be called in the generated
163*890232f2SAndroid Build Coastguard Worker   ///   code to verify the actual objects
164*890232f2SAndroid Build Coastguard Worker   /// - Throws: FlatbuffersErrors
165*890232f2SAndroid Build Coastguard Worker   public static func verify(
166*890232f2SAndroid Build Coastguard Worker     _ verifier: inout Verifier,
167*890232f2SAndroid Build Coastguard Worker     keyPosition: Int,
168*890232f2SAndroid Build Coastguard Worker     fieldPosition: Int,
169*890232f2SAndroid Build Coastguard Worker     unionKeyName: String,
170*890232f2SAndroid Build Coastguard Worker     fieldName: String,
171*890232f2SAndroid Build Coastguard Worker     completion: @escaping Completion) throws
172*890232f2SAndroid Build Coastguard Worker   {
173*890232f2SAndroid Build Coastguard Worker     /// Get offset for union key vectors and offset vectors
174*890232f2SAndroid Build Coastguard Worker     let keyOffset: UOffset = try verifier.getValue(at: keyPosition)
175*890232f2SAndroid Build Coastguard Worker     let fieldOffset: UOffset = try verifier.getValue(at: fieldPosition)
176*890232f2SAndroid Build Coastguard Worker 
177*890232f2SAndroid Build Coastguard Worker     /// Check if values are within the buffer, returns the start position of vectors, and vector counts
178*890232f2SAndroid Build Coastguard Worker     /// Using &+ is safe since we already verified that the value is within the buffer, where the max is
179*890232f2SAndroid Build Coastguard Worker     /// going to be 2Gib and swift supports Int64 by default
180*890232f2SAndroid Build Coastguard Worker     let keysRange = try S.T.verifyRange(
181*890232f2SAndroid Build Coastguard Worker       &verifier,
182*890232f2SAndroid Build Coastguard Worker       at: Int(keyOffset) &+ keyPosition,
183*890232f2SAndroid Build Coastguard Worker       of: S.T.self)
184*890232f2SAndroid Build Coastguard Worker     let offsetsRange = try UOffset.verifyRange(
185*890232f2SAndroid Build Coastguard Worker       &verifier,
186*890232f2SAndroid Build Coastguard Worker       at: Int(fieldOffset) &+ fieldPosition,
187*890232f2SAndroid Build Coastguard Worker       of: UOffset.self)
188*890232f2SAndroid Build Coastguard Worker 
189*890232f2SAndroid Build Coastguard Worker     guard keysRange.count == offsetsRange.count else {
190*890232f2SAndroid Build Coastguard Worker       throw FlatbuffersErrors.unionVectorSize(
191*890232f2SAndroid Build Coastguard Worker         keyVectorSize: keysRange.count,
192*890232f2SAndroid Build Coastguard Worker         fieldVectorSize: offsetsRange.count,
193*890232f2SAndroid Build Coastguard Worker         unionKeyName: unionKeyName,
194*890232f2SAndroid Build Coastguard Worker         fieldName: fieldName)
195*890232f2SAndroid Build Coastguard Worker     }
196*890232f2SAndroid Build Coastguard Worker 
197*890232f2SAndroid Build Coastguard Worker     var count = 0
198*890232f2SAndroid Build Coastguard Worker     /// Iterate over the vector of keys and offsets.
199*890232f2SAndroid Build Coastguard Worker     while count < keysRange.count {
200*890232f2SAndroid Build Coastguard Worker 
201*890232f2SAndroid Build Coastguard Worker       /// index of readable enum value in array
202*890232f2SAndroid Build Coastguard Worker       let keysIndex = MemoryLayout<S.T>.size * count
203*890232f2SAndroid Build Coastguard Worker       guard let _enum = try S.init(value: verifier._buffer.read(
204*890232f2SAndroid Build Coastguard Worker         def: S.T.self,
205*890232f2SAndroid Build Coastguard Worker         position: keysRange.start + keysIndex)) else
206*890232f2SAndroid Build Coastguard Worker       {
207*890232f2SAndroid Build Coastguard Worker         throw FlatbuffersErrors.unknownUnionCase
208*890232f2SAndroid Build Coastguard Worker       }
209*890232f2SAndroid Build Coastguard Worker       /// index of readable offset value in array
210*890232f2SAndroid Build Coastguard Worker       let fieldIndex = MemoryLayout<UOffset>.size * count
211*890232f2SAndroid Build Coastguard Worker       try completion(&verifier, _enum, offsetsRange.start + fieldIndex)
212*890232f2SAndroid Build Coastguard Worker       count += 1
213*890232f2SAndroid Build Coastguard Worker     }
214*890232f2SAndroid Build Coastguard Worker   }
215*890232f2SAndroid Build Coastguard Worker }
216