damus

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

TableVerifier.swift (6455B)


      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 /// `TableVerifier` verifies a table object is within a provided memory.
     24 /// It checks if all the objects for a specific generated table, are within
     25 /// the bounds of the buffer, aligned.
     26 public struct TableVerifier {
     27 
     28   /// position of current table in `ByteBuffer`
     29   fileprivate var _position: Int
     30 
     31   /// Current VTable position
     32   fileprivate var _vtable: Int
     33 
     34   /// Length of current VTable
     35   fileprivate var _vtableLength: Int
     36 
     37   /// `Verifier` object created in the base verifable call.
     38   fileprivate var _verifier: Verifier
     39 
     40   /// Creates a `TableVerifier` verifier that allows the Flatbuffer object
     41   /// to verify the buffer before accessing any of the data.
     42   ///
     43   /// - Parameters:
     44   ///   - position: Current table Position
     45   ///   - vtable: Current `VTable` position
     46   ///   - vtableLength: Current `VTable` length
     47   ///   - verifier: `Verifier` Object  that caches the data of the verifiable object
     48   internal init(
     49     position: Int,
     50     vtable: Int,
     51     vtableLength: Int,
     52     verifier: inout Verifier)
     53   {
     54     _position = position
     55     _vtable = vtable
     56     _vtableLength = vtableLength
     57     _verifier = verifier
     58   }
     59 
     60   /// Dereference the current object position from the `VTable`
     61   /// - Parameter field: Current VTable refrence to  position.
     62   /// - Throws: A `FlatbuffersErrors` incase the voffset is not aligned/outOfBounds/apparentSizeTooLarge
     63   /// - Returns: An optional position for current field
     64   internal mutating func dereference(_ field: VOffset) throws -> Int? {
     65     if field >= _vtableLength {
     66       return nil
     67     }
     68 
     69     /// Reading the offset for the field needs to be read.
     70     let offset: VOffset = try _verifier.getValue(
     71       at: Int(clamping: _vtable &+ Int(field)))
     72 
     73     if offset > 0 {
     74       return Int(clamping: _position &+ Int(offset))
     75     }
     76     return nil
     77   }
     78 
     79   /// Visits all the fields within the table to validate the integrity
     80   /// of the data
     81   /// - Parameters:
     82   ///   - field: voffset of the current field to be read
     83   ///   - fieldName: fieldname to report data Errors.
     84   ///   - required: If the field has to be available in the buffer
     85   ///   - type: Type of field to be read
     86   /// - Throws: A `FlatbuffersErrors` where the field is corrupt
     87   public mutating func visit<T>(
     88     field: VOffset,
     89     fieldName: String,
     90     required: Bool,
     91     type: T.Type) throws where T: Verifiable
     92   {
     93     let derefValue = try dereference(field)
     94 
     95     if let value = derefValue {
     96       try T.verify(&_verifier, at: value, of: T.self)
     97       return
     98     }
     99     if required {
    100       throw FlatbuffersErrors.requiredFieldDoesntExist(
    101         position: field,
    102         name: fieldName)
    103     }
    104   }
    105 
    106   /// Visits all the fields for a union object within the table to
    107   /// validate the integrity of the data
    108   /// - Parameters:
    109   ///   - key: Current Key Voffset
    110   ///   - field: Current field Voffset
    111   ///   - unionKeyName: Union key name
    112   ///   - fieldName: Field key name
    113   ///   - required: indicates if an object is required to be present
    114   ///   - completion: Completion is a handler that WILL be called in the generated
    115   /// - Throws: A `FlatbuffersErrors` where the field is corrupt
    116   public mutating func visit<T>(
    117     unionKey key: VOffset,
    118     unionField field: VOffset,
    119     unionKeyName: String,
    120     fieldName: String,
    121     required: Bool,
    122     completion: @escaping (inout Verifier, T, Int) throws -> Void) throws
    123     where T: UnionEnum
    124   {
    125     let keyPos = try dereference(key)
    126     let valPos = try dereference(field)
    127 
    128     if keyPos == nil && valPos == nil {
    129       if required {
    130         throw FlatbuffersErrors.requiredFieldDoesntExist(
    131           position: key,
    132           name: unionKeyName)
    133       }
    134       return
    135     }
    136 
    137     if let _key = keyPos,
    138        let _val = valPos
    139     {
    140       /// verifiying that the key is within the buffer
    141       try T.T.verify(&_verifier, at: _key, of: T.T.self)
    142       guard let _enum = try T.init(value: _verifier._buffer.read(
    143         def: T.T.self,
    144         position: _key)) else
    145       {
    146         throw FlatbuffersErrors.unknownUnionCase
    147       }
    148       /// we are assuming that Unions will always be of type Uint8
    149       try completion(
    150         &_verifier,
    151         _enum,
    152         _val)
    153       return
    154     }
    155     throw FlatbuffersErrors.valueNotFound(
    156       key: keyPos,
    157       keyName: unionKeyName,
    158       field: valPos,
    159       fieldName: fieldName)
    160   }
    161 
    162   /// Visits and validates all the objects within a union vector
    163   /// - Parameters:
    164   ///   - key: Current Key Voffset
    165   ///   - field: Current field Voffset
    166   ///   - unionKeyName: Union key name
    167   ///   - fieldName: Field key name
    168   ///   - required: indicates if an object is required to be present
    169   ///   - completion: Completion is a handler that WILL be called in the generated
    170   /// - Throws: A `FlatbuffersErrors` where the field is corrupt
    171   public mutating func visitUnionVector<T>(
    172     unionKey key: VOffset,
    173     unionField field: VOffset,
    174     unionKeyName: String,
    175     fieldName: String,
    176     required: Bool,
    177     completion: @escaping (inout Verifier, T, Int) throws -> Void) throws
    178     where T: UnionEnum
    179   {
    180     let keyVectorPosition = try dereference(key)
    181     let offsetVectorPosition = try dereference(field)
    182 
    183     if let keyPos = keyVectorPosition,
    184        let valPos = offsetVectorPosition
    185     {
    186       try UnionVector<T>.verify(
    187         &_verifier,
    188         keyPosition: keyPos,
    189         fieldPosition: valPos,
    190         unionKeyName: unionKeyName,
    191         fieldName: fieldName,
    192         completion: completion)
    193       return
    194     }
    195     if required {
    196       throw FlatbuffersErrors.requiredFieldDoesntExist(
    197         position: field,
    198         name: fieldName)
    199     }
    200   }
    201 
    202   /// Finishs the current Table verifier, and subtracts the current
    203   /// table from the incremented depth.
    204   public mutating func finish() {
    205     _verifier.finish()
    206   }
    207 }