208 lines
6.3 KiB
Swift
208 lines
6.3 KiB
Swift
/*
|
|
* Copyright 2023 Google Inc. All rights reserved.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#if !os(WASI)
|
|
import Foundation
|
|
#else
|
|
import SwiftOverlayShims
|
|
#endif
|
|
|
|
/// `TableVerifier` verifies a table object is within a provided memory.
|
|
/// It checks if all the objects for a specific generated table, are within
|
|
/// the bounds of the buffer, aligned.
|
|
public struct TableVerifier {
|
|
|
|
/// position of current table in `ByteBuffer`
|
|
fileprivate var _position: Int
|
|
|
|
/// Current VTable position
|
|
fileprivate var _vtable: Int
|
|
|
|
/// Length of current VTable
|
|
fileprivate var _vtableLength: Int
|
|
|
|
/// `Verifier` object created in the base verifable call.
|
|
fileprivate var _verifier: Verifier
|
|
|
|
/// Creates a `TableVerifier` verifier that allows the Flatbuffer object
|
|
/// to verify the buffer before accessing any of the data.
|
|
///
|
|
/// - Parameters:
|
|
/// - position: Current table Position
|
|
/// - vtable: Current `VTable` position
|
|
/// - vtableLength: Current `VTable` length
|
|
/// - verifier: `Verifier` Object that caches the data of the verifiable object
|
|
internal init(
|
|
position: Int,
|
|
vtable: Int,
|
|
vtableLength: Int,
|
|
verifier: inout Verifier)
|
|
{
|
|
_position = position
|
|
_vtable = vtable
|
|
_vtableLength = vtableLength
|
|
_verifier = verifier
|
|
}
|
|
|
|
/// Dereference the current object position from the `VTable`
|
|
/// - Parameter field: Current VTable refrence to position.
|
|
/// - Throws: A `FlatbuffersErrors` incase the voffset is not aligned/outOfBounds/apparentSizeTooLarge
|
|
/// - Returns: An optional position for current field
|
|
internal mutating func dereference(_ field: VOffset) throws -> Int? {
|
|
if field >= _vtableLength {
|
|
return nil
|
|
}
|
|
|
|
/// Reading the offset for the field needs to be read.
|
|
let offset: VOffset = try _verifier.getValue(
|
|
at: Int(clamping: _vtable &+ Int(field)))
|
|
|
|
if offset > 0 {
|
|
return Int(clamping: _position &+ Int(offset))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
/// Visits all the fields within the table to validate the integrity
|
|
/// of the data
|
|
/// - Parameters:
|
|
/// - field: voffset of the current field to be read
|
|
/// - fieldName: fieldname to report data Errors.
|
|
/// - required: If the field has to be available in the buffer
|
|
/// - type: Type of field to be read
|
|
/// - Throws: A `FlatbuffersErrors` where the field is corrupt
|
|
public mutating func visit<T>(
|
|
field: VOffset,
|
|
fieldName: String,
|
|
required: Bool,
|
|
type: T.Type) throws where T: Verifiable
|
|
{
|
|
let derefValue = try dereference(field)
|
|
|
|
if let value = derefValue {
|
|
try T.verify(&_verifier, at: value, of: T.self)
|
|
return
|
|
}
|
|
if required {
|
|
throw FlatbuffersErrors.requiredFieldDoesntExist(
|
|
position: field,
|
|
name: fieldName)
|
|
}
|
|
}
|
|
|
|
/// Visits all the fields for a union object within the table to
|
|
/// validate the integrity of the data
|
|
/// - Parameters:
|
|
/// - key: Current Key Voffset
|
|
/// - field: Current field Voffset
|
|
/// - unionKeyName: Union key name
|
|
/// - fieldName: Field key name
|
|
/// - required: indicates if an object is required to be present
|
|
/// - completion: Completion is a handler that WILL be called in the generated
|
|
/// - Throws: A `FlatbuffersErrors` where the field is corrupt
|
|
public mutating func visit<T>(
|
|
unionKey key: VOffset,
|
|
unionField field: VOffset,
|
|
unionKeyName: String,
|
|
fieldName: String,
|
|
required: Bool,
|
|
completion: @escaping (inout Verifier, T, Int) throws -> Void) throws
|
|
where T: UnionEnum
|
|
{
|
|
let keyPos = try dereference(key)
|
|
let valPos = try dereference(field)
|
|
|
|
if keyPos == nil && valPos == nil {
|
|
if required {
|
|
throw FlatbuffersErrors.requiredFieldDoesntExist(
|
|
position: key,
|
|
name: unionKeyName)
|
|
}
|
|
return
|
|
}
|
|
|
|
if let _key = keyPos,
|
|
let _val = valPos
|
|
{
|
|
/// verifiying that the key is within the buffer
|
|
try T.T.verify(&_verifier, at: _key, of: T.T.self)
|
|
guard let _enum = try T.init(value: _verifier._buffer.read(
|
|
def: T.T.self,
|
|
position: _key)) else
|
|
{
|
|
throw FlatbuffersErrors.unknownUnionCase
|
|
}
|
|
/// we are assuming that Unions will always be of type Uint8
|
|
try completion(
|
|
&_verifier,
|
|
_enum,
|
|
_val)
|
|
return
|
|
}
|
|
throw FlatbuffersErrors.valueNotFound(
|
|
key: keyPos,
|
|
keyName: unionKeyName,
|
|
field: valPos,
|
|
fieldName: fieldName)
|
|
}
|
|
|
|
/// Visits and validates all the objects within a union vector
|
|
/// - Parameters:
|
|
/// - key: Current Key Voffset
|
|
/// - field: Current field Voffset
|
|
/// - unionKeyName: Union key name
|
|
/// - fieldName: Field key name
|
|
/// - required: indicates if an object is required to be present
|
|
/// - completion: Completion is a handler that WILL be called in the generated
|
|
/// - Throws: A `FlatbuffersErrors` where the field is corrupt
|
|
public mutating func visitUnionVector<T>(
|
|
unionKey key: VOffset,
|
|
unionField field: VOffset,
|
|
unionKeyName: String,
|
|
fieldName: String,
|
|
required: Bool,
|
|
completion: @escaping (inout Verifier, T, Int) throws -> Void) throws
|
|
where T: UnionEnum
|
|
{
|
|
let keyVectorPosition = try dereference(key)
|
|
let offsetVectorPosition = try dereference(field)
|
|
|
|
if let keyPos = keyVectorPosition,
|
|
let valPos = offsetVectorPosition
|
|
{
|
|
try UnionVector<T>.verify(
|
|
&_verifier,
|
|
keyPosition: keyPos,
|
|
fieldPosition: valPos,
|
|
unionKeyName: unionKeyName,
|
|
fieldName: fieldName,
|
|
completion: completion)
|
|
return
|
|
}
|
|
if required {
|
|
throw FlatbuffersErrors.requiredFieldDoesntExist(
|
|
position: field,
|
|
name: fieldName)
|
|
}
|
|
}
|
|
|
|
/// Finishs the current Table verifier, and subtracts the current
|
|
/// table from the incremented depth.
|
|
public mutating func finish() {
|
|
_verifier.finish()
|
|
}
|
|
}
|