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