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