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