mirror of
https://github.com/google/flatbuffers.git
synced 2026-06-17 15:45:21 +00:00
Implements verifier and code gen for swift (#6373)
Updates test cases on linux Adhere to new protocol naming Adds fuzzing Adds documentation Adds support for string unions Updated fuzzer generated code
This commit is contained in:
@@ -16,6 +16,9 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
/// `ByteBuffer` is the interface that stores the data for a `Flatbuffers` object
|
||||
/// it allows users to write and read data directly from memory thus the use of its
|
||||
/// functions should be used
|
||||
@frozen
|
||||
public struct ByteBuffer {
|
||||
|
||||
@@ -32,7 +35,9 @@ public struct ByteBuffer {
|
||||
|
||||
@usableFromInline
|
||||
init(count: Int, alignment: Int) {
|
||||
memory = UnsafeMutableRawPointer.allocate(byteCount: count, alignment: alignment)
|
||||
memory = UnsafeMutableRawPointer.allocate(
|
||||
byteCount: count,
|
||||
alignment: alignment)
|
||||
capacity = count
|
||||
unowned = false
|
||||
}
|
||||
@@ -69,7 +74,7 @@ public struct ByteBuffer {
|
||||
/// Reallocates the buffer incase the object to be written doesnt fit in the current buffer
|
||||
/// - Parameter size: Size of the current object
|
||||
@usableFromInline
|
||||
internal func reallocate(_ size: Int, writerSize: Int, alignment: Int) {
|
||||
func reallocate(_ size: Int, writerSize: Int, alignment: Int) {
|
||||
let currentWritingIndex = capacity &- writerSize
|
||||
while capacity <= writerSize &+ size {
|
||||
capacity = capacity << 1
|
||||
@@ -78,7 +83,9 @@ public struct ByteBuffer {
|
||||
/// solution take from Apple-NIO
|
||||
capacity = capacity.convertToPowerofTwo
|
||||
|
||||
let newData = UnsafeMutableRawPointer.allocate(byteCount: capacity, alignment: alignment)
|
||||
let newData = UnsafeMutableRawPointer.allocate(
|
||||
byteCount: capacity,
|
||||
alignment: alignment)
|
||||
memset(newData, 0, capacity &- writerSize)
|
||||
memcpy(
|
||||
newData.advanced(by: capacity &- writerSize),
|
||||
@@ -94,9 +101,9 @@ public struct ByteBuffer {
|
||||
/// The size of the elements written to the buffer + their paddings
|
||||
private var _writerSize: Int = 0
|
||||
/// Aliginment of the current memory being written to the buffer
|
||||
internal var alignment = 1
|
||||
var alignment = 1
|
||||
/// Current Index which is being used to write to the buffer, it is written from the end to the start of the buffer
|
||||
internal var writerIndex: Int { _storage.capacity &- _writerSize }
|
||||
var writerIndex: Int { _storage.capacity &- _writerSize }
|
||||
|
||||
/// Reader is the position of the current Writer Index (capacity - size)
|
||||
public var reader: Int { writerIndex }
|
||||
@@ -166,7 +173,7 @@ public struct ByteBuffer {
|
||||
/// - Parameters:
|
||||
/// - memory: Current memory of the buffer
|
||||
/// - count: count of bytes
|
||||
internal init(memory: UnsafeMutableRawPointer, count: Int) {
|
||||
init(memory: UnsafeMutableRawPointer, count: Int) {
|
||||
_storage = Storage(count: count, alignment: alignment)
|
||||
_storage.copy(from: memory, count: count)
|
||||
_writerSize = _storage.capacity
|
||||
@@ -177,7 +184,11 @@ public struct ByteBuffer {
|
||||
/// - memory: Current memory of the buffer
|
||||
/// - count: count of bytes
|
||||
/// - removeBytes: Removes a number of bytes from the current size
|
||||
internal init(memory: UnsafeMutableRawPointer, count: Int, removing removeBytes: Int) {
|
||||
init(
|
||||
memory: UnsafeMutableRawPointer,
|
||||
count: Int,
|
||||
removing removeBytes: Int)
|
||||
{
|
||||
_storage = Storage(count: count, alignment: alignment)
|
||||
_storage.copy(from: memory, count: count)
|
||||
_writerSize = removeBytes
|
||||
@@ -247,7 +258,7 @@ public struct ByteBuffer {
|
||||
/// - bytes: Pointer to the view
|
||||
/// - len: Size of string
|
||||
@inline(__always)
|
||||
mutating internal func push(
|
||||
mutating func push(
|
||||
bytes: UnsafeBufferPointer<String.UTF8View.Element>,
|
||||
len: Int) -> Bool
|
||||
{
|
||||
@@ -292,20 +303,18 @@ public struct ByteBuffer {
|
||||
/// pops the written VTable if it's already written into the buffer
|
||||
/// - Parameter size: size of the `VTable`
|
||||
@inline(__always)
|
||||
mutating internal func pop(_ size: Int) {
|
||||
mutating func pop(_ size: Int) {
|
||||
assert((_writerSize &- size) > 0, "New size should NOT be a negative number")
|
||||
memset(_storage.memory.advanced(by: writerIndex), 0, _writerSize &- size)
|
||||
_writerSize = size
|
||||
}
|
||||
|
||||
/// Clears the current size of the buffer
|
||||
@inline(__always)
|
||||
mutating public func clearSize() {
|
||||
_writerSize = 0
|
||||
}
|
||||
|
||||
/// Clears the current instance of the buffer, replacing it with new memory
|
||||
@inline(__always)
|
||||
mutating public func clear() {
|
||||
_writerSize = 0
|
||||
alignment = 1
|
||||
@@ -317,10 +326,7 @@ public struct ByteBuffer {
|
||||
/// - def: Type of the object
|
||||
/// - position: the index of the object in the buffer
|
||||
public func read<T>(def: T.Type, position: Int) -> T {
|
||||
assert(
|
||||
position + MemoryLayout<T>.size <= _storage.capacity,
|
||||
"Reading out of bounds is illegal")
|
||||
return _storage.memory.advanced(by: position).load(as: T.self)
|
||||
_storage.memory.advanced(by: position).load(as: T.self)
|
||||
}
|
||||
|
||||
/// Reads a slice from the memory assuming a type of T
|
||||
@@ -329,14 +335,14 @@ public struct ByteBuffer {
|
||||
/// - count: count of bytes in memory
|
||||
@inline(__always)
|
||||
public func readSlice<T>(
|
||||
index: Int32,
|
||||
count: Int32) -> [T]
|
||||
index: Int,
|
||||
count: Int) -> [T]
|
||||
{
|
||||
let _index = Int(index)
|
||||
let _count = Int(count)
|
||||
assert(_index + _count <= _storage.capacity, "Reading out of bounds is illegal")
|
||||
let start = _storage.memory.advanced(by: _index).assumingMemoryBound(to: T.self)
|
||||
let array = UnsafeBufferPointer(start: start, count: _count)
|
||||
assert(
|
||||
index + count <= _storage.capacity,
|
||||
"Reading out of bounds is illegal")
|
||||
let start = _storage.memory.advanced(by: index).assumingMemoryBound(to: T.self)
|
||||
let array = UnsafeBufferPointer(start: start, count: count)
|
||||
return Array(array)
|
||||
}
|
||||
|
||||
@@ -345,17 +351,16 @@ public struct ByteBuffer {
|
||||
/// - index: index of the string in the buffer
|
||||
/// - count: length of the string
|
||||
/// - type: Encoding of the string
|
||||
@inline(__always)
|
||||
public func readString(
|
||||
at index: Int32,
|
||||
count: Int32,
|
||||
at index: Int,
|
||||
count: Int,
|
||||
type: String.Encoding = .utf8) -> String?
|
||||
{
|
||||
let _index = Int(index)
|
||||
let _count = Int(count)
|
||||
assert(_index + _count <= _storage.capacity, "Reading out of bounds is illegal")
|
||||
let start = _storage.memory.advanced(by: _index).assumingMemoryBound(to: UInt8.self)
|
||||
let bufprt = UnsafeBufferPointer(start: start, count: _count)
|
||||
assert(
|
||||
index + count <= _storage.capacity,
|
||||
"Reading out of bounds is illegal")
|
||||
let start = _storage.memory.advanced(by: index).assumingMemoryBound(to: UInt8.self)
|
||||
let bufprt = UnsafeBufferPointer(start: start, count: count)
|
||||
return String(bytes: Array(bufprt), encoding: type)
|
||||
}
|
||||
|
||||
@@ -363,12 +368,22 @@ public struct ByteBuffer {
|
||||
/// - Parameter removeBytes: the amount of bytes to remove from the current Size
|
||||
public func duplicate(removing removeBytes: Int = 0) -> ByteBuffer {
|
||||
assert(removeBytes > 0, "Can NOT remove negative bytes")
|
||||
assert(removeBytes < _storage.capacity, "Can NOT remove more bytes than the ones allocated")
|
||||
assert(
|
||||
removeBytes < _storage.capacity,
|
||||
"Can NOT remove more bytes than the ones allocated")
|
||||
return ByteBuffer(
|
||||
memory: _storage.memory,
|
||||
count: _storage.capacity,
|
||||
removing: _writerSize &- removeBytes)
|
||||
}
|
||||
|
||||
/// SkipPrefix Skips the first 4 bytes in case one of the following
|
||||
/// functions are called `getPrefixedSizeCheckedRoot` & `getPrefixedSizeRoot`
|
||||
/// which allows us to skip the first 4 bytes instead of recreating the buffer
|
||||
@usableFromInline
|
||||
mutating func skipPrefix() {
|
||||
_writerSize = _writerSize &- MemoryLayout<Int32>.size
|
||||
}
|
||||
}
|
||||
|
||||
extension ByteBuffer: CustomDebugStringConvertible {
|
||||
|
||||
@@ -32,14 +32,16 @@ public typealias VOffset = UInt16
|
||||
/// Maximum size for a buffer
|
||||
public let FlatBufferMaxSize = UInt32.max << ((MemoryLayout<SOffset>.size * 8 - 1) - 1)
|
||||
|
||||
/// Protocol that confirms all the numbers
|
||||
/// Protocol that All Scalars should conform to
|
||||
///
|
||||
/// Scalar is used to confirm all the numbers that can be represented in a FlatBuffer. It's used to write/read from the buffer.
|
||||
/// Scalar is used to conform all the numbers that can be represented in a FlatBuffer. It's used to write/read from the buffer.
|
||||
public protocol Scalar: Equatable {
|
||||
associatedtype NumericValue
|
||||
var convertedEndian: NumericValue { get }
|
||||
}
|
||||
|
||||
extension Scalar where Self: Verifiable {}
|
||||
|
||||
extension Scalar where Self: FixedWidthInteger {
|
||||
/// Converts the value from BigEndian to LittleEndian
|
||||
///
|
||||
@@ -49,7 +51,7 @@ extension Scalar where Self: FixedWidthInteger {
|
||||
}
|
||||
}
|
||||
|
||||
extension Double: Scalar {
|
||||
extension Double: Scalar, Verifiable {
|
||||
public typealias NumericValue = UInt64
|
||||
|
||||
public var convertedEndian: UInt64 {
|
||||
@@ -57,7 +59,7 @@ extension Double: Scalar {
|
||||
}
|
||||
}
|
||||
|
||||
extension Float32: Scalar {
|
||||
extension Float32: Scalar, Verifiable {
|
||||
public typealias NumericValue = UInt32
|
||||
|
||||
public var convertedEndian: UInt32 {
|
||||
@@ -65,7 +67,7 @@ extension Float32: Scalar {
|
||||
}
|
||||
}
|
||||
|
||||
extension Bool: Scalar {
|
||||
extension Bool: Scalar, Verifiable {
|
||||
public var convertedEndian: UInt8 {
|
||||
self == true ? 1 : 0
|
||||
}
|
||||
@@ -73,39 +75,39 @@ extension Bool: Scalar {
|
||||
public typealias NumericValue = UInt8
|
||||
}
|
||||
|
||||
extension Int: Scalar {
|
||||
extension Int: Scalar, Verifiable {
|
||||
public typealias NumericValue = Int
|
||||
}
|
||||
|
||||
extension Int8: Scalar {
|
||||
extension Int8: Scalar, Verifiable {
|
||||
public typealias NumericValue = Int8
|
||||
}
|
||||
|
||||
extension Int16: Scalar {
|
||||
extension Int16: Scalar, Verifiable {
|
||||
public typealias NumericValue = Int16
|
||||
}
|
||||
|
||||
extension Int32: Scalar {
|
||||
extension Int32: Scalar, Verifiable {
|
||||
public typealias NumericValue = Int32
|
||||
}
|
||||
|
||||
extension Int64: Scalar {
|
||||
extension Int64: Scalar, Verifiable {
|
||||
public typealias NumericValue = Int64
|
||||
}
|
||||
|
||||
extension UInt8: Scalar {
|
||||
extension UInt8: Scalar, Verifiable {
|
||||
public typealias NumericValue = UInt8
|
||||
}
|
||||
|
||||
extension UInt16: Scalar {
|
||||
extension UInt16: Scalar, Verifiable {
|
||||
public typealias NumericValue = UInt16
|
||||
}
|
||||
|
||||
extension UInt32: Scalar {
|
||||
extension UInt32: Scalar, Verifiable {
|
||||
public typealias NumericValue = UInt32
|
||||
}
|
||||
|
||||
extension UInt64: Scalar {
|
||||
extension UInt64: Scalar, Verifiable {
|
||||
public typealias NumericValue = UInt64
|
||||
}
|
||||
|
||||
|
||||
54
swift/Sources/FlatBuffers/Enum.swift
Normal file
54
swift/Sources/FlatBuffers/Enum.swift
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2021 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.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Enum is a protocol that all flatbuffers enums should conform to
|
||||
/// Since it allows us to get the actual `ByteSize` and `Value`
|
||||
public protocol Enum {
|
||||
/// associatedtype that the type of the enum should conform to
|
||||
associatedtype T: Scalar & Verifiable
|
||||
/// Size of the current associatedtype in the enum
|
||||
static var byteSize: Int { get }
|
||||
/// The current value the enum hosts
|
||||
var value: T { get }
|
||||
}
|
||||
|
||||
extension Enum where Self: Verifiable {
|
||||
|
||||
/// Verifies that the current value is which the bounds of the buffer, and if
|
||||
/// the current `Value` is aligned properly
|
||||
/// - Parameters:
|
||||
/// - verifier: Verifier that hosts the buffer
|
||||
/// - position: Current position within the buffer
|
||||
/// - type: The type of the object to be verified
|
||||
/// - Throws: Errors coming from `inBuffer` function
|
||||
public static func verify<T>(
|
||||
_ verifier: inout Verifier,
|
||||
at position: Int,
|
||||
of type: T.Type) throws where T: Verifiable
|
||||
{
|
||||
try verifier.inBuffer(position: position, of: type.self)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// UnionEnum is a Protocol that allows us to create Union type of enums
|
||||
/// and their value initializers. Since an `init` was required by
|
||||
/// the verifier
|
||||
public protocol UnionEnum: Enum {
|
||||
init?(value: T) throws
|
||||
}
|
||||
@@ -16,6 +16,9 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
/// `FlatBufferBuilder` builds a `FlatBuffer` through manipulating its internal state.
|
||||
/// This is done by creating a `ByteBuffer` that hosts the incoming data and
|
||||
/// has a hardcoded growth limit of `2GiB` which is set by the Flatbuffers standards
|
||||
@frozen
|
||||
public struct FlatBufferBuilder {
|
||||
|
||||
@@ -74,7 +77,9 @@ public struct FlatBufferBuilder {
|
||||
/// Returns A sized Buffer from the readable bytes
|
||||
public var sizedBuffer: ByteBuffer {
|
||||
assert(finished, "Data shouldn't be called before finish()")
|
||||
return ByteBuffer(memory: _bb.memory.advanced(by: _bb.reader), count: Int(_bb.size))
|
||||
return ByteBuffer(
|
||||
memory: _bb.memory.advanced(by: _bb.reader),
|
||||
count: Int(_bb.size))
|
||||
}
|
||||
|
||||
// MARK: - Init
|
||||
@@ -112,7 +117,9 @@ public struct FlatBufferBuilder {
|
||||
for field in fields {
|
||||
let start = _bb.capacity &- Int(table.o)
|
||||
let startTable = start &- Int(_bb.read(def: Int32.self, position: start))
|
||||
let isOkay = _bb.read(def: VOffset.self, position: startTable &+ Int(field)) != 0
|
||||
let isOkay = _bb.read(
|
||||
def: VOffset.self,
|
||||
position: startTable &+ Int(field)) != 0
|
||||
assert(isOkay, "Flatbuffers requires the following field")
|
||||
}
|
||||
}
|
||||
@@ -122,9 +129,15 @@ public struct FlatBufferBuilder {
|
||||
/// - offset: Offset of the table
|
||||
/// - fileId: Takes the fileId
|
||||
/// - prefix: if false it wont add the size of the buffer
|
||||
mutating public func finish(offset: Offset, fileId: String, addPrefix prefix: Bool = false) {
|
||||
mutating public func finish(
|
||||
offset: Offset,
|
||||
fileId: String,
|
||||
addPrefix prefix: Bool = false)
|
||||
{
|
||||
let size = MemoryLayout<UOffset>.size
|
||||
preAlign(len: size &+ (prefix ? size : 0) &+ FileIdLength, alignment: _minAlignment)
|
||||
preAlign(
|
||||
len: size &+ (prefix ? size : 0) &+ FileIdLength,
|
||||
alignment: _minAlignment)
|
||||
assert(fileId.count == FileIdLength, "Flatbuffers requires file id to be 4")
|
||||
_bb.push(string: fileId, len: 4)
|
||||
finish(offset: offset, addPrefix: prefix)
|
||||
@@ -134,7 +147,10 @@ public struct FlatBufferBuilder {
|
||||
/// - Parameters:
|
||||
/// - offset: Offset of the table
|
||||
/// - prefix: if false it wont add the size of the buffer
|
||||
mutating public func finish(offset: Offset, addPrefix prefix: Bool = false) {
|
||||
mutating public func finish(
|
||||
offset: Offset,
|
||||
addPrefix prefix: Bool = false)
|
||||
{
|
||||
notNested()
|
||||
let size = MemoryLayout<UOffset>.size
|
||||
preAlign(len: size &+ (prefix ? size : 0), alignment: _minAlignment)
|
||||
@@ -184,7 +200,10 @@ public struct FlatBufferBuilder {
|
||||
itr = itr &+ _vtableStorage.size
|
||||
guard loaded.offset != 0 else { continue }
|
||||
let _index = (_bb.writerIndex &+ Int(loaded.position))
|
||||
_bb.write(value: VOffset(vTableOffset &- loaded.offset), index: _index, direct: true)
|
||||
_bb.write(
|
||||
value: VOffset(vTableOffset &- loaded.offset),
|
||||
index: _index,
|
||||
direct: true)
|
||||
}
|
||||
|
||||
_vtableStorage.clear()
|
||||
@@ -375,7 +394,9 @@ public struct FlatBufferBuilder {
|
||||
/// - Parameter structs: A vector of structs
|
||||
/// - Returns: offset of the vector
|
||||
mutating public func createVector<T: NativeStruct>(ofStructs structs: [T]) -> Offset {
|
||||
startVector(structs.count * MemoryLayout<T>.size, elementSize: MemoryLayout<T>.alignment)
|
||||
startVector(
|
||||
structs.count * MemoryLayout<T>.size,
|
||||
elementSize: MemoryLayout<T>.alignment)
|
||||
for i in structs.reversed() {
|
||||
_ = create(struct: i)
|
||||
}
|
||||
@@ -394,7 +415,9 @@ public struct FlatBufferBuilder {
|
||||
struct s: T, position: VOffset) -> Offset
|
||||
{
|
||||
let offset = create(struct: s)
|
||||
_vtableStorage.add(loc: FieldLoc(offset: _bb.size, position: VOffset(position)))
|
||||
_vtableStorage.add(loc: FieldLoc(
|
||||
offset: _bb.size,
|
||||
position: VOffset(position)))
|
||||
return offset
|
||||
}
|
||||
|
||||
@@ -529,8 +552,6 @@ extension FlatBufferBuilder: CustomDebugStringConvertible {
|
||||
var numOfFields: Int = 0
|
||||
/// Last written Index
|
||||
var writtenIndex: Int = 0
|
||||
/// the amount of added elements into the buffer
|
||||
var addedElements: Int { capacity - (numOfFields &* size) }
|
||||
|
||||
/// Creates the memory to store the buffer in
|
||||
@usableFromInline
|
||||
@@ -555,7 +576,9 @@ extension FlatBufferBuilder: CustomDebugStringConvertible {
|
||||
/// and max offset
|
||||
/// - Parameter loc: Location of encoded element
|
||||
func add(loc: FieldLoc) {
|
||||
memory.baseAddress?.advanced(by: writtenIndex).storeBytes(of: loc, as: FieldLoc.self)
|
||||
memory.baseAddress?.advanced(by: writtenIndex).storeBytes(
|
||||
of: loc,
|
||||
as: FieldLoc.self)
|
||||
writtenIndex = writtenIndex &+ size
|
||||
numOfFields = numOfFields &+ 1
|
||||
maxOffset = max(loc.position, maxOffset)
|
||||
@@ -574,7 +597,9 @@ extension FlatBufferBuilder: CustomDebugStringConvertible {
|
||||
func ensure(space: Int) {
|
||||
guard space &+ writtenIndex > capacity else { return }
|
||||
memory.deallocate()
|
||||
memory = UnsafeMutableRawBufferPointer.allocate(byteCount: space, alignment: size)
|
||||
memory = UnsafeMutableRawBufferPointer.allocate(
|
||||
byteCount: space,
|
||||
alignment: size)
|
||||
capacity = space
|
||||
}
|
||||
|
||||
|
||||
@@ -31,15 +31,26 @@ public protocol FlatBufferObject: FlatbuffersInitializable {
|
||||
var __buffer: ByteBuffer! { get }
|
||||
}
|
||||
|
||||
/// `ObjectAPIPacker` is a protocol that allows object to pack and unpack from a
|
||||
/// `NativeObject` to a flatbuffers Object and vice versa.
|
||||
public protocol ObjectAPIPacker {
|
||||
/// associatedtype to the object that should be unpacked.
|
||||
associatedtype T
|
||||
|
||||
/// `pack` tries packs the variables of a native Object into the `ByteBuffer` by using
|
||||
/// the FlatBufferBuilder
|
||||
/// - Parameters:
|
||||
/// - builder: FlatBufferBuilder that will host incoming data
|
||||
/// - obj: Object of associatedtype to the current implementer
|
||||
static func pack(_ builder: inout FlatBufferBuilder, obj: inout T?) -> Offset
|
||||
|
||||
/// `pack` packs the variables of a native Object into the `ByteBuffer` by using
|
||||
/// the FlatBufferBuilder
|
||||
/// - Parameters:
|
||||
/// - builder: FlatBufferBuilder that will host incoming data
|
||||
/// - obj: Object of associatedtype to the current implementer
|
||||
static func pack(_ builder: inout FlatBufferBuilder, obj: inout T) -> Offset
|
||||
|
||||
/// `Unpack` unpacks a flatbuffers object into a `NativeObject`
|
||||
mutating func unpack() -> T
|
||||
}
|
||||
|
||||
public protocol Enum {
|
||||
associatedtype T: Scalar
|
||||
static var byteSize: Int { get }
|
||||
var value: T { get }
|
||||
}
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
public final class FlatBuffersUtils {
|
||||
/// FlatBuffersUtils hosts some utility functions that might be useful
|
||||
public enum FlatBuffersUtils {
|
||||
|
||||
/// Gets the size of the prefix
|
||||
/// - Parameter bb: Flatbuffer object
|
||||
@@ -24,7 +25,9 @@ public final class FlatBuffersUtils {
|
||||
bb.read(def: Int32.self, position: bb.reader)
|
||||
}
|
||||
|
||||
/// Removes the prefix by duplicating the Flatbuffer
|
||||
/// Removes the prefix by duplicating the Flatbuffer this call is expensive since its
|
||||
/// creates a new buffer use `readPrefixedSizeCheckedRoot` instead
|
||||
/// unless a completely new buffer is required
|
||||
/// - Parameter bb: Flatbuffer object
|
||||
public static func removeSizePrefix(bb: ByteBuffer) -> ByteBuffer {
|
||||
bb.duplicate(removing: MemoryLayout<Int32>.size)
|
||||
|
||||
59
swift/Sources/FlatBuffers/FlatbuffersErrors.swift
Normal file
59
swift/Sources/FlatBuffers/FlatbuffersErrors.swift
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2021 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.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Collection of thrown from the Flatbuffer verifier
|
||||
public enum FlatbuffersErrors: Error, Equatable {
|
||||
|
||||
/// Thrown when buffer is bigger than the allowed 2GiB
|
||||
case exceedsMaxSizeAllowed
|
||||
/// Thrown when there is an missaligned pointer at position
|
||||
/// of type
|
||||
case missAlignedPointer(position: Int, type: String)
|
||||
/// Thrown when trying to read a value that goes out of the
|
||||
/// current buffer bounds
|
||||
case outOfBounds(position: UInt, end: Int)
|
||||
/// Thrown when the signed offset is out of the bounds of the
|
||||
/// current buffer
|
||||
case signedOffsetOutOfBounds(offset: Int, position: Int)
|
||||
/// Thrown when a required field doesnt exist within the buffer
|
||||
case requiredFieldDoesntExist(position: VOffset, name: String)
|
||||
/// Thrown when a string is missing its NULL Terminator `\0`,
|
||||
/// this can be disabled in the `VerifierOptions`
|
||||
case missingNullTerminator(position: Int, str: String?)
|
||||
/// Thrown when the verifier has reached the maximum tables allowed,
|
||||
/// this can be disabled in the `VerifierOptions`
|
||||
case maximumTables
|
||||
/// Thrown when the verifier has reached the maximum depth allowed,
|
||||
/// this can be disabled in the `VerifierOptions`
|
||||
case maximumDepth
|
||||
/// Thrown when the verifier is presented with an unknown union case
|
||||
case unknownUnionCase
|
||||
/// thrown when a value for a union is not found within the buffer
|
||||
case valueNotFound(key: Int?, keyName: String, field: Int?, fieldName: String)
|
||||
/// thrown when the size of the keys vector doesnt match fields vector
|
||||
case unionVectorSize(
|
||||
keyVectorSize: Int,
|
||||
fieldVectorSize: Int,
|
||||
unionKeyName: String,
|
||||
fieldName: String)
|
||||
case apparentSizeTooLarge
|
||||
|
||||
public static func == (lhs: FlatbuffersErrors, rhs: FlatbuffersErrors) -> Bool {
|
||||
lhs.localizedDescription == rhs.localizedDescription
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
/// FlatBufferGRPCMessage protocol that should allow us to invoke
|
||||
/// initializers directly from the GRPC generated code
|
||||
public protocol FlatBufferGRPCMessage {
|
||||
|
||||
/// Raw pointer which would be pointing to the beginning of the readable bytes
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
/// NativeObject is a protocol that all of the `Object-API` generated code should be
|
||||
/// conforming to since it allows developers the ease of use to pack and unpack their
|
||||
/// Flatbuffers objects
|
||||
public protocol NativeObject {}
|
||||
|
||||
extension NativeObject {
|
||||
@@ -36,7 +39,10 @@ extension NativeObject {
|
||||
/// - Returns: returns the encoded sized ByteBuffer
|
||||
/// - Note: The `serialize(builder:type)` can be considered as a function that allows you to create smaller builder instead of the default `1024`.
|
||||
/// It can be considered less expensive in terms of memory allocation
|
||||
public func serialize<T: ObjectAPIPacker>(builder: inout FlatBufferBuilder, type: T.Type) -> ByteBuffer where T.T == Self {
|
||||
public func serialize<T: ObjectAPIPacker>(
|
||||
builder: inout FlatBufferBuilder,
|
||||
type: T.Type) -> ByteBuffer where T.T == Self
|
||||
{
|
||||
var s = self
|
||||
let root = type.pack(&builder, obj: &s)
|
||||
builder.finish(offset: root)
|
||||
|
||||
68
swift/Sources/FlatBuffers/Root.swift
Normal file
68
swift/Sources/FlatBuffers/Root.swift
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2021 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.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Takes in a prefixed sized buffer, where the prefixed size would be skipped.
|
||||
/// And would verify that the buffer passed is a valid `Flatbuffers` Object.
|
||||
/// - Parameters:
|
||||
/// - byteBuffer: Buffer that needs to be checked and read
|
||||
/// - options: Verifier options
|
||||
/// - Throws: FlatbuffersErrors
|
||||
/// - Returns: Returns a valid, checked Flatbuffers object
|
||||
public func getPrefixedSizeCheckedRoot<T: FlatBufferObject & Verifiable>(
|
||||
byteBuffer: inout ByteBuffer,
|
||||
options: VerifierOptions = .init()) throws -> T
|
||||
{
|
||||
byteBuffer.skipPrefix()
|
||||
return try getCheckedRoot(byteBuffer: &byteBuffer, options: options)
|
||||
}
|
||||
|
||||
/// Takes in a prefixed sized buffer, where the prefixed size would be skipped.
|
||||
/// Returns a `NON-Checked` flatbuffers object
|
||||
/// - Parameter byteBuffer: Buffer that contains data
|
||||
/// - Returns: Returns a Flatbuffers object
|
||||
public func getPrefixedSizeRoot<T: FlatBufferObject>(byteBuffer: inout ByteBuffer) -> T {
|
||||
byteBuffer.skipPrefix()
|
||||
return getRoot(byteBuffer: &byteBuffer)
|
||||
|
||||
}
|
||||
|
||||
/// Verifies that the buffer passed is a valid `Flatbuffers` Object.
|
||||
/// - Parameters:
|
||||
/// - byteBuffer: Buffer that needs to be checked and read
|
||||
/// - options: Verifier options
|
||||
/// - Throws: FlatbuffersErrors
|
||||
/// - Returns: Returns a valid, checked Flatbuffers object
|
||||
public func getCheckedRoot<T: FlatBufferObject & Verifiable>(
|
||||
byteBuffer: inout ByteBuffer,
|
||||
options: VerifierOptions = .init()) throws -> T
|
||||
{
|
||||
var verifier = try Verifier(buffer: &byteBuffer, options: options)
|
||||
try ForwardOffset<T>.verify(&verifier, at: 0, of: T.self)
|
||||
return T.init(
|
||||
byteBuffer,
|
||||
o: Int32(byteBuffer.read(def: UOffset.self, position: byteBuffer.reader)) + Int32(byteBuffer.reader))
|
||||
}
|
||||
|
||||
/// Returns a `NON-Checked` flatbuffers object
|
||||
/// - Parameter byteBuffer: Buffer that contains data
|
||||
/// - Returns: Returns a Flatbuffers object
|
||||
public func getRoot<T: FlatBufferObject>(byteBuffer: inout ByteBuffer) -> T {
|
||||
T.init(
|
||||
byteBuffer,
|
||||
o: Int32(byteBuffer.read(def: UOffset.self, position: byteBuffer.reader)) + Int32(byteBuffer.reader))
|
||||
}
|
||||
@@ -16,6 +16,42 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
extension String: Verifiable {
|
||||
|
||||
/// Verifies that the current value is which the bounds of the buffer, and if
|
||||
/// the current `Value` is aligned properly
|
||||
/// - Parameters:
|
||||
/// - verifier: Verifier that hosts the buffer
|
||||
/// - position: Current position within the buffer
|
||||
/// - type: The type of the object to be verified
|
||||
/// - Throws: Errors coming from `inBuffer`, `missingNullTerminator` and `outOfBounds`
|
||||
public static func verify<T>(
|
||||
_ verifier: inout Verifier,
|
||||
at position: Int,
|
||||
of type: T.Type) throws where T: Verifiable
|
||||
{
|
||||
|
||||
let range = try String.verifyRange(&verifier, at: position, of: UInt8.self)
|
||||
/// Safe &+ since we already check for overflow in verify range
|
||||
let stringLen = range.start &+ range.count
|
||||
|
||||
if stringLen >= verifier.capacity {
|
||||
throw FlatbuffersErrors.outOfBounds(
|
||||
position: UInt(clamping: stringLen.magnitude),
|
||||
end: verifier.capacity)
|
||||
}
|
||||
|
||||
let isNullTerminated = verifier._buffer.read(
|
||||
def: UInt8.self,
|
||||
position: stringLen) == 0
|
||||
|
||||
if !verifier._options._ignoreMissingNullTerminators && !isNullTerminated {
|
||||
let str = verifier._buffer.readString(at: range.start, count: range.count)
|
||||
throw FlatbuffersErrors.missingNullTerminator(position: position, str: str)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension String: FlatbuffersInitializable {
|
||||
|
||||
/// Initailizes a string from a Flatbuffers ByteBuffer
|
||||
@@ -23,10 +59,11 @@ extension String: FlatbuffersInitializable {
|
||||
/// - bb: ByteBuffer containing the readable string
|
||||
/// - o: Current position
|
||||
public init(_ bb: ByteBuffer, o: Int32) {
|
||||
let count = bb.read(def: Int32.self, position: Int(o))
|
||||
let v = Int(o)
|
||||
let count = bb.read(def: Int32.self, position: v)
|
||||
self = bb.readString(
|
||||
at: Int32(MemoryLayout<Int32>.size) + o,
|
||||
count: count) ?? ""
|
||||
at: MemoryLayout<Int32>.size + v,
|
||||
count: Int(count)) ?? ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +90,10 @@ extension String: NativeObject {
|
||||
fatalError("serialize should never be called from string directly")
|
||||
}
|
||||
|
||||
public func serialize<T: ObjectAPIPacker>(builder: inout FlatBufferBuilder, type: T.Type) -> ByteBuffer where T.T == Self {
|
||||
public func serialize<T: ObjectAPIPacker>(
|
||||
builder: inout FlatBufferBuilder,
|
||||
type: T.Type) -> ByteBuffer where T.T == Self
|
||||
{
|
||||
fatalError("serialize should never be called from string directly")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,16 +16,30 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Struct is a representation of a mutable `Flatbuffers` struct
|
||||
/// since native structs are value types and cant be mutated
|
||||
@frozen
|
||||
public struct Struct {
|
||||
|
||||
/// Hosting Bytebuffer
|
||||
public private(set) var bb: ByteBuffer
|
||||
/// Current position of the struct
|
||||
public private(set) var postion: Int32
|
||||
|
||||
/// Initializer for a mutable flatbuffers struct
|
||||
/// - Parameters:
|
||||
/// - bb: Current hosting Bytebuffer
|
||||
/// - position: Current position for the struct in the ByteBuffer
|
||||
public init(bb: ByteBuffer, position: Int32 = 0) {
|
||||
self.bb = bb
|
||||
postion = position
|
||||
}
|
||||
|
||||
/// Reads data from the buffer directly at offset O
|
||||
/// - Parameters:
|
||||
/// - type: Type of data to be read
|
||||
/// - o: Current offset of the data
|
||||
/// - Returns: Data of Type T that conforms to type Scalar
|
||||
public func readBuffer<T: Scalar>(of type: T.Type, at o: Int32) -> T {
|
||||
let r = bb.read(def: T.self, position: Int(o + postion))
|
||||
return r
|
||||
|
||||
@@ -16,11 +16,22 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
/// `Table` is a Flatbuffers object that can read,
|
||||
/// mutate scalar fields within a valid flatbuffers buffer
|
||||
@frozen
|
||||
public struct Table {
|
||||
|
||||
/// Hosting Bytebuffer
|
||||
public private(set) var bb: ByteBuffer
|
||||
/// Current position of the table within the buffer
|
||||
public private(set) var postion: Int32
|
||||
|
||||
/// Initializer for the table interface to allow generated code to read
|
||||
/// data from memory
|
||||
/// - Parameters:
|
||||
/// - bb: ByteBuffer that stores data
|
||||
/// - position: Current table position
|
||||
/// - Note: This will `CRASH` if read on a big endian machine
|
||||
public init(bb: ByteBuffer, position: Int32 = 0) {
|
||||
guard isLitteEndian else {
|
||||
fatalError("Reading/Writing a buffer in big endian machine is not supported on swift")
|
||||
@@ -29,6 +40,10 @@ public struct Table {
|
||||
postion = position
|
||||
}
|
||||
|
||||
/// Gets the offset of the current field within the buffer by reading
|
||||
/// the vtable
|
||||
/// - Parameter o: current offset
|
||||
/// - Returns: offset of field within buffer
|
||||
public func offset(_ o: Int32) -> Int32 {
|
||||
let vtable = postion - bb.read(def: Int32.self, position: Int(postion))
|
||||
return o < bb.read(def: VOffset.self, position: Int(vtable)) ? Int32(bb.read(
|
||||
@@ -36,7 +51,13 @@ public struct Table {
|
||||
position: Int(vtable + o))) : 0
|
||||
}
|
||||
|
||||
public func indirect(_ o: Int32) -> Int32 { o + bb.read(def: Int32.self, position: Int(o)) }
|
||||
/// Gets the indirect offset of the current stored object
|
||||
/// (applicable only for object arrays)
|
||||
/// - Parameter o: current offset
|
||||
/// - Returns: offset of field within buffer
|
||||
public func indirect(_ o: Int32) -> Int32 {
|
||||
o + bb.read(def: Int32.self, position: Int(o))
|
||||
}
|
||||
|
||||
/// String reads from the buffer with respect to position of the current table.
|
||||
/// - Parameter offset: Offset of the string
|
||||
@@ -45,14 +66,15 @@ public struct Table {
|
||||
}
|
||||
|
||||
/// Direct string reads from the buffer disregarding the position of the table.
|
||||
/// It would be preferable to use string unless the current position of the table is not needed
|
||||
/// It would be preferable to use string unless the current position of the table
|
||||
/// is not needed
|
||||
/// - Parameter offset: Offset of the string
|
||||
public func directString(at offset: Int32) -> String? {
|
||||
var offset = offset
|
||||
offset += bb.read(def: Int32.self, position: Int(offset))
|
||||
let count = bb.read(def: Int32.self, position: Int(offset))
|
||||
let position = offset + Int32(MemoryLayout<Int32>.size)
|
||||
return bb.readString(at: position, count: count)
|
||||
let position = Int(offset) + MemoryLayout<Int32>.size
|
||||
return bb.readString(at: position, count: Int(count))
|
||||
}
|
||||
|
||||
/// Reads from the buffer with respect to the position in the table.
|
||||
@@ -81,19 +103,30 @@ public struct Table {
|
||||
return r
|
||||
}
|
||||
|
||||
/// Returns that current `Union` object at a specific offset
|
||||
/// by adding offset to the current position of table
|
||||
/// - Parameter o: offset
|
||||
/// - Returns: A flatbuffers object
|
||||
public func union<T: FlatbuffersInitializable>(_ o: Int32) -> T {
|
||||
let o = o + postion
|
||||
return directUnion(o)
|
||||
}
|
||||
|
||||
/// Returns a direct `Union` object at a specific offset
|
||||
/// - Parameter o: offset
|
||||
/// - Returns: A flatbuffers object
|
||||
public func directUnion<T: FlatbuffersInitializable>(_ o: Int32) -> T {
|
||||
T.init(bb, o: o + bb.read(def: Int32.self, position: Int(o)))
|
||||
}
|
||||
|
||||
/// Returns a vector of type T at a specific offset
|
||||
/// This should only be used by `Scalars`
|
||||
/// - Parameter off: Readable offset
|
||||
/// - Returns: Returns a vector of type [T]
|
||||
public func getVector<T>(at off: Int32) -> [T]? {
|
||||
let o = offset(off)
|
||||
guard o != 0 else { return nil }
|
||||
return bb.readSlice(index: vector(at: o), count: vector(count: o))
|
||||
return bb.readSlice(index: Int(vector(at: o)), count: Int(vector(count: o)))
|
||||
}
|
||||
|
||||
/// Vector count gets the count of Elements within the array
|
||||
@@ -115,17 +148,36 @@ public struct Table {
|
||||
return o + bb.read(def: Int32.self, position: Int(o)) + 4
|
||||
}
|
||||
|
||||
static public func indirect(_ o: Int32, _ fbb: ByteBuffer) -> Int32 { o + fbb.read(
|
||||
def: Int32.self,
|
||||
position: Int(o)) }
|
||||
/// Reading an indirect offset of a table.
|
||||
/// - Parameters:
|
||||
/// - o: position within the buffer
|
||||
/// - fbb: ByteBuffer
|
||||
/// - Returns: table offset
|
||||
static public func indirect(_ o: Int32, _ fbb: ByteBuffer) -> Int32 {
|
||||
o + fbb.read(def: Int32.self, position: Int(o))
|
||||
}
|
||||
|
||||
/// Gets a vtable value according to an table Offset and a field offset
|
||||
/// - Parameters:
|
||||
/// - o: offset relative to entire buffer
|
||||
/// - vOffset: Field offset within a vtable
|
||||
/// - fbb: ByteBuffer
|
||||
/// - Returns: an position of a field
|
||||
static public func offset(_ o: Int32, vOffset: Int32, fbb: ByteBuffer) -> Int32 {
|
||||
let vTable = Int32(fbb.capacity) - o
|
||||
return vTable + Int32(fbb.read(
|
||||
def: Int16.self,
|
||||
position: Int(vTable + vOffset - fbb.read(def: Int32.self, position: Int(vTable)))))
|
||||
position: Int(vTable + vOffset - fbb.read(
|
||||
def: Int32.self,
|
||||
position: Int(vTable)))))
|
||||
}
|
||||
|
||||
/// Compares two objects at offset A and offset B within a ByteBuffer
|
||||
/// - Parameters:
|
||||
/// - off1: first offset to compare
|
||||
/// - off2: second offset to compare
|
||||
/// - fbb: Bytebuffer
|
||||
/// - Returns: returns the difference between
|
||||
static public func compare(_ off1: Int32, _ off2: Int32, fbb: ByteBuffer) -> Int32 {
|
||||
let memorySize = Int32(MemoryLayout<Int32>.size)
|
||||
let _off1 = off1 + fbb.read(def: Int32.self, position: Int(off1))
|
||||
@@ -145,6 +197,12 @@ public struct Table {
|
||||
return len1 - len2
|
||||
}
|
||||
|
||||
/// Compares two objects at offset A and array of `Bytes` within a ByteBuffer
|
||||
/// - Parameters:
|
||||
/// - off1: Offset to compare to
|
||||
/// - key: bytes array to compare to
|
||||
/// - fbb: Bytebuffer
|
||||
/// - Returns: returns the difference between
|
||||
static public func compare(_ off1: Int32, _ key: [Byte], fbb: ByteBuffer) -> Int32 {
|
||||
let memorySize = Int32(MemoryLayout<Int32>.size)
|
||||
let _off1 = off1 + fbb.read(def: Int32.self, position: Int(off1))
|
||||
|
||||
202
swift/Sources/FlatBuffers/TableVerifier.swift
Normal file
202
swift/Sources/FlatBuffers/TableVerifier.swift
Normal file
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
* Copyright 2021 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.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
/// `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()
|
||||
}
|
||||
}
|
||||
52
swift/Sources/FlatBuffers/VeriferOptions.swift
Normal file
52
swift/Sources/FlatBuffers/VeriferOptions.swift
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2021 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.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
/// `VerifierOptions` is a set of options to verify a flatbuffer
|
||||
public struct VerifierOptions {
|
||||
|
||||
/// Maximum `Apparent` size if the buffer can be expanded into a DAG tree
|
||||
internal var _maxApparentSize: UOffset
|
||||
|
||||
/// Maximum table count allowed in a buffer
|
||||
internal var _maxTableCount: UOffset
|
||||
|
||||
/// Maximum depth allowed in a buffer
|
||||
internal var _maxDepth: UOffset
|
||||
|
||||
/// Ignoring missing null terminals in strings
|
||||
internal var _ignoreMissingNullTerminators: Bool
|
||||
|
||||
/// initializes the set of options for the verifier
|
||||
/// - Parameters:
|
||||
/// - maxDepth: Maximum depth allowed in a buffer
|
||||
/// - maxTableCount: Maximum table count allowed in a buffer
|
||||
/// - maxApparentSize: Maximum `Apparent` size if the buffer can be expanded into a DAG tree
|
||||
/// - ignoreMissingNullTerminators: Ignoring missing null terminals in strings *Currently not supported in swift*
|
||||
public init(
|
||||
maxDepth: UOffset = 64,
|
||||
maxTableCount: UOffset = 1000000,
|
||||
maxApparentSize: UOffset = 1 << 31,
|
||||
ignoreMissingNullTerminators: Bool = false)
|
||||
{
|
||||
_maxDepth = maxDepth
|
||||
_maxTableCount = maxTableCount
|
||||
_maxApparentSize = maxApparentSize
|
||||
_ignoreMissingNullTerminators = ignoreMissingNullTerminators
|
||||
}
|
||||
|
||||
}
|
||||
211
swift/Sources/FlatBuffers/Verifiable.swift
Normal file
211
swift/Sources/FlatBuffers/Verifiable.swift
Normal file
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
* Copyright 2021 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.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Verifiable is a protocol all swift flatbuffers object should conform to,
|
||||
/// since swift is similar to `cpp` and `rust` where the data is read directly
|
||||
/// from `unsafeMemory` thus the need to verify if the buffer received is a valid one
|
||||
public protocol Verifiable {
|
||||
|
||||
/// Verifies that the current value is which the bounds of the buffer, and if
|
||||
/// the current `Value` is aligned properly
|
||||
/// - Parameters:
|
||||
/// - verifier: Verifier that hosts the buffer
|
||||
/// - position: Current position within the buffer
|
||||
/// - type: The type of the object to be verified
|
||||
/// - Throws: Errors coming from `inBuffer` function
|
||||
static func verify<T>(
|
||||
_ verifier: inout Verifier,
|
||||
at position: Int,
|
||||
of type: T.Type) throws where T: Verifiable
|
||||
}
|
||||
|
||||
extension Verifiable {
|
||||
|
||||
/// Verifies if the current range to be read is within the bounds of the buffer,
|
||||
/// and if the range is properly aligned
|
||||
/// - Parameters:
|
||||
/// - verifier: Verifier that hosts the buffer
|
||||
/// - position: Current position within the buffer
|
||||
/// - type: The type of the object to be verified
|
||||
/// - Throws: Erros thrown from `isAligned` & `rangeInBuffer`
|
||||
/// - Returns: a tuple of the start position and the count of objects within the range
|
||||
@discardableResult
|
||||
public static func verifyRange<T>(
|
||||
_ verifier: inout Verifier,
|
||||
at position: Int, of type: T.Type) throws -> (start: Int, count: Int)
|
||||
{
|
||||
let len: UOffset = try verifier.getValue(at: position)
|
||||
let intLen = Int(len)
|
||||
let start = Int(clamping: (position &+ MemoryLayout<Int32>.size).magnitude)
|
||||
try verifier.isAligned(position: start, type: type.self)
|
||||
try verifier.rangeInBuffer(position: start, size: intLen)
|
||||
return (start, intLen)
|
||||
}
|
||||
}
|
||||
|
||||
extension Verifiable where Self: Scalar {
|
||||
|
||||
/// Verifies that the current value is which the bounds of the buffer, and if
|
||||
/// the current `Value` is aligned properly
|
||||
/// - Parameters:
|
||||
/// - verifier: Verifier that hosts the buffer
|
||||
/// - position: Current position within the buffer
|
||||
/// - type: The type of the object to be verified
|
||||
/// - Throws: Errors coming from `inBuffer` function
|
||||
public static func verify<T>(
|
||||
_ verifier: inout Verifier,
|
||||
at position: Int,
|
||||
of type: T.Type) throws where T: Verifiable
|
||||
{
|
||||
try verifier.inBuffer(position: position, of: type.self)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - ForwardOffset
|
||||
|
||||
/// ForwardOffset is a container to wrap around the Generic type to be verified
|
||||
/// from the flatbuffers object.
|
||||
public enum ForwardOffset<U>: Verifiable where U: Verifiable {
|
||||
|
||||
/// Verifies that the current value is which the bounds of the buffer, and if
|
||||
/// the current `Value` is aligned properly
|
||||
/// - Parameters:
|
||||
/// - verifier: Verifier that hosts the buffer
|
||||
/// - position: Current position within the buffer
|
||||
/// - type: The type of the object to be verified
|
||||
/// - Throws: Errors coming from `inBuffer` function
|
||||
public static func verify<T>(
|
||||
_ verifier: inout Verifier,
|
||||
at position: Int,
|
||||
of type: T.Type) throws where T: Verifiable
|
||||
{
|
||||
let offset: UOffset = try verifier.getValue(at: position)
|
||||
let nextOffset = Int(clamping: (Int(offset) &+ position).magnitude)
|
||||
try U.verify(&verifier, at: nextOffset, of: U.self)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Vector
|
||||
|
||||
/// Vector is a container to wrap around the Generic type to be verified
|
||||
/// from the flatbuffers object.
|
||||
public enum Vector<U, S>: Verifiable where U: Verifiable, S: Verifiable {
|
||||
|
||||
/// Verifies that the current value is which the bounds of the buffer, and if
|
||||
/// the current `Value` is aligned properly
|
||||
/// - Parameters:
|
||||
/// - verifier: Verifier that hosts the buffer
|
||||
/// - position: Current position within the buffer
|
||||
/// - type: The type of the object to be verified
|
||||
/// - Throws: Errors coming from `inBuffer` function
|
||||
public static func verify<T>(
|
||||
_ verifier: inout Verifier,
|
||||
at position: Int,
|
||||
of type: T.Type) throws where T: Verifiable
|
||||
{
|
||||
/// checks if the next verification type S is equal to U of type forwardOffset
|
||||
/// This had to be done since I couldnt find a solution for duplicate call functions
|
||||
/// A fix will be appreciated
|
||||
if U.self is ForwardOffset<S>.Type {
|
||||
let range = try verifyRange(&verifier, at: position, of: UOffset.self)
|
||||
for index in stride(
|
||||
from: range.start,
|
||||
to: Int(clamping: range.start &+ range.count),
|
||||
by: MemoryLayout<UOffset>.size)
|
||||
{
|
||||
try U.verify(&verifier, at: index, of: U.self)
|
||||
}
|
||||
} else {
|
||||
try S.verifyRange(&verifier, at: position, of: S.self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UnionVector
|
||||
|
||||
/// UnionVector is a container to wrap around the Generic type to be verified
|
||||
/// from the flatbuffers object.
|
||||
public enum UnionVector<S> where S: UnionEnum {
|
||||
|
||||
/// Completion handler for the function Verify, that passes the verifier
|
||||
/// enum type and position of union field
|
||||
public typealias Completion = (inout Verifier, S, Int) throws -> Void
|
||||
|
||||
/// Verifies if the current range to be read is within the bounds of the buffer,
|
||||
/// and if the range is properly aligned. It also verifies if the union type is a
|
||||
/// *valid/supported* union type.
|
||||
/// - Parameters:
|
||||
/// - verifier: Verifier that hosts the buffer
|
||||
/// - keyPosition: Current union key position within the buffer
|
||||
/// - fieldPosition: Current union field position within the buffer
|
||||
/// - unionKeyName: Name of key to written if error is presented
|
||||
/// - fieldName: Name of field to written if error is presented
|
||||
/// - completion: Completion is a handler that WILL be called in the generated
|
||||
/// code to verify the actual objects
|
||||
/// - Throws: FlatbuffersErrors
|
||||
public static func verify(
|
||||
_ verifier: inout Verifier,
|
||||
keyPosition: Int,
|
||||
fieldPosition: Int,
|
||||
unionKeyName: String,
|
||||
fieldName: String,
|
||||
completion: @escaping Completion) throws
|
||||
{
|
||||
/// Get offset for union key vectors and offset vectors
|
||||
let keyOffset: UOffset = try verifier.getValue(at: keyPosition)
|
||||
let fieldOffset: UOffset = try verifier.getValue(at: fieldPosition)
|
||||
|
||||
/// Check if values are within the buffer, returns the start position of vectors, and vector counts
|
||||
/// Using &+ is safe since we already verified that the value is within the buffer, where the max is
|
||||
/// going to be 2Gib and swift supports Int64 by default
|
||||
let keysRange = try S.T.verifyRange(
|
||||
&verifier,
|
||||
at: Int(keyOffset) &+ keyPosition,
|
||||
of: S.T.self)
|
||||
let offsetsRange = try UOffset.verifyRange(
|
||||
&verifier,
|
||||
at: Int(fieldOffset) &+ fieldPosition,
|
||||
of: UOffset.self)
|
||||
|
||||
guard keysRange.count == offsetsRange.count else {
|
||||
throw FlatbuffersErrors.unionVectorSize(
|
||||
keyVectorSize: keysRange.count,
|
||||
fieldVectorSize: offsetsRange.count,
|
||||
unionKeyName: unionKeyName,
|
||||
fieldName: fieldName)
|
||||
}
|
||||
|
||||
var count = 0
|
||||
/// Iterate over the vector of keys and offsets.
|
||||
while count < keysRange.count {
|
||||
|
||||
/// index of readable enum value in array
|
||||
let keysIndex = MemoryLayout<S.T>.size * count
|
||||
guard let _enum = try S.init(value: verifier._buffer.read(
|
||||
def: S.T.self,
|
||||
position: keysRange.start + keysIndex)) else
|
||||
{
|
||||
throw FlatbuffersErrors.unknownUnionCase
|
||||
}
|
||||
/// index of readable offset value in array
|
||||
let fieldIndex = MemoryLayout<UOffset>.size * count
|
||||
try completion(&verifier, _enum, offsetsRange.start + fieldIndex)
|
||||
count += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
199
swift/Sources/FlatBuffers/Verifier.swift
Normal file
199
swift/Sources/FlatBuffers/Verifier.swift
Normal file
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* Copyright 2021 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.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Verifier that check if the buffer passed into it is a valid,
|
||||
/// safe, aligned Flatbuffers object since swift read from `unsafeMemory`
|
||||
public struct Verifier {
|
||||
|
||||
/// Flag to check for alignment if true
|
||||
fileprivate let _checkAlignment: Bool
|
||||
/// Capacity of the current buffer
|
||||
fileprivate var _capacity: Int
|
||||
/// Current ApparentSize
|
||||
fileprivate var _apparentSize: UOffset = 0
|
||||
/// Amount of tables present within a buffer
|
||||
fileprivate var _tableCount = 0
|
||||
|
||||
/// Capacity of the buffer
|
||||
internal var capacity: Int { _capacity }
|
||||
/// Current reached depth within the buffer
|
||||
internal var _depth = 0
|
||||
/// Current verifiable ByteBuffer
|
||||
internal var _buffer: ByteBuffer
|
||||
/// Options for verification
|
||||
internal let _options: VerifierOptions
|
||||
|
||||
/// Initializer for the verifier
|
||||
/// - Parameters:
|
||||
/// - buffer: Bytebuffer that is required to be verified
|
||||
/// - options: `VerifierOptions` that set the rule for some of the verification done
|
||||
/// - checkAlignment: If alignment check is required to be preformed
|
||||
/// - Throws: `exceedsMaxSizeAllowed` if capacity of the buffer is more than 2GiB
|
||||
public init(
|
||||
buffer: inout ByteBuffer,
|
||||
options: VerifierOptions = .init(),
|
||||
checkAlignment: Bool = true) throws
|
||||
{
|
||||
guard buffer.capacity < FlatBufferMaxSize else {
|
||||
throw FlatbuffersErrors.exceedsMaxSizeAllowed
|
||||
}
|
||||
|
||||
_buffer = buffer
|
||||
_capacity = buffer.capacity
|
||||
_checkAlignment = checkAlignment
|
||||
_options = options
|
||||
}
|
||||
|
||||
/// Resets the verifier to initial state
|
||||
public mutating func reset() {
|
||||
_depth = 0
|
||||
_tableCount = 0
|
||||
}
|
||||
|
||||
/// Checks if the value of type `T` is aligned properly in the buffer
|
||||
/// - Parameters:
|
||||
/// - position: Current position
|
||||
/// - type: Type of value to check
|
||||
/// - Throws: `missAlignedPointer` if the pointer is not aligned properly
|
||||
public mutating func isAligned<T>(position: Int, type: T.Type) throws {
|
||||
|
||||
/// If check alignment is false this mutating function doesnt continue
|
||||
if !_checkAlignment { return }
|
||||
|
||||
/// advance pointer to position X
|
||||
let ptr = _buffer._storage.memory.advanced(by: position)
|
||||
/// Check if the pointer is aligned
|
||||
if Int(bitPattern: ptr) & (MemoryLayout<T>.alignment &- 1) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
throw FlatbuffersErrors.missAlignedPointer(
|
||||
position: position,
|
||||
type: String(describing: T.self))
|
||||
}
|
||||
|
||||
/// Checks if the value of Size "X" is within the range of the buffer
|
||||
/// - Parameters:
|
||||
/// - position: Current postion to be read
|
||||
/// - size: `Byte` Size of readable object within the buffer
|
||||
/// - Throws: `outOfBounds` if the value is out of the bounds of the buffer
|
||||
/// and `apparentSizeTooLarge` if the apparent size is bigger than the one specified
|
||||
/// in `VerifierOptions`
|
||||
public mutating func rangeInBuffer(position: Int, size: Int) throws {
|
||||
let end = UInt(clamping: (position &+ size).magnitude)
|
||||
if end > _buffer.capacity {
|
||||
throw FlatbuffersErrors.outOfBounds(position: end, end: capacity)
|
||||
}
|
||||
_apparentSize = _apparentSize &+ UInt32(size)
|
||||
if _apparentSize > _options._maxApparentSize {
|
||||
throw FlatbuffersErrors.apparentSizeTooLarge
|
||||
}
|
||||
}
|
||||
|
||||
/// Validates if a value of type `T` is aligned and within the bounds of
|
||||
/// the buffer
|
||||
/// - Parameters:
|
||||
/// - position: Current readable position
|
||||
/// - type: Type of value to check
|
||||
/// - Throws: FlatbuffersErrors
|
||||
public mutating func inBuffer<T>(position: Int, of type: T.Type) throws {
|
||||
try isAligned(position: position, type: type)
|
||||
try rangeInBuffer(position: position, size: MemoryLayout<T>.size)
|
||||
}
|
||||
|
||||
/// Visits a table at the current position and validates if the table meets
|
||||
/// the rules specified in the `VerifierOptions`
|
||||
/// - Parameter position: Current position to be read
|
||||
/// - Throws: FlatbuffersErrors
|
||||
/// - Returns: A `TableVerifier` at the current readable table
|
||||
public mutating func visitTable(at position: Int) throws -> TableVerifier {
|
||||
let vtablePosition = try derefOffset(position: position)
|
||||
let vtableLength: VOffset = try getValue(at: vtablePosition)
|
||||
|
||||
let length = Int(vtableLength)
|
||||
try isAligned(
|
||||
position: Int(clamping: (vtablePosition + length).magnitude),
|
||||
type: VOffset.self)
|
||||
try rangeInBuffer(position: vtablePosition, size: length)
|
||||
|
||||
_tableCount += 1
|
||||
|
||||
if _tableCount > _options._maxTableCount {
|
||||
throw FlatbuffersErrors.maximumTables
|
||||
}
|
||||
|
||||
_depth += 1
|
||||
|
||||
if _depth > _options._maxDepth {
|
||||
throw FlatbuffersErrors.maximumDepth
|
||||
}
|
||||
|
||||
return TableVerifier(
|
||||
position: position,
|
||||
vtable: vtablePosition,
|
||||
vtableLength: length,
|
||||
verifier: &self)
|
||||
}
|
||||
|
||||
/// Validates if a value of type `T` is within the buffer and returns it
|
||||
/// - Parameter position: Current position to be read
|
||||
/// - Throws: `inBuffer` errors
|
||||
/// - Returns: a value of type `T` usually a `VTable` or a table offset
|
||||
internal mutating func getValue<T>(at position: Int) throws -> T {
|
||||
try inBuffer(position: position, of: T.self)
|
||||
return _buffer.read(def: T.self, position: position)
|
||||
}
|
||||
|
||||
/// derefrences an offset within a vtable to get the position of the field
|
||||
/// in the bytebuffer
|
||||
/// - Parameter position: Current readable position
|
||||
/// - Throws: `inBuffer` errors & `signedOffsetOutOfBounds`
|
||||
/// - Returns: Current readable position for a field
|
||||
@inline(__always)
|
||||
internal mutating func derefOffset(position: Int) throws -> Int {
|
||||
try inBuffer(position: position, of: Int32.self)
|
||||
|
||||
let offset = _buffer.read(def: Int32.self, position: position)
|
||||
// switching to int32 since swift's default Int is int64
|
||||
// this should be safe since we already checked if its within
|
||||
// the buffer
|
||||
let _int32Position = UInt32(position)
|
||||
|
||||
let reportedOverflow: (partialValue: UInt32, overflow: Bool)
|
||||
if offset > 0 {
|
||||
reportedOverflow = _int32Position.subtractingReportingOverflow(offset.magnitude)
|
||||
} else {
|
||||
reportedOverflow = _int32Position.addingReportingOverflow(offset.magnitude)
|
||||
}
|
||||
|
||||
/// since `subtractingReportingOverflow` & `addingReportingOverflow` returns true,
|
||||
/// if there is overflow we return failure
|
||||
if reportedOverflow.overflow || reportedOverflow.partialValue > _buffer.capacity {
|
||||
throw FlatbuffersErrors.signedOffsetOutOfBounds(
|
||||
offset: Int(offset),
|
||||
position: position)
|
||||
}
|
||||
|
||||
return Int(reportedOverflow.partialValue)
|
||||
}
|
||||
|
||||
/// finishes the current iteration of verification on an object
|
||||
internal mutating func finish() {
|
||||
_depth -= 1
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user