damus

nostr ios client
git clone git://jb55.com/damus
Log | Files | Refs | README | LICENSE

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 }