forked from BigfootDev/flatbuffers
[Swift] Flexbuffers native swift port (#8577)
* Offical Swift port for FlexBuffers This is the offical port for FlexBuffers within swift, and it introcudes a Common Module where code is shared between flatbuffers and flexbuffers. Writing most supported values like maps, vectors, nil and scalars into a flexbuffer buffer. And includes tests to verify that its similar to cpp * Reading a flexbuffer Implementing reading from a flexbuffer, enabling most of the buffers features, like most types, maps, vectors, typedvectors, and fixedtypedvectors. Currently, if an offset/object cant be read we default to a swift nil instead of the default flexbuffers 'null' with all values. * Fixes bazel breaking due to new project structure Address warnings within the library * Adds comment on why we added the code & properly enforce the amout of bytes needed
This commit is contained in:
@@ -20,17 +20,28 @@ import PackageDescription
|
||||
let package = Package(
|
||||
name: "FlatBuffers",
|
||||
platforms: [
|
||||
.iOS(.v11),
|
||||
.iOS(.v12),
|
||||
.macOS(.v10_14),
|
||||
],
|
||||
products: [
|
||||
.library(
|
||||
name: "FlatBuffers",
|
||||
targets: ["FlatBuffers"]),
|
||||
.library(
|
||||
name: "FlexBuffers",
|
||||
targets: ["FlexBuffers"]),
|
||||
],
|
||||
targets: [
|
||||
.target(
|
||||
name: "FlatBuffers",
|
||||
dependencies: ["Common"],
|
||||
path: "swift/Sources/FlatBuffers"),
|
||||
.target(
|
||||
name: "FlexBuffers",
|
||||
dependencies: ["Common"],
|
||||
path: "swift/Sources/FlexBuffers"),
|
||||
.target(
|
||||
name: "Common",
|
||||
dependencies: [],
|
||||
path: "swift/Sources"),
|
||||
path: "swift/Sources/Common"),
|
||||
])
|
||||
|
||||
@@ -20,7 +20,7 @@ import PackageDescription
|
||||
let package = Package(
|
||||
name: "Greeter",
|
||||
platforms: [
|
||||
.iOS(.v11),
|
||||
.iOS(.v12),
|
||||
.macOS(.v10_14),
|
||||
],
|
||||
dependencies: [
|
||||
|
||||
@@ -23,5 +23,6 @@
|
||||
--exclude **/*_generated.swift
|
||||
--exclude **/swift_code_*.swift
|
||||
--exclude **/*.grpc.swift
|
||||
--exclude **/Build/tests/**
|
||||
|
||||
--header "/*\n * Copyright 2024 Google Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */"
|
||||
@@ -1,8 +1,21 @@
|
||||
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
|
||||
|
||||
swift_library(
|
||||
name = "FlexBuffers",
|
||||
srcs = glob([
|
||||
"Sources/FlexBuffers/**/*.swift",
|
||||
"Sources/Common/*.swift",
|
||||
]),
|
||||
module_name = "FlexBuffers",
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
swift_library(
|
||||
name = "swift",
|
||||
srcs = glob(["Sources/FlatBuffers/*.swift"]),
|
||||
srcs = glob([
|
||||
"Sources/FlatBuffers/*.swift",
|
||||
"Sources/Common/*.swift",
|
||||
]),
|
||||
module_name = "FlatBuffers",
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
@@ -22,9 +22,10 @@ extension Int {
|
||||
///
|
||||
/// This is used since the UnsafeMutableRawPointer will face issues when writing/reading
|
||||
/// if the buffer alignment exceeds that actual size of the buffer
|
||||
var convertToPowerofTwo: Int {
|
||||
@inline(__always)
|
||||
public var convertToPowerofTwo: Int {
|
||||
guard self > 0 else { return 1 }
|
||||
var n = UOffset(self)
|
||||
var n = UInt32(self)
|
||||
|
||||
#if arch(arm) || arch(i386)
|
||||
let max = UInt32(Int.max)
|
||||
107
swift/Sources/Common/Scalar.swift
Normal file
107
swift/Sources/Common/Scalar.swift
Normal file
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright 2024 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
|
||||
|
||||
/// A boolean to see if the system is littleEndian
|
||||
public let isLitteEndian: Bool = {
|
||||
let number: UInt32 = 0x12345678
|
||||
return number == number.littleEndian
|
||||
}()
|
||||
|
||||
/// Constant for the file id length
|
||||
public let FileIdLength = 4
|
||||
|
||||
/// Protocol that All Scalars should conform to
|
||||
///
|
||||
/// 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: FixedWidthInteger {
|
||||
/// Converts the value from BigEndian to LittleEndian
|
||||
///
|
||||
/// Converts values to little endian on machines that work with BigEndian, however this is NOT TESTED yet.
|
||||
public var convertedEndian: NumericValue {
|
||||
self as! Self.NumericValue
|
||||
}
|
||||
}
|
||||
|
||||
extension Double: Scalar {
|
||||
public typealias NumericValue = UInt64
|
||||
|
||||
public var convertedEndian: UInt64 {
|
||||
bitPattern.littleEndian
|
||||
}
|
||||
}
|
||||
|
||||
extension Float32: Scalar {
|
||||
public typealias NumericValue = UInt32
|
||||
|
||||
public var convertedEndian: UInt32 {
|
||||
bitPattern.littleEndian
|
||||
}
|
||||
}
|
||||
|
||||
extension Bool: Scalar {
|
||||
public var convertedEndian: UInt8 {
|
||||
self == true ? 1 : 0
|
||||
}
|
||||
|
||||
public typealias NumericValue = UInt8
|
||||
}
|
||||
|
||||
extension Int: Scalar {
|
||||
public typealias NumericValue = Int
|
||||
}
|
||||
|
||||
extension Int8: Scalar {
|
||||
public typealias NumericValue = Int8
|
||||
}
|
||||
|
||||
extension Int16: Scalar {
|
||||
public typealias NumericValue = Int16
|
||||
}
|
||||
|
||||
extension Int32: Scalar {
|
||||
public typealias NumericValue = Int32
|
||||
}
|
||||
|
||||
extension Int64: Scalar {
|
||||
public typealias NumericValue = Int64
|
||||
}
|
||||
|
||||
extension UInt: Scalar {
|
||||
public typealias NumericValue = UInt
|
||||
}
|
||||
|
||||
extension UInt8: Scalar {
|
||||
public typealias NumericValue = UInt8
|
||||
}
|
||||
|
||||
extension UInt16: Scalar {
|
||||
public typealias NumericValue = UInt16
|
||||
}
|
||||
|
||||
extension UInt32: Scalar {
|
||||
public typealias NumericValue = UInt32
|
||||
}
|
||||
|
||||
extension UInt64: Scalar {
|
||||
public typealias NumericValue = UInt64
|
||||
}
|
||||
29
swift/Sources/Common/padding.swift
Normal file
29
swift/Sources/Common/padding.swift
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2024 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
|
||||
|
||||
/// Gets the padding for the current element
|
||||
/// - Parameters:
|
||||
/// - bufSize: Current size of the buffer + the offset of the object to be written
|
||||
/// - elementSize: Element size
|
||||
@inline(__always)
|
||||
public func padding(
|
||||
bufSize: UInt,
|
||||
elementSize: UInt) -> UInt
|
||||
{
|
||||
((~bufSize) &+ 1) & (elementSize &- 1)
|
||||
}
|
||||
@@ -293,7 +293,7 @@ public struct ByteBuffer {
|
||||
}
|
||||
assert(index < _storage.capacity, "Write index is out of writing bound")
|
||||
assert(index >= 0, "Writer index should be above zero")
|
||||
_ = withUnsafePointer(to: value) { ptr in
|
||||
withUnsafePointer(to: value) { ptr in
|
||||
_storage.withUnsafeRawPointer {
|
||||
memcpy(
|
||||
$0.advanced(by: index),
|
||||
|
||||
@@ -14,15 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#if canImport(Common)
|
||||
@_exported import Common
|
||||
#endif
|
||||
import Foundation
|
||||
|
||||
/// A boolean to see if the system is littleEndian
|
||||
let isLitteEndian: Bool = {
|
||||
let number: UInt32 = 0x12345678
|
||||
return number == number.littleEndian
|
||||
}()
|
||||
/// Constant for the file id length
|
||||
let FileIdLength = 4
|
||||
/// Type aliases
|
||||
public typealias Byte = UInt8
|
||||
public typealias UOffset = UInt32
|
||||
@@ -35,80 +31,31 @@ public let FlatBufferMaxSize = UInt32
|
||||
/// Protocol that All Scalars should conform to
|
||||
///
|
||||
/// 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 {}
|
||||
|
||||
extension Scalar where Self: FixedWidthInteger {
|
||||
/// Converts the value from BigEndian to LittleEndian
|
||||
///
|
||||
/// Converts values to little endian on machines that work with BigEndian, however this is NOT TESTED yet.
|
||||
public var convertedEndian: NumericValue {
|
||||
self as! Self.NumericValue
|
||||
}
|
||||
}
|
||||
extension Double: Verifiable {}
|
||||
|
||||
extension Double: Scalar, Verifiable {
|
||||
public typealias NumericValue = UInt64
|
||||
extension Float32: Verifiable {}
|
||||
|
||||
public var convertedEndian: UInt64 {
|
||||
bitPattern.littleEndian
|
||||
}
|
||||
}
|
||||
extension Bool: Verifiable {}
|
||||
|
||||
extension Float32: Scalar, Verifiable {
|
||||
public typealias NumericValue = UInt32
|
||||
extension Int: Verifiable {}
|
||||
|
||||
public var convertedEndian: UInt32 {
|
||||
bitPattern.littleEndian
|
||||
}
|
||||
}
|
||||
extension Int8: Verifiable {}
|
||||
|
||||
extension Bool: Scalar, Verifiable {
|
||||
public var convertedEndian: UInt8 {
|
||||
self == true ? 1 : 0
|
||||
}
|
||||
extension Int16: Verifiable {}
|
||||
|
||||
public typealias NumericValue = UInt8
|
||||
}
|
||||
extension Int32: Verifiable {}
|
||||
|
||||
extension Int: Scalar, Verifiable {
|
||||
public typealias NumericValue = Int
|
||||
}
|
||||
extension Int64: Verifiable {}
|
||||
|
||||
extension Int8: Scalar, Verifiable {
|
||||
public typealias NumericValue = Int8
|
||||
}
|
||||
extension UInt8: Verifiable {}
|
||||
|
||||
extension Int16: Scalar, Verifiable {
|
||||
public typealias NumericValue = Int16
|
||||
}
|
||||
extension UInt16: Verifiable {}
|
||||
|
||||
extension Int32: Scalar, Verifiable {
|
||||
public typealias NumericValue = Int32
|
||||
}
|
||||
extension UInt32: Verifiable {}
|
||||
|
||||
extension Int64: Scalar, Verifiable {
|
||||
public typealias NumericValue = Int64
|
||||
}
|
||||
|
||||
extension UInt8: Scalar, Verifiable {
|
||||
public typealias NumericValue = UInt8
|
||||
}
|
||||
|
||||
extension UInt16: Scalar, Verifiable {
|
||||
public typealias NumericValue = UInt16
|
||||
}
|
||||
|
||||
extension UInt32: Scalar, Verifiable {
|
||||
public typealias NumericValue = UInt32
|
||||
}
|
||||
|
||||
extension UInt64: Scalar, Verifiable {
|
||||
public typealias NumericValue = UInt64
|
||||
}
|
||||
extension UInt64: Verifiable {}
|
||||
|
||||
public func FlatBuffersVersion_25_2_10() {}
|
||||
|
||||
@@ -343,19 +343,6 @@ public struct FlatBufferBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the padding for the current element
|
||||
/// - Parameters:
|
||||
/// - bufSize: Current size of the buffer + the offset of the object to be written
|
||||
/// - elementSize: Element size
|
||||
@inline(__always)
|
||||
@usableFromInline
|
||||
mutating internal func padding(
|
||||
bufSize: UInt32,
|
||||
elementSize: UInt32) -> UInt32
|
||||
{
|
||||
((~bufSize) &+ 1) & (elementSize &- 1)
|
||||
}
|
||||
|
||||
/// Prealigns the buffer before writting a new object into the buffer
|
||||
/// - Parameters:
|
||||
/// - len:Length of the object
|
||||
@@ -364,11 +351,9 @@ public struct FlatBufferBuilder {
|
||||
@usableFromInline
|
||||
mutating internal func preAlign(len: Int, alignment: Int) {
|
||||
minAlignment(size: alignment)
|
||||
_bb.fill(
|
||||
padding: Int(
|
||||
padding(
|
||||
bufSize: _bb.size &+ UOffset(len),
|
||||
elementSize: UOffset(alignment))))
|
||||
_bb.fill(padding: numericCast(padding(
|
||||
bufSize: numericCast(_bb.size) &+ numericCast(len),
|
||||
elementSize: numericCast(alignment))))
|
||||
}
|
||||
|
||||
/// Prealigns the buffer before writting a new object into the buffer
|
||||
|
||||
@@ -253,7 +253,7 @@ struct _InternalByteBuffer {
|
||||
}
|
||||
assert(index < _storage.capacity, "Write index is out of writing bound")
|
||||
assert(index >= 0, "Writer index should be above zero")
|
||||
_ = withUnsafePointer(to: value) {
|
||||
withUnsafePointer(to: value) {
|
||||
memcpy(
|
||||
_storage.memory.advanced(by: index),
|
||||
$0,
|
||||
|
||||
482
swift/Sources/FlexBuffers/ByteBuffer.swift
Normal file
482
swift/Sources/FlexBuffers/ByteBuffer.swift
Normal file
@@ -0,0 +1,482 @@
|
||||
/*
|
||||
* Copyright 2024 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
|
||||
|
||||
/// `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 {
|
||||
|
||||
/// Storage is a container that would hold the memory pointer to solve the issue of
|
||||
/// deallocating the memory that was held by (memory: UnsafeMutableRawPointer)
|
||||
@usableFromInline
|
||||
final class Storage {
|
||||
@usableFromInline
|
||||
enum Blob {
|
||||
#if !os(WASI)
|
||||
case data(Data)
|
||||
case bytes(ContiguousBytes)
|
||||
#endif
|
||||
|
||||
case byteBuffer(_InternalByteBuffer)
|
||||
case array([UInt8])
|
||||
case pointer(UnsafeMutableRawPointer)
|
||||
}
|
||||
|
||||
/// This storage doesn't own the memory, therefore, we won't deallocate on deinit.
|
||||
private let isOwned: Bool
|
||||
/// Retained blob of data that requires the storage to retain a pointer to.
|
||||
@usableFromInline
|
||||
var retainedBlob: Blob
|
||||
/// Capacity of UInt8 the buffer can hold
|
||||
var capacity: Int
|
||||
|
||||
@usableFromInline
|
||||
init(count: Int) {
|
||||
let memory = UnsafeMutableRawPointer.allocate(
|
||||
byteCount: count,
|
||||
alignment: MemoryLayout<UInt8>.alignment)
|
||||
capacity = count
|
||||
retainedBlob = .pointer(memory)
|
||||
isOwned = true
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
init(blob: Blob, capacity count: Int) {
|
||||
capacity = count
|
||||
retainedBlob = blob
|
||||
isOwned = false
|
||||
}
|
||||
|
||||
deinit {
|
||||
guard isOwned else { return }
|
||||
switch retainedBlob {
|
||||
case .pointer(let unsafeMutableRawPointer):
|
||||
unsafeMutableRawPointer.deallocate()
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
func copy(from ptr: UnsafeRawPointer, count: Int) {
|
||||
assert(
|
||||
isOwned,
|
||||
"copy should NOT be called on a buffer that is built by assumingMemoryBound")
|
||||
withUnsafeRawPointer {
|
||||
$0.copyMemory(from: ptr, byteCount: count)
|
||||
}
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
func initialize(for size: Int) {
|
||||
assert(
|
||||
isOwned,
|
||||
"initalize should NOT be called on a buffer that is built by assumingMemoryBound")
|
||||
withUnsafeRawPointer {
|
||||
memset($0, 0, size)
|
||||
}
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
@inline(__always)
|
||||
func withUnsafeBytes<T>(
|
||||
_ body: (UnsafeRawBufferPointer) throws
|
||||
-> T) rethrows -> T
|
||||
{
|
||||
switch retainedBlob {
|
||||
case .byteBuffer(let byteBuffer):
|
||||
return try byteBuffer.withUnsafeBytes(body)
|
||||
#if !os(WASI)
|
||||
case .data(let data):
|
||||
return try data.withUnsafeBytes(body)
|
||||
case .bytes(let contiguousBytes):
|
||||
return try contiguousBytes.withUnsafeBytes(body)
|
||||
#endif
|
||||
case .array(let array):
|
||||
return try array.withUnsafeBytes(body)
|
||||
case .pointer(let ptr):
|
||||
return try body(UnsafeRawBufferPointer(start: ptr, count: capacity))
|
||||
}
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
@inline(__always)
|
||||
func withUnsafeRawPointer<T>(
|
||||
_ body: (UnsafeMutableRawPointer) throws
|
||||
-> T) rethrows -> T
|
||||
{
|
||||
switch retainedBlob {
|
||||
case .byteBuffer(let byteBuffer):
|
||||
return try byteBuffer.withUnsafeRawPointer(body)
|
||||
#if !os(WASI)
|
||||
case .data(let data):
|
||||
return try data
|
||||
.withUnsafeBytes {
|
||||
try body(UnsafeMutableRawPointer(mutating: $0.baseAddress!))
|
||||
}
|
||||
case .bytes(let contiguousBytes):
|
||||
return try contiguousBytes
|
||||
.withUnsafeBytes {
|
||||
try body(UnsafeMutableRawPointer(mutating: $0.baseAddress!))
|
||||
}
|
||||
#endif
|
||||
case .array(let array):
|
||||
return try array
|
||||
.withUnsafeBytes {
|
||||
try body(UnsafeMutableRawPointer(mutating: $0.baseAddress!))
|
||||
}
|
||||
case .pointer(let ptr):
|
||||
return try body(ptr)
|
||||
}
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
@inline(__always)
|
||||
func readWithUnsafeRawPointer<T>(
|
||||
position: Int,
|
||||
_ body: (UnsafeRawPointer) throws -> T) rethrows -> T
|
||||
{
|
||||
switch retainedBlob {
|
||||
case .byteBuffer(let byteBuffer):
|
||||
return try byteBuffer.readWithUnsafeRawPointer(position: position, body)
|
||||
#if !os(WASI)
|
||||
case .data(let data):
|
||||
return try data.withUnsafeBytes {
|
||||
try body($0.baseAddress!.advanced(by: position))
|
||||
}
|
||||
case .bytes(let contiguousBytes):
|
||||
return try contiguousBytes.withUnsafeBytes {
|
||||
try body($0.baseAddress!.advanced(by: position))
|
||||
}
|
||||
#endif
|
||||
case .array(let array):
|
||||
return try array.withUnsafeBytes {
|
||||
try body($0.baseAddress!.advanced(by: position))
|
||||
}
|
||||
case .pointer(let ptr):
|
||||
return try body(ptr.advanced(by: position))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@usableFromInline var _storage: Storage
|
||||
|
||||
/// The size of the elements written to the buffer + their paddings
|
||||
private var _readerIndex: Int = 0
|
||||
// /// Reader is the position of the current Writer Index (capacity - size)
|
||||
// var reader: Int { _storage.capacity &- _readerIndex }
|
||||
/// Current size of the buffer
|
||||
public var count: Int { _readerIndex }
|
||||
/// Current capacity for the buffer including unused space
|
||||
public var capacity: Int { _storage.capacity }
|
||||
|
||||
/// Constructor that creates a Flatbuffer object from an InternalByteBuffer
|
||||
/// - Parameter
|
||||
/// - bytes: Array of UInt8
|
||||
@inline(__always)
|
||||
init(byteBuffer: _InternalByteBuffer) {
|
||||
_storage = Storage(
|
||||
blob: .byteBuffer(byteBuffer),
|
||||
capacity: byteBuffer.capacity)
|
||||
_readerIndex = byteBuffer.writerIndex
|
||||
}
|
||||
|
||||
/// Constructor that creates a Flatbuffer from unsafe memory region by copying
|
||||
/// the underlying data to a new pointer
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - copyingMemoryBound: The unsafe memory region
|
||||
/// - capacity: The size of the given memory region
|
||||
@inline(__always)
|
||||
public init(
|
||||
copyingMemoryBound memory: UnsafeRawPointer,
|
||||
capacity: Int)
|
||||
{
|
||||
_storage = Storage(count: capacity)
|
||||
_storage.copy(from: memory, count: capacity)
|
||||
_readerIndex = _storage.capacity
|
||||
}
|
||||
|
||||
/// Constructor that creates a Flatbuffer object from a UInt8
|
||||
/// - Parameter
|
||||
/// - bytes: Array of UInt8
|
||||
@inline(__always)
|
||||
public init(bytes: [UInt8]) {
|
||||
_storage = Storage(blob: .array(bytes), capacity: bytes.count)
|
||||
_readerIndex = _storage.capacity
|
||||
}
|
||||
|
||||
#if !os(WASI)
|
||||
/// Constructor that creates a Flatbuffer from the Swift Data type object
|
||||
/// - Parameter
|
||||
/// - data: Swift data Object
|
||||
@inline(__always)
|
||||
public init(data: Data) {
|
||||
_storage = Storage(blob: .data(data), capacity: data.count)
|
||||
_readerIndex = _storage.capacity
|
||||
}
|
||||
|
||||
/// Constructor that creates a Flatbuffer object from a ContiguousBytes
|
||||
/// - Parameters:
|
||||
/// - contiguousBytes: Binary stripe to use as the buffer
|
||||
/// - count: amount of readable bytes
|
||||
@inline(__always)
|
||||
public init<Bytes: ContiguousBytes>(
|
||||
contiguousBytes: Bytes,
|
||||
count: Int)
|
||||
{
|
||||
_storage = Storage(blob: .bytes(contiguousBytes), capacity: count)
|
||||
_readerIndex = _storage.capacity
|
||||
}
|
||||
#endif
|
||||
|
||||
/// Constructor that creates a Flatbuffer from unsafe memory region without copying
|
||||
/// **NOTE** Needs a call to `memory.deallocate()` later on to free the memory
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - assumingMemoryBound: The unsafe memory region
|
||||
/// - capacity: The size of the given memory region
|
||||
@inline(__always)
|
||||
public init(
|
||||
assumingMemoryBound memory: UnsafeMutableRawPointer,
|
||||
capacity: Int)
|
||||
{
|
||||
_storage = Storage(
|
||||
blob: .pointer(memory),
|
||||
capacity: capacity)
|
||||
_readerIndex = _storage.capacity
|
||||
}
|
||||
|
||||
/// Creates a copy of the existing flatbuffer, by copying it to a different memory.
|
||||
/// - Parameters:
|
||||
/// - memory: Current memory of the buffer
|
||||
/// - count: count of bytes
|
||||
/// - removeBytes: Removes a number of bytes from the current size
|
||||
@inline(__always)
|
||||
init(
|
||||
blob: Storage.Blob,
|
||||
count: Int,
|
||||
removing removeBytes: Int)
|
||||
{
|
||||
_storage = Storage(blob: blob, capacity: count)
|
||||
_readerIndex = removeBytes
|
||||
}
|
||||
|
||||
/// Write stores an object into the buffer directly or indirectly.
|
||||
///
|
||||
/// Direct: ignores the capacity of buffer which would mean we are referring to the direct point in memory
|
||||
/// indirect: takes into respect the current capacity of the buffer (capacity - index), writing to the buffer from the end
|
||||
/// - Parameters:
|
||||
/// - value: Value that needs to be written to the buffer
|
||||
/// - index: index to write to
|
||||
/// - direct: Should take into consideration the capacity of the buffer
|
||||
@inline(__always)
|
||||
func write<T>(value: T, index: Int, direct: Bool = false) {
|
||||
var index = index
|
||||
if !direct {
|
||||
index = _storage.capacity &- index
|
||||
}
|
||||
assert(index < _storage.capacity, "Write index is out of writing bound")
|
||||
assert(index >= 0, "Writer index should be above zero")
|
||||
withUnsafePointer(to: value) { ptr in
|
||||
_storage.withUnsafeRawPointer {
|
||||
memcpy(
|
||||
$0.advanced(by: index),
|
||||
ptr,
|
||||
MemoryLayout<T>.size)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads an object from the buffer
|
||||
/// - Parameters:
|
||||
/// - def: Type of the object
|
||||
/// - position: the index of the object in the buffer
|
||||
@inline(__always)
|
||||
public func read<T>(def: T.Type, position: Int) -> T {
|
||||
_storage.readWithUnsafeRawPointer(position: position) {
|
||||
$0.bindMemory(to: T.self, capacity: 1)
|
||||
.pointee
|
||||
}
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public func readUInt64(offset: Int, byteWidth: UInt8) -> UInt64 {
|
||||
readSizedScalar(
|
||||
def: UInt64.self,
|
||||
t1: UInt8.self,
|
||||
t2: UInt16.self,
|
||||
t3: UInt32.self,
|
||||
t4: UInt64.self,
|
||||
position: offset,
|
||||
byteWidth: byteWidth)
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public func readInt64(offset: Int, byteWidth: UInt8) -> Int64 {
|
||||
readSizedScalar(
|
||||
def: Int64.self,
|
||||
t1: Int8.self,
|
||||
t2: Int16.self,
|
||||
t3: Int32.self,
|
||||
t4: Int64.self,
|
||||
position: offset,
|
||||
byteWidth: byteWidth)
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public func readDouble(offset: Int, byteWidth: UInt8) -> Double {
|
||||
switch byteWidth {
|
||||
case 4:
|
||||
Double(read(def: Float32.self, position: offset))
|
||||
default:
|
||||
read(def: Double.self, position: offset)
|
||||
}
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
func readSizedScalar<
|
||||
T: BinaryInteger,
|
||||
T1: BinaryInteger,
|
||||
T2: BinaryInteger,
|
||||
T3: BinaryInteger,
|
||||
T4: BinaryInteger
|
||||
>(
|
||||
def: T.Type,
|
||||
t1: T1.Type,
|
||||
t2: T2.Type,
|
||||
t3: T3.Type,
|
||||
t4: T4.Type,
|
||||
position: Int,
|
||||
byteWidth: UInt8) -> T
|
||||
{
|
||||
switch byteWidth {
|
||||
case 1:
|
||||
numericCast(read(def: T1.self, position: position))
|
||||
case 2:
|
||||
numericCast(read(def: T2.self, position: position))
|
||||
case 4:
|
||||
numericCast(read(def: T3.self, position: position))
|
||||
default:
|
||||
numericCast(read(def: T4.self, position: position))
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads a slice from the memory assuming a type of T
|
||||
/// - Parameters:
|
||||
/// - index: index of the object to be read from the buffer
|
||||
/// - count: count of bytes in memory
|
||||
@inline(__always)
|
||||
public func readSlice<T>(
|
||||
index: Int,
|
||||
count: Int) -> [T]
|
||||
{
|
||||
assert(
|
||||
index + count <= _storage.capacity,
|
||||
"Reading out of bounds is illegal")
|
||||
|
||||
return _storage.readWithUnsafeRawPointer(position: index) {
|
||||
let buf = UnsafeBufferPointer(
|
||||
start: $0.bindMemory(to: T.self, capacity: count),
|
||||
count: count)
|
||||
return Array(buf)
|
||||
}
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public func readString(
|
||||
at index: Int,
|
||||
count: Int,
|
||||
type: String.Encoding) -> String?
|
||||
{
|
||||
assert(
|
||||
index + count <= _storage.capacity,
|
||||
"Reading out of bounds is illegal")
|
||||
return _storage.readWithUnsafeRawPointer(position: index) {
|
||||
let buf = UnsafeBufferPointer(
|
||||
start: $0.bindMemory(to: UInt8.self, capacity: count),
|
||||
count: count)
|
||||
return String(
|
||||
bytes: buf,
|
||||
encoding: type)
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads a string from the buffer and encodes it to a swift string
|
||||
/// - Parameters:
|
||||
/// - index: index of the string in the buffer
|
||||
/// - count: length of the string
|
||||
@inline(__always)
|
||||
public func readString(
|
||||
at index: Int,
|
||||
count: Int) -> String?
|
||||
{
|
||||
assert(
|
||||
index + count <= _storage.capacity,
|
||||
"Reading out of bounds is illegal")
|
||||
return _storage.readWithUnsafeRawPointer(position: index) {
|
||||
String(cString: $0.bindMemory(to: UInt8.self, capacity: count))
|
||||
}
|
||||
}
|
||||
|
||||
/// Provides a pointer towards the underlying primitive types
|
||||
/// - Parameters:
|
||||
/// - index: index of the object to be read from the buffer
|
||||
/// - count: count of bytes in memory
|
||||
@discardableResult
|
||||
@inline(__always)
|
||||
public func withUnsafePointerToSlice<T>(
|
||||
index: Int,
|
||||
count: Int,
|
||||
body: (UnsafeRawBufferPointer) throws -> T) rethrows -> T
|
||||
{
|
||||
assert(
|
||||
index + count <= _storage.capacity,
|
||||
"Reading out of bounds is illegal")
|
||||
return try _storage.readWithUnsafeRawPointer(position: index) {
|
||||
try body(UnsafeRawBufferPointer(start: $0, count: count))
|
||||
}
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
@inline(__always)
|
||||
public func withUnsafeBytes<T>(
|
||||
body: (UnsafeRawBufferPointer) throws
|
||||
-> T) rethrows -> T
|
||||
{
|
||||
try _storage.withUnsafeBytes(body)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
@inline(__always)
|
||||
func withUnsafeMutableRawPointer<T>(
|
||||
body: (UnsafeMutableRawPointer) throws
|
||||
-> T) rethrows -> T
|
||||
{
|
||||
try _storage.withUnsafeRawPointer(body)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
@inline(__always)
|
||||
func readWithUnsafeRawPointer<T>(
|
||||
position: Int,
|
||||
_ body: (UnsafeRawPointer) throws -> T) rethrows -> T
|
||||
{
|
||||
try _storage.readWithUnsafeRawPointer(position: position, body)
|
||||
}
|
||||
}
|
||||
75
swift/Sources/FlexBuffers/FlexBufferType.swift
Normal file
75
swift/Sources/FlexBuffers/FlexBufferType.swift
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2024 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
|
||||
|
||||
public enum FlexBufferType: UInt64 {
|
||||
case null = 0
|
||||
/// Variable width signed integer: `Int8, Int16, Int32, Int64`
|
||||
case int = 1
|
||||
/// Variable width signed integer: `UInt8, UInt16, UInt32, UInt64`
|
||||
case uint = 2
|
||||
/// Variable width floating point: `Float32, Double64`
|
||||
case float = 3
|
||||
case key = 4
|
||||
case string = 5
|
||||
/// An Int, stored by offset rather than inline. Indirect types can keep the bitwidth of a
|
||||
/// vector or map small when the inline value would have increased the bitwidth.
|
||||
case indirectInt = 6
|
||||
/// A UInt, stored by offset rather than inline. Indirect types can keep the bitwidth of a
|
||||
/// vector or map small when the inline value would have increased the bitwidth.
|
||||
case indirectUInt = 7
|
||||
/// A Float, stored by offset rather than inline. Indirect types can keep the bitwidth of a
|
||||
/// vector or map small when the inline value would have increased the bitwidth.
|
||||
case indirectFloat = 8
|
||||
case map = 9
|
||||
/// Untyped
|
||||
case vector = 10
|
||||
/// Typed any sizes (stores no type table).
|
||||
case vectorInt = 11
|
||||
case vectorUInt = 12
|
||||
case vectorFloat = 13
|
||||
case vectorKey = 14
|
||||
@available(
|
||||
*,
|
||||
deprecated,
|
||||
message: "use FBT_VECTOR or FBT_VECTOR_KEY instead.")
|
||||
case vectorString = 15
|
||||
|
||||
/// Typed tuples (no type table, no size field).
|
||||
case vectorInt2 = 16
|
||||
case vectorUInt2 = 17
|
||||
case vectorFloat2 = 18
|
||||
/// Typed triples (no type table, no size field).
|
||||
case vectorInt3 = 19
|
||||
case vectorUInt3 = 20
|
||||
case vectorFloat3 = 21
|
||||
/// Typed quad (no type table, no size field).
|
||||
case vectorInt4 = 22
|
||||
case vectorUInt4 = 23
|
||||
case vectorFloat4 = 24
|
||||
case blob = 25
|
||||
case bool = 26
|
||||
/// To Allow the same type of conversion of type to vector type
|
||||
case vectorBool = 36
|
||||
case max = 37
|
||||
}
|
||||
|
||||
extension FlexBufferType: Comparable {
|
||||
public static func < (lhs: FlexBufferType, rhs: FlexBufferType) -> Bool {
|
||||
lhs.rawValue < rhs.rawValue
|
||||
}
|
||||
}
|
||||
53
swift/Sources/FlexBuffers/Reader/FixedTypedVector.swift
Normal file
53
swift/Sources/FlexBuffers/Reader/FixedTypedVector.swift
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2024 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
|
||||
|
||||
public struct FixedTypedVector: FlexBufferVector {
|
||||
public let byteBuffer: ByteBuffer
|
||||
public let offset: Int
|
||||
public let type: FlexBufferType
|
||||
public let count: Int
|
||||
public var isEmpty: Bool { count == 0 }
|
||||
|
||||
let byteWidth: UInt8
|
||||
|
||||
@inline(__always)
|
||||
init(
|
||||
byteBuffer: ByteBuffer,
|
||||
offset: Int,
|
||||
byteWidth: UInt8,
|
||||
type: FlexBufferType,
|
||||
count: Int)
|
||||
{
|
||||
self.byteBuffer = byteBuffer
|
||||
self.offset = offset
|
||||
self.byteWidth = byteWidth
|
||||
self.type = type
|
||||
self.count = count
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public subscript(index: Int) -> Reference? {
|
||||
let elementOffset = offset &+ (numericCast(index) &* numericCast(byteWidth))
|
||||
return Reference(
|
||||
byteBuffer: byteBuffer,
|
||||
offset: elementOffset,
|
||||
parentWidth: byteWidth,
|
||||
byteWidth: 1,
|
||||
type: type)
|
||||
}
|
||||
}
|
||||
54
swift/Sources/FlexBuffers/Reader/FlexBufferVector.swift
Normal file
54
swift/Sources/FlexBuffers/Reader/FlexBufferVector.swift
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2024 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
|
||||
|
||||
protocol FlexBufferVector: Sized & FlexBufferContiguousBytes {
|
||||
subscript(index: Int) -> Reference? { get }
|
||||
}
|
||||
|
||||
extension FlexBufferVector {
|
||||
public func jsonBuilder(json: inout String) {
|
||||
json += "["
|
||||
for i in 0..<count {
|
||||
if let val = self[i]?.jsonString() {
|
||||
let comma = i == count - 1 ? "" : ", "
|
||||
json += "\(val)\(comma)"
|
||||
}
|
||||
}
|
||||
json += "]"
|
||||
}
|
||||
}
|
||||
|
||||
public protocol FlexBufferContiguousBytes {
|
||||
var byteBuffer: ByteBuffer { get }
|
||||
var offset: Int { get }
|
||||
var count: Int { get }
|
||||
|
||||
func withUnsafeRawBufferPointer<Result>(
|
||||
_ body: (UnsafeRawBufferPointer) throws -> Result) rethrows -> Result
|
||||
}
|
||||
|
||||
extension FlexBufferContiguousBytes {
|
||||
public func withUnsafeRawBufferPointer<Result>(
|
||||
_ body: (UnsafeRawBufferPointer) throws -> Result) rethrows -> Result
|
||||
{
|
||||
try byteBuffer.withUnsafePointerToSlice(
|
||||
index: offset,
|
||||
count: count,
|
||||
body: body)
|
||||
}
|
||||
}
|
||||
62
swift/Sources/FlexBuffers/Reader/Map.swift
Normal file
62
swift/Sources/FlexBuffers/Reader/Map.swift
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2024 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.
|
||||
*/
|
||||
|
||||
public struct Map: Sized {
|
||||
let byteBuffer: ByteBuffer
|
||||
let offset: Int
|
||||
let byteWidth: UInt8
|
||||
public let keys: TypedVector
|
||||
public let count: Int
|
||||
|
||||
public var values: Vector {
|
||||
return Vector(byteBuffer: byteBuffer, offset: offset, byteWidth: byteWidth)
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
init(byteBuffer: ByteBuffer, offset: Int, byteWidth: UInt8) {
|
||||
self.byteBuffer = byteBuffer
|
||||
self.offset = offset
|
||||
self.byteWidth = byteWidth
|
||||
|
||||
count = getCount(buffer: byteBuffer, offset: offset, byteWidth: byteWidth)
|
||||
keys = TypedVector.mapKeys(
|
||||
byteBuffer: byteBuffer,
|
||||
offset: offset,
|
||||
byteWidth: byteWidth)
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public subscript(key: String) -> Reference? {
|
||||
guard let position = binarySearch(vector: keys, target: key)
|
||||
else { return nil }
|
||||
|
||||
return getReference(at: position)
|
||||
}
|
||||
}
|
||||
|
||||
extension Map {
|
||||
public func jsonBuilder(json: inout String) {
|
||||
json += "{"
|
||||
for i in 0..<count {
|
||||
if let key = keys[i]?.cString {
|
||||
let comma = i == count - 1 ? "" : ", "
|
||||
let value = values[i]?.jsonString() ?? StaticJSON.null
|
||||
json += "\"\(key)\": \(value)\(comma)"
|
||||
}
|
||||
}
|
||||
json += "}"
|
||||
}
|
||||
}
|
||||
298
swift/Sources/FlexBuffers/Reader/Reference.swift
Normal file
298
swift/Sources/FlexBuffers/Reader/Reference.swift
Normal file
@@ -0,0 +1,298 @@
|
||||
/*
|
||||
* Copyright 2024 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 FlexBuffersErrors: Error {
|
||||
case sizeOfBufferIsTooSmall
|
||||
case typeCouldNotBeDetermined
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public func getRoot(buffer: ByteBuffer) throws -> Reference? {
|
||||
let end = buffer.count
|
||||
if buffer.count < 3 {
|
||||
throw FlexBuffersErrors.sizeOfBufferIsTooSmall
|
||||
}
|
||||
|
||||
let byteWidth = buffer.read(def: UInt8.self, position: end &- 1)
|
||||
let packedType = buffer.read(def: UInt8.self, position: end &- 2)
|
||||
let offset = end &- 2 &- numericCast(byteWidth)
|
||||
|
||||
return Reference(
|
||||
byteBuffer: buffer,
|
||||
offset: offset,
|
||||
parentWidth: byteWidth,
|
||||
packedType: packedType)
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public func getRootChecked(buffer: ByteBuffer) throws -> Reference? {
|
||||
// TODO(mustiikhalil): implement verifier
|
||||
return try getRoot(buffer: buffer)
|
||||
}
|
||||
|
||||
public struct Reference {
|
||||
private let byteBuffer: ByteBuffer
|
||||
private let offset: Int
|
||||
private let parentWidth: UInt8
|
||||
private let byteWidth: UInt8
|
||||
|
||||
public let type: FlexBufferType
|
||||
|
||||
@inline(__always)
|
||||
init?(
|
||||
byteBuffer: ByteBuffer,
|
||||
offset: Int,
|
||||
parentWidth: UInt8,
|
||||
packedType: UInt8)
|
||||
{
|
||||
guard let type = FlexBufferType(rawValue: UInt64(packedType >> 2)) else {
|
||||
return nil
|
||||
}
|
||||
self.byteBuffer = byteBuffer
|
||||
self.offset = offset
|
||||
self.parentWidth = parentWidth
|
||||
byteWidth = 1 << (packedType & 3)
|
||||
self.type = type
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
init(
|
||||
byteBuffer: ByteBuffer,
|
||||
offset: Int,
|
||||
parentWidth: UInt8,
|
||||
byteWidth: UInt8,
|
||||
type: FlexBufferType)
|
||||
{
|
||||
self.byteBuffer = byteBuffer
|
||||
self.offset = offset
|
||||
self.parentWidth = parentWidth
|
||||
self.byteWidth = byteWidth
|
||||
self.type = type
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public var bool: Bool? {
|
||||
return switch type {
|
||||
case .bool: byteBuffer.readUInt64(offset: offset, byteWidth: byteWidth) != 0
|
||||
default: nil
|
||||
}
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public var uint: UInt64? {
|
||||
return switch type {
|
||||
case .uint: byteBuffer.readUInt64(offset: offset, byteWidth: byteWidth)
|
||||
case .indirectUInt: byteBuffer.readUInt64(
|
||||
offset: indirect(),
|
||||
byteWidth: byteWidth)
|
||||
default: nil
|
||||
}
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public var int: Int64? {
|
||||
return switch type {
|
||||
case .int: byteBuffer.readInt64(offset: offset, byteWidth: byteWidth)
|
||||
case .indirectInt: byteBuffer.readInt64(
|
||||
offset: indirect(),
|
||||
byteWidth: byteWidth)
|
||||
default: nil
|
||||
}
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public var double: Double? {
|
||||
return switch type {
|
||||
case .float: byteBuffer.readDouble(offset: offset, byteWidth: byteWidth)
|
||||
case .indirectFloat: byteBuffer.readDouble(
|
||||
offset: indirect(),
|
||||
byteWidth: byteWidth)
|
||||
default: nil
|
||||
}
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public var map: Map? {
|
||||
guard type == .map else { return nil }
|
||||
return Map(
|
||||
byteBuffer: byteBuffer,
|
||||
offset: indirect(),
|
||||
byteWidth: byteWidth)
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public var vector: Vector? {
|
||||
guard type == .vector || type == .map else { return nil }
|
||||
return Vector(
|
||||
byteBuffer: byteBuffer,
|
||||
offset: indirect(),
|
||||
byteWidth: byteWidth)
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public var cString: String? {
|
||||
guard type == .string || type == .key else { return nil }
|
||||
let offset = indirect()
|
||||
|
||||
let count = getCount(
|
||||
buffer: byteBuffer,
|
||||
offset: offset,
|
||||
byteWidth: byteWidth)
|
||||
|
||||
return byteBuffer.readString(
|
||||
at: offset,
|
||||
count: count)
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public func blob<Result>(_ completion: (UnsafeRawBufferPointer) -> Result)
|
||||
-> Result?
|
||||
{
|
||||
guard type == .blob || type == .string else { return nil }
|
||||
|
||||
let offset = indirect()
|
||||
let count = getCount(
|
||||
buffer: byteBuffer,
|
||||
offset: offset,
|
||||
byteWidth: byteWidth)
|
||||
return byteBuffer.withUnsafePointerToSlice(
|
||||
index: offset,
|
||||
count: count,
|
||||
body: completion)
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public var typedVector: TypedVector? {
|
||||
guard isTypedVectorType(type: type) else { return nil }
|
||||
guard var type = toTypedVectorElementType(type: type) else { return nil }
|
||||
if type == .string {
|
||||
type = .key
|
||||
}
|
||||
return TypedVector(
|
||||
byteBuffer: byteBuffer,
|
||||
offset: indirect(),
|
||||
byteWidth: byteWidth,
|
||||
type: type)
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public var fixedTypedVector: FixedTypedVector? {
|
||||
guard isFixedTypedVectorType(type: type) else { return nil }
|
||||
let t = toFixedTypedVectorElementType(type: type)
|
||||
guard let type = t.type else { return nil }
|
||||
return FixedTypedVector(
|
||||
byteBuffer: byteBuffer,
|
||||
offset: indirect(),
|
||||
byteWidth: byteWidth,
|
||||
type: type,
|
||||
count: t.count)
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public func string(encoding: String.Encoding = .utf8) -> String? {
|
||||
guard type == .string else { return nil }
|
||||
let offset = indirect()
|
||||
|
||||
let count = getCount(
|
||||
buffer: byteBuffer,
|
||||
offset: offset,
|
||||
byteWidth: byteWidth)
|
||||
|
||||
return byteBuffer.readString(
|
||||
at: offset,
|
||||
count: count,
|
||||
type: encoding)
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public func asInt<T: FixedWidthInteger>() -> T? {
|
||||
guard let v = int else {
|
||||
return nil
|
||||
}
|
||||
return numericCast(v)
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public func asUInt<T: FixedWidthInteger>() -> T? {
|
||||
guard let v = uint else {
|
||||
return nil
|
||||
}
|
||||
return numericCast(v)
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public func withUnsafeRawPointer<Result>(
|
||||
_ completion: (UnsafeRawPointer) throws
|
||||
-> Result)
|
||||
rethrows -> Result?
|
||||
{
|
||||
return try byteBuffer.readWithUnsafeRawPointer(
|
||||
position: indirect(),
|
||||
completion)
|
||||
}
|
||||
|
||||
private func indirect() -> Int {
|
||||
readIndirect(buffer: byteBuffer, offset: offset, parentWidth)
|
||||
}
|
||||
}
|
||||
|
||||
extension Reference {
|
||||
|
||||
public func jsonString() -> String {
|
||||
var str = ""
|
||||
jsonBuilder(json: &str)
|
||||
return str
|
||||
}
|
||||
|
||||
func jsonBuilder(json: inout String) {
|
||||
switch type {
|
||||
case .null:
|
||||
json += StaticJSON.null
|
||||
case .uint, .indirectUInt:
|
||||
json += uint.valueOrNull
|
||||
case .int, .indirectInt:
|
||||
json += int.valueOrNull
|
||||
case .float, .indirectFloat:
|
||||
json += double.valueOrNull
|
||||
case .string, .key:
|
||||
json += "\"\(cString ?? StaticJSON.null)\""
|
||||
case .map:
|
||||
map?.jsonBuilder(json: &json)
|
||||
case .bool:
|
||||
json += bool.valueOrNull
|
||||
case .blob:
|
||||
if let p = blob({ String(data: Data($0), encoding: .utf8) })?
|
||||
.valueOrNull
|
||||
{
|
||||
json += "\"\(p)\""
|
||||
} else {
|
||||
json += StaticJSON.null
|
||||
}
|
||||
default:
|
||||
if type == .vector {
|
||||
vector?.jsonBuilder(json: &json)
|
||||
} else if isTypedVectorType(type: type) {
|
||||
typedVector?.jsonBuilder(json: &json)
|
||||
} else if isFixedTypedVectorType(type: type) {
|
||||
fixedTypedVector?.jsonBuilder(json: &json)
|
||||
} else {
|
||||
json += StaticJSON.null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
43
swift/Sources/FlexBuffers/Reader/Sized.swift
Normal file
43
swift/Sources/FlexBuffers/Reader/Sized.swift
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2024 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.
|
||||
*/
|
||||
|
||||
protocol Sized {
|
||||
var byteBuffer: ByteBuffer { get }
|
||||
var offset: Int { get }
|
||||
var byteWidth: UInt8 { get }
|
||||
var count: Int { get }
|
||||
}
|
||||
|
||||
extension Sized {
|
||||
|
||||
@inline(__always)
|
||||
func getReference(at index: Int) -> Reference? {
|
||||
if index >= count { return nil }
|
||||
let bWidth = Int(byteWidth)
|
||||
|
||||
let packedType = byteBuffer.read(
|
||||
def: UInt8.self,
|
||||
position: (offset &+ (count &* bWidth)) &+ index)
|
||||
|
||||
let offset = offset &+ (index &* bWidth)
|
||||
|
||||
return Reference(
|
||||
byteBuffer: byteBuffer,
|
||||
offset: offset,
|
||||
parentWidth: byteWidth,
|
||||
packedType: packedType)
|
||||
}
|
||||
}
|
||||
99
swift/Sources/FlexBuffers/Reader/TypedVector.swift
Normal file
99
swift/Sources/FlexBuffers/Reader/TypedVector.swift
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright 2024 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
|
||||
|
||||
public struct TypedVector: FlexBufferVector {
|
||||
public let byteBuffer: ByteBuffer
|
||||
public let offset: Int
|
||||
public let type: FlexBufferType
|
||||
public let count: Int
|
||||
public var isEmpty: Bool { count == 0 }
|
||||
|
||||
let byteWidth: UInt8
|
||||
|
||||
@inline(__always)
|
||||
init(
|
||||
byteBuffer: ByteBuffer,
|
||||
offset: Int,
|
||||
byteWidth: UInt8,
|
||||
type: FlexBufferType)
|
||||
{
|
||||
self.byteBuffer = byteBuffer
|
||||
self.offset = offset
|
||||
self.byteWidth = byteWidth
|
||||
self.type = type
|
||||
count = getCount(buffer: byteBuffer, offset: offset, byteWidth: byteWidth)
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public subscript(index: Int) -> Reference? {
|
||||
let elementOffset = offset &+ (numericCast(index) &* numericCast(byteWidth))
|
||||
return Reference(
|
||||
byteBuffer: byteBuffer,
|
||||
offset: elementOffset,
|
||||
parentWidth: byteWidth,
|
||||
byteWidth: 1,
|
||||
type: type)
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
static func mapKeys(
|
||||
byteBuffer: ByteBuffer,
|
||||
offset: Int,
|
||||
byteWidth: UInt8) -> TypedVector
|
||||
{
|
||||
let prefixedFields = 3
|
||||
let keysOffset = offset &- (numericCast(byteWidth) &* prefixedFields)
|
||||
|
||||
let indirectOffset = readIndirect(
|
||||
buffer: byteBuffer,
|
||||
offset: keysOffset,
|
||||
byteWidth)
|
||||
|
||||
let childByteWidth = byteBuffer.readUInt64(
|
||||
offset: keysOffset &+ numericCast(byteWidth),
|
||||
byteWidth: byteWidth)
|
||||
|
||||
return TypedVector(
|
||||
byteBuffer: byteBuffer,
|
||||
offset: indirectOffset,
|
||||
byteWidth: numericCast(childByteWidth),
|
||||
type: .key)
|
||||
}
|
||||
}
|
||||
|
||||
extension TypedVector {
|
||||
@inline(__always)
|
||||
func compare(offset off: Int, target: String) -> Int {
|
||||
|
||||
let elementOffset = offset &+ (off &* numericCast(byteWidth))
|
||||
|
||||
let indirectoffset = readIndirect(
|
||||
buffer: byteBuffer,
|
||||
offset: elementOffset,
|
||||
byteWidth)
|
||||
|
||||
return byteBuffer.readWithUnsafeRawPointer(
|
||||
position: indirectoffset)
|
||||
{ bufPointer in
|
||||
target.withCString { strPointer in
|
||||
Int(strcmp(bufPointer, strPointer))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
42
swift/Sources/FlexBuffers/Reader/Vector.swift
Normal file
42
swift/Sources/FlexBuffers/Reader/Vector.swift
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2024 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.
|
||||
*/
|
||||
|
||||
// MARK: - Vector
|
||||
|
||||
public struct Vector: FlexBufferVector {
|
||||
public let byteBuffer: ByteBuffer
|
||||
public let offset: Int
|
||||
public let count: Int
|
||||
public var isEmpty: Bool { count == 0 }
|
||||
|
||||
let byteWidth: UInt8
|
||||
|
||||
@inline(__always)
|
||||
init(byteBuffer: ByteBuffer, offset: Int, byteWidth: UInt8) {
|
||||
self.byteBuffer = byteBuffer
|
||||
self.offset = offset
|
||||
self.byteWidth = byteWidth
|
||||
count = getCount(
|
||||
buffer: byteBuffer,
|
||||
offset: offset,
|
||||
byteWidth: byteWidth)
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public subscript(index: Int) -> Reference? {
|
||||
return getReference(at: index)
|
||||
}
|
||||
}
|
||||
48
swift/Sources/FlexBuffers/Utils/BitWidth.swift
Normal file
48
swift/Sources/FlexBuffers/Utils/BitWidth.swift
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2024 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
|
||||
|
||||
@usableFromInline
|
||||
enum BitWidth: UInt64, CaseIterable {
|
||||
case w8 = 0, w16 = 1, w32 = 2, w64 = 3
|
||||
}
|
||||
|
||||
extension BitWidth: Comparable {
|
||||
@usableFromInline
|
||||
static func < (lhs: BitWidth, rhs: BitWidth) -> Bool {
|
||||
lhs.rawValue < rhs.rawValue
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
static func widthB(_ v: Int) -> BitWidth {
|
||||
switch v {
|
||||
case 1: return .w8
|
||||
case 2: return .w16
|
||||
case 4: return .w32
|
||||
case 8: return .w64
|
||||
default:
|
||||
assert(false, "We shouldn't reach here")
|
||||
return .w64
|
||||
}
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
static func max(_ lhs: BitWidth, rhs: BitWidth) -> BitWidth {
|
||||
if lhs.rawValue > rhs.rawValue { return lhs }
|
||||
return rhs
|
||||
}
|
||||
}
|
||||
58
swift/Sources/FlexBuffers/Utils/Constants.swift
Normal file
58
swift/Sources/FlexBuffers/Utils/Constants.swift
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2024 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 canImport(Common)
|
||||
@_exported import Common
|
||||
#endif
|
||||
import Foundation
|
||||
|
||||
extension UInt64 {
|
||||
static let one: UInt64 = 1
|
||||
}
|
||||
|
||||
extension UInt32 {
|
||||
static let one: UInt32 = 1
|
||||
}
|
||||
|
||||
public enum BuilderFlag: UInt8 {
|
||||
case none = 0
|
||||
case shareKeys = 1
|
||||
case shareStrings = 2
|
||||
case shareKeysAndStrings = 3
|
||||
case shareKeyVectors = 4
|
||||
case shareAll = 7
|
||||
}
|
||||
|
||||
extension BuilderFlag: Comparable {
|
||||
public static func < (lhs: BuilderFlag, rhs: BuilderFlag) -> Bool {
|
||||
lhs.rawValue < rhs.rawValue
|
||||
}
|
||||
}
|
||||
|
||||
enum StaticJSON {
|
||||
static let null = "null"
|
||||
}
|
||||
|
||||
extension Optional {
|
||||
var valueOrNull: String {
|
||||
if let value = self {
|
||||
return "\(value)"
|
||||
} else {
|
||||
return StaticJSON.null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
137
swift/Sources/FlexBuffers/Utils/Value.swift
Normal file
137
swift/Sources/FlexBuffers/Utils/Value.swift
Normal file
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright 2024 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
|
||||
|
||||
public struct Value: Equatable {
|
||||
|
||||
@usableFromInline
|
||||
enum Union: Equatable {
|
||||
case i(Int64)
|
||||
case u(UInt64)
|
||||
case f(Double)
|
||||
}
|
||||
|
||||
var sloc: Union
|
||||
let type: FlexBufferType
|
||||
let bitWidth: BitWidth
|
||||
|
||||
@inline(__always)
|
||||
private init() {
|
||||
sloc = .i(0)
|
||||
type = .null
|
||||
bitWidth = .w8
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
init(bool: Bool) {
|
||||
sloc = .u(bool ? 1 : 0)
|
||||
type = .bool
|
||||
bitWidth = .w8
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
init(v: UInt64, type: FlexBufferType, bitWidth: BitWidth) {
|
||||
sloc = .u(v)
|
||||
self.type = type
|
||||
self.bitWidth = bitWidth
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
init(v: Int64, type: FlexBufferType, bitWidth: BitWidth) {
|
||||
sloc = .i(v)
|
||||
self.type = type
|
||||
self.bitWidth = bitWidth
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
init(v: Double, type: FlexBufferType, bitWidth: BitWidth) {
|
||||
sloc = .f(v)
|
||||
self.type = type
|
||||
self.bitWidth = bitWidth
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
init(sloc: Union, type: FlexBufferType, bitWidth: BitWidth) {
|
||||
self.sloc = sloc
|
||||
self.type = type
|
||||
self.bitWidth = bitWidth
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
var i: Int64 {
|
||||
switch sloc {
|
||||
case .i(let v): v
|
||||
default: 0
|
||||
}
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
var u: UInt64 {
|
||||
switch sloc {
|
||||
case .u(let v): v
|
||||
default: 0
|
||||
}
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
var f: Double {
|
||||
switch sloc {
|
||||
case .f(let v): v
|
||||
default: 0
|
||||
}
|
||||
}
|
||||
|
||||
static let `nil` = Value()
|
||||
}
|
||||
|
||||
extension Value {
|
||||
@usableFromInline
|
||||
@inline(__always)
|
||||
func elementWidth(size: Int, index: UInt64) -> BitWidth {
|
||||
if isInline(type) {
|
||||
return bitWidth
|
||||
} else {
|
||||
for byteWidth in stride(from: 1, to: MemoryLayout<UInt64>.size, by: 2) {
|
||||
let _offsetLoc: UInt64 = numericCast(numericCast(size) &+ padding(
|
||||
bufSize: numericCast(size),
|
||||
elementSize: numericCast(byteWidth)))
|
||||
let offsetLoc = _offsetLoc &+ (index &* numericCast(byteWidth))
|
||||
let offset = offsetLoc &- u
|
||||
|
||||
let bitWidth = widthU(offset)
|
||||
if (UInt32.one << bitWidth.rawValue) == byteWidth {
|
||||
return bitWidth
|
||||
}
|
||||
}
|
||||
return .w64
|
||||
}
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
func storedPackedType(width: BitWidth = .w8) -> UInt8 {
|
||||
packedType(bitWidth: storedWidth(width: width), type: type)
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
private func storedWidth(width: BitWidth) -> BitWidth {
|
||||
if isInline(type) {
|
||||
return max(bitWidth, width)
|
||||
} else {
|
||||
return bitWidth
|
||||
}
|
||||
}
|
||||
}
|
||||
158
swift/Sources/FlexBuffers/Utils/functions.swift
Normal file
158
swift/Sources/FlexBuffers/Utils/functions.swift
Normal file
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Copyright 2024 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
|
||||
|
||||
@inline(__always)
|
||||
internal func isInline(_ t: FlexBufferType) -> Bool {
|
||||
return t <= .float || t == .bool
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
private func check(_ v: UInt64, width: UInt64) -> Bool {
|
||||
(v & ~((.one << width) &- 1)) == 0
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
internal func widthI(_ v: Int64) -> BitWidth {
|
||||
let u = UInt64(bitPattern: v) << 1
|
||||
return widthU(v >= 0 ? u : ~u)
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
internal func widthF(_ v: Double) -> BitWidth {
|
||||
Double(Float(v)) == v ? .w32 : .w64
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
internal func widthU(_ v: UInt64) -> BitWidth {
|
||||
if check(v, width: 8) { return .w8 }
|
||||
if check(v, width: 16) { return .w16 }
|
||||
if check(v, width: 32) { return .w32 }
|
||||
return .w64
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
internal func packedType(bitWidth: BitWidth, type: FlexBufferType) -> UInt8 {
|
||||
numericCast(bitWidth.rawValue | (type.rawValue << 2))
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
func getScalarType<T>(type: T.Type) -> FlexBufferType where T: Scalar {
|
||||
if T.self is (any BinaryFloatingPoint.Type) {
|
||||
return .float
|
||||
}
|
||||
|
||||
if T.self is Bool.Type {
|
||||
return .bool
|
||||
}
|
||||
|
||||
if T.self is (any UnsignedInteger.Type) {
|
||||
return .uint
|
||||
}
|
||||
|
||||
return .int
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
func toTypedVector(type: FlexBufferType, length: UInt64) -> FlexBufferType {
|
||||
let type: UInt64 = switch length {
|
||||
case 0: type.rawValue &- FlexBufferType.int.rawValue &+ FlexBufferType
|
||||
.vectorInt.rawValue
|
||||
case 2: type.rawValue &- FlexBufferType.int.rawValue &+ FlexBufferType
|
||||
.vectorInt2.rawValue
|
||||
case 3: type.rawValue &- FlexBufferType.int.rawValue &+ FlexBufferType
|
||||
.vectorInt3.rawValue
|
||||
case 4: type.rawValue &- FlexBufferType.int.rawValue &+ FlexBufferType
|
||||
.vectorInt4.rawValue
|
||||
default: 0
|
||||
}
|
||||
return FlexBufferType(rawValue: type) ?? .null
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
func isTypedVectorElementType(type: FlexBufferType) -> Bool {
|
||||
return type >= .int && type <= .string || type == .bool
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
func isTypedVectorType(type: FlexBufferType) -> Bool {
|
||||
return type >= .vectorInt && type <= .vectorString || type == .vectorBool
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
func toTypedVectorElementType(type: FlexBufferType) -> FlexBufferType? {
|
||||
return FlexBufferType(
|
||||
rawValue: type.rawValue &- FlexBufferType.vectorInt
|
||||
.rawValue &+ FlexBufferType.int.rawValue)
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
func isFixedTypedVectorType(type: FlexBufferType) -> Bool {
|
||||
return type >= .vectorInt2 && type <= .vectorFloat4
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
func toFixedTypedVectorElementType(type: FlexBufferType)
|
||||
-> (type: FlexBufferType?, count: Int)
|
||||
{
|
||||
assert(isFixedTypedVectorType(type: type))
|
||||
let fixedType: UInt64 = numericCast(
|
||||
type.rawValue &- FlexBufferType.vectorInt2
|
||||
.rawValue)
|
||||
let len: Int = numericCast((fixedType / 3) + 2)
|
||||
return (
|
||||
FlexBufferType(rawValue: (fixedType % 3) + FlexBufferType.int.rawValue),
|
||||
len)
|
||||
}
|
||||
|
||||
// MARK: - Reader functions
|
||||
|
||||
@inline(__always)
|
||||
func binarySearch(
|
||||
vector: TypedVector,
|
||||
target: String) -> Int?
|
||||
{
|
||||
var left = 0
|
||||
var right = vector.count
|
||||
|
||||
while left <= right {
|
||||
let mid = left &+ (right &- left) / 2
|
||||
let comp = vector.compare(offset: mid, target: target)
|
||||
if comp == 0 {
|
||||
return mid
|
||||
} else if comp < 0 {
|
||||
left = mid &+ 1
|
||||
} else {
|
||||
right = mid &- 1
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
func readIndirect(buffer: ByteBuffer, offset: Int, _ byteWidth: UInt8) -> Int {
|
||||
return offset &- numericCast(buffer.readUInt64(
|
||||
offset: offset,
|
||||
byteWidth: byteWidth))
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
func getCount(buffer: ByteBuffer, offset: Int, byteWidth: UInt8) -> Int {
|
||||
Int(buffer.readUInt64(
|
||||
offset: offset &- numericCast(byteWidth),
|
||||
byteWidth: byteWidth))
|
||||
}
|
||||
884
swift/Sources/FlexBuffers/Writer/FlexBuffersWriter.swift
Normal file
884
swift/Sources/FlexBuffers/Writer/FlexBuffersWriter.swift
Normal file
@@ -0,0 +1,884 @@
|
||||
/*
|
||||
* Copyright 2024 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
|
||||
|
||||
private let twentyFourBytes: Int = 24
|
||||
public typealias FlexBuffersWriterBuilder = (inout FlexBuffersWriter) -> Void
|
||||
|
||||
public struct FlexBuffersWriter {
|
||||
|
||||
var capacity: Int {
|
||||
_bb.capacity
|
||||
}
|
||||
|
||||
var writerIndex: Int {
|
||||
_bb.writerIndex
|
||||
}
|
||||
|
||||
private var finished = false
|
||||
private var hasDuplicatedKeys = false
|
||||
private var minBitWidth: BitWidth = .w8
|
||||
private var _bb: _InternalByteBuffer
|
||||
private var stack: [Value] = []
|
||||
private var keyPool: [Int: UInt] = [:]
|
||||
private var stringPool: [Int: UInt] = [:]
|
||||
private var flags: BuilderFlag
|
||||
|
||||
public init(initialSize: Int = 1024, flags: BuilderFlag = .shareKeys) {
|
||||
_bb = _InternalByteBuffer(initialSize: initialSize)
|
||||
self.flags = flags
|
||||
}
|
||||
|
||||
/// Returns the written bytes into the ``ByteBuffer``
|
||||
///
|
||||
/// Should only be used after ``finish(offset:addPrefix:)`` is called
|
||||
public var sizedByteArray: [UInt8] {
|
||||
assert(
|
||||
finished == true,
|
||||
"function finish() should be called before accessing data")
|
||||
return _bb.underlyingBytes
|
||||
}
|
||||
|
||||
public var sizedByteBuffer: ByteBuffer {
|
||||
assert(
|
||||
finished == true,
|
||||
"function finish() should be called before accessing data")
|
||||
return _bb.withUnsafeSlicedBytes {
|
||||
ByteBuffer(copyingMemoryBound: $0.baseAddress!, capacity: $0.count)
|
||||
}
|
||||
}
|
||||
|
||||
public var byteBuffer: ByteBuffer {
|
||||
assert(
|
||||
finished == true,
|
||||
"function finish() should be called before accessing data")
|
||||
return ByteBuffer(byteBuffer: _bb)
|
||||
}
|
||||
|
||||
/// Resets the internal state. Automatically called before building a new flexbuffer.
|
||||
public mutating func reset() {
|
||||
_bb.clear()
|
||||
stack.removeAll(keepingCapacity: true)
|
||||
finished = false
|
||||
minBitWidth = .w8
|
||||
keyPool.removeAll()
|
||||
stringPool.removeAll()
|
||||
}
|
||||
|
||||
// MARK: - Storing root
|
||||
@inline(__always)
|
||||
public mutating func finish() {
|
||||
assert(stack.count == 1)
|
||||
|
||||
// Write root value.
|
||||
var byteWidth = align(
|
||||
width: stack[0].elementWidth(
|
||||
size: writerIndex,
|
||||
index: 0))
|
||||
|
||||
write(value: stack[0], byteWidth: byteWidth)
|
||||
var storedType = stack[0].storedPackedType()
|
||||
// Write root type.
|
||||
_bb.writeBytes(&storedType, len: 1)
|
||||
// Write root size. Normally determined by parent, but root has no parent :)
|
||||
_bb.writeBytes(&byteWidth, len: 1)
|
||||
|
||||
finished = true
|
||||
}
|
||||
|
||||
// MARK: - Vector
|
||||
@discardableResult
|
||||
@inline(__always)
|
||||
public func startVector() -> Int {
|
||||
stack.count
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
@inline(__always)
|
||||
public mutating func startVector(key k: String) -> Int {
|
||||
add(key: k)
|
||||
return stack.count
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
@inline(__always)
|
||||
public mutating func endVector(
|
||||
start: Int,
|
||||
typed: Bool = false,
|
||||
fixed: Bool = false) -> UInt64
|
||||
{
|
||||
let vec = createVector(
|
||||
start: start,
|
||||
count: stack.count - start,
|
||||
step: 1,
|
||||
typed: typed,
|
||||
fixed: fixed,
|
||||
keys: nil)
|
||||
stack = Array(stack[..<start])
|
||||
stack.append(vec)
|
||||
return vec.u
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
@inline(__always)
|
||||
public mutating func create<T>(vector: [T]) -> Int where T: Scalar {
|
||||
create(vector: vector, fixed: false)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
@inline(__always)
|
||||
public mutating func create<T>(vector: [T], key: borrowing String) -> Int
|
||||
where T: Scalar
|
||||
{
|
||||
add(key: key)
|
||||
return create(vector: vector, fixed: false)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
@inline(__always)
|
||||
public mutating func createFixed<T>(vector: [T]) -> Int where T: Scalar {
|
||||
assert(vector.count >= 2 && vector.count <= 4)
|
||||
return create(vector: vector, fixed: true)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
@inline(__always)
|
||||
public mutating func createFixed<T>(
|
||||
vector: [T],
|
||||
key: borrowing String) -> Int where T: Scalar
|
||||
{
|
||||
assert(vector.count >= 2 && vector.count <= 4)
|
||||
add(key: key)
|
||||
return create(vector: vector, fixed: true)
|
||||
}
|
||||
|
||||
// MARK: - Map
|
||||
@discardableResult
|
||||
@inline(__always)
|
||||
public func startMap() -> Int {
|
||||
stack.count
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
@inline(__always)
|
||||
public mutating func startMap(key k: String) -> Int {
|
||||
add(key: k)
|
||||
return stack.count
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
@inline(__always)
|
||||
public mutating func endMap(start: Int) -> UInt64 {
|
||||
let len = sortMapByKeys(start: start)
|
||||
|
||||
let keys = createVector(
|
||||
start: start,
|
||||
count: len,
|
||||
step: 2,
|
||||
typed: true,
|
||||
fixed: false)
|
||||
let vec = createVector(
|
||||
start: start + 1,
|
||||
count: len,
|
||||
step: 2,
|
||||
typed: false,
|
||||
fixed: false,
|
||||
keys: keys)
|
||||
stack = Array(stack[..<start])
|
||||
stack.append(vec)
|
||||
return numericCast(vec.u)
|
||||
}
|
||||
|
||||
// MARK: - Write Null
|
||||
|
||||
@inline(__always)
|
||||
public mutating func addNil() {
|
||||
stack.append(Value.nil)
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public mutating func addNil(key: borrowing String) {
|
||||
add(key: key)
|
||||
addNil()
|
||||
}
|
||||
|
||||
// MARK: - Write Bool
|
||||
|
||||
@inline(__always)
|
||||
public mutating func add(bool: borrowing Bool) {
|
||||
stack.append(Value(bool: bool))
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public mutating func add(bool: borrowing Bool, key: borrowing String) {
|
||||
add(key: key)
|
||||
add(bool: bool)
|
||||
}
|
||||
|
||||
// MARK: - Write UInt
|
||||
|
||||
@inline(__always)
|
||||
public mutating func add(uint8 value: UInt8) {
|
||||
add(uint64: numericCast(value))
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public mutating func add(uint8 value: UInt8, key: borrowing String) {
|
||||
add(key: key)
|
||||
add(uint64: numericCast(value))
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public mutating func add(uint16 value: UInt16) {
|
||||
add(uint64: numericCast(value))
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public mutating func add(uint16 value: UInt16, key: borrowing String) {
|
||||
add(key: key)
|
||||
add(uint64: numericCast(value))
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public mutating func add(uint32 value: UInt32) {
|
||||
add(uint64: numericCast(value))
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public mutating func add(uint32 value: UInt32, key: borrowing String) {
|
||||
add(key: key)
|
||||
add(uint64: numericCast(value))
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public mutating func add(uint value: UInt) {
|
||||
add(uint64: numericCast(value))
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public mutating func add(uint value: UInt, key: borrowing String) {
|
||||
add(key: key)
|
||||
add(uint64: numericCast(value))
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public mutating func add(uint64 value: borrowing UInt64) {
|
||||
stack.append(Value(v: value, type: .uint, bitWidth: widthU(value)))
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public mutating func add(
|
||||
uint64 value: borrowing UInt64,
|
||||
key: borrowing String)
|
||||
{
|
||||
add(key: key)
|
||||
add(uint64: value)
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public mutating func indirect(uint64 val: borrowing UInt64) {
|
||||
pushIndirect(value: val, type: .indirectUInt, bitWidth: widthU(val))
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public mutating func indirect(
|
||||
uint64 val: borrowing UInt64,
|
||||
key: borrowing String)
|
||||
{
|
||||
add(key: key)
|
||||
indirect(uint64: val)
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public mutating func indirect(
|
||||
uint val: borrowing UInt,
|
||||
key: borrowing String)
|
||||
{
|
||||
add(key: key)
|
||||
indirect(uint64: numericCast(val))
|
||||
}
|
||||
|
||||
// MARK: - Write Int
|
||||
|
||||
@inline(__always)
|
||||
public mutating func add(int8 value: Int8) {
|
||||
add(int64: numericCast(value))
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public mutating func add(int8 value: Int8, key: borrowing String) {
|
||||
add(key: key)
|
||||
add(int64: numericCast(value))
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public mutating func add(int16 value: Int16) {
|
||||
add(int64: numericCast(value))
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public mutating func add(int16 value: Int16, key: borrowing String) {
|
||||
add(key: key)
|
||||
add(int64: numericCast(value))
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public mutating func add(int32 value: Int32) {
|
||||
add(int64: numericCast(value))
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public mutating func add(int32 value: Int32, key: borrowing String) {
|
||||
add(key: key)
|
||||
add(int64: numericCast(value))
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public mutating func add(int value: Int) {
|
||||
add(int64: numericCast(value))
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public mutating func add(int value: Int, key: borrowing String) {
|
||||
add(key: key)
|
||||
add(int64: numericCast(value))
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public mutating func add(int64 value: borrowing Int64) {
|
||||
stack.append(Value(v: value, type: .int, bitWidth: widthI(value)))
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public mutating func add(
|
||||
int64 value: borrowing Int64,
|
||||
key: borrowing String)
|
||||
{
|
||||
add(key: key)
|
||||
add(int64: value)
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public mutating func indirect(int64 val: borrowing Int64) {
|
||||
pushIndirect(value: val, type: .indirectInt, bitWidth: widthI(val))
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public mutating func indirect(
|
||||
int64 val: borrowing Int64,
|
||||
key: borrowing String)
|
||||
{
|
||||
add(key: key)
|
||||
indirect(int64: val)
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public mutating func indirect(
|
||||
int val: borrowing Int,
|
||||
key: borrowing String)
|
||||
{
|
||||
add(key: key)
|
||||
indirect(int64: numericCast(val))
|
||||
}
|
||||
|
||||
// MARK: - Write Floats
|
||||
|
||||
@inline(__always)
|
||||
public mutating func add(float32 value: borrowing Float32) {
|
||||
stack.append(Value(v: Double(value), type: .float, bitWidth: .w32))
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public mutating func add(
|
||||
float32 value: borrowing Float32,
|
||||
key: borrowing String)
|
||||
{
|
||||
add(key: key)
|
||||
add(float32: value)
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public mutating func indirect(float32 val: borrowing Float32) {
|
||||
pushIndirect(value: val, type: .indirectFloat, bitWidth: .w32)
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public mutating func indirect(
|
||||
float32 val: borrowing Float32,
|
||||
key: borrowing String)
|
||||
{
|
||||
add(key: key)
|
||||
indirect(float32: val)
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public mutating func add(double value: borrowing Double) {
|
||||
stack.append(Value(v: value, type: .float, bitWidth: widthF(value)))
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public mutating func add(
|
||||
double value: borrowing Double,
|
||||
key: borrowing String)
|
||||
{
|
||||
add(key: key)
|
||||
add(double: value)
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public mutating func indirect(double val: borrowing Double) {
|
||||
pushIndirect(value: val, type: .indirectFloat, bitWidth: widthF(val))
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public mutating func indirect(
|
||||
double val: borrowing Double,
|
||||
key: borrowing String)
|
||||
{
|
||||
add(key: key)
|
||||
indirect(double: val)
|
||||
}
|
||||
|
||||
// MARK: - Writing strings
|
||||
@inline(__always)
|
||||
public mutating func add(string: borrowing String, key: borrowing String) {
|
||||
add(key: key)
|
||||
write(str: string, len: string.count)
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public mutating func add(string: borrowing String) {
|
||||
write(str: string, len: string.count)
|
||||
}
|
||||
|
||||
// MARK: - Writing Blobs
|
||||
@discardableResult
|
||||
@inline(__always)
|
||||
public mutating func add<T>(
|
||||
blob: borrowing T,
|
||||
length l: Int) -> UInt where T: ContiguousBytes
|
||||
{
|
||||
storeBlob(blob, len: l, type: .blob)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
@inline(__always)
|
||||
public mutating func add<T>(
|
||||
blob: borrowing T,
|
||||
key: borrowing String,
|
||||
length l: Int) -> UInt where T: ContiguousBytes
|
||||
{
|
||||
add(key: key)
|
||||
return storeBlob(blob, len: l, type: .blob)
|
||||
}
|
||||
|
||||
// MARK: - Reuse Values
|
||||
@inline(__always)
|
||||
public func lastValue() -> Value? {
|
||||
return stack.last
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public mutating func reuse(value: Value) {
|
||||
stack.append(value)
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public mutating func reuse(value: Value, key: borrowing String) {
|
||||
add(key: key)
|
||||
reuse(value: value)
|
||||
}
|
||||
|
||||
// MARK: - Private -
|
||||
|
||||
// MARK: Writing to buffer
|
||||
|
||||
@inline(__always)
|
||||
private mutating func write(value: Value, byteWidth: Int) {
|
||||
switch value.type {
|
||||
case .null, .int: write(value: value.i, byteWidth: byteWidth)
|
||||
case .bool, .uint: write(value: value.u, byteWidth: byteWidth)
|
||||
case .float: write(double: value.f, byteWidth: byteWidth)
|
||||
default:
|
||||
write(offset: value.u, byteWidth: byteWidth)
|
||||
}
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
private mutating func pushIndirect<T>(
|
||||
value: T,
|
||||
type: FlexBufferType,
|
||||
bitWidth: BitWidth)
|
||||
{
|
||||
let byteWidth = align(width: bitWidth)
|
||||
let iloc = writerIndex
|
||||
_bb.ensureSpace(size: byteWidth)
|
||||
|
||||
_bb.write(value, len: byteWidth)
|
||||
stack.append(
|
||||
Value(
|
||||
sloc: .u(numericCast(iloc)),
|
||||
type: type,
|
||||
bitWidth: bitWidth))
|
||||
}
|
||||
|
||||
// MARK: Internal Writing Strings
|
||||
|
||||
/// Adds a string to the buffer using swift.utf8 object
|
||||
/// - Parameter str: String that will be added to the buffer
|
||||
/// - Parameter len: length of the string
|
||||
@discardableResult
|
||||
@inline(__always)
|
||||
private mutating func write(str: borrowing String, len: Int) -> UInt {
|
||||
let resetTo = writerIndex
|
||||
var sloc = str.withCString {
|
||||
storeBlob(pointer: $0, len: len, trailing: 1, type: .string)
|
||||
}
|
||||
|
||||
if flags >= .shareKeysAndStrings {
|
||||
let loc = stringPool[str.hashValue]
|
||||
if let loc {
|
||||
_bb.resetWriter(to: resetTo)
|
||||
sloc = loc
|
||||
assert(
|
||||
stack.count > 0,
|
||||
"Attempting to override the location, but stack is empty")
|
||||
stack[stack.count - 1].sloc = .u(numericCast(sloc))
|
||||
} else {
|
||||
stringPool[str.hashValue] = sloc
|
||||
}
|
||||
}
|
||||
return sloc
|
||||
}
|
||||
|
||||
// MARK: Write Keys
|
||||
@discardableResult
|
||||
@inline(__always)
|
||||
private mutating func add(key: borrowing String) -> UInt {
|
||||
add(key: key, len: key.count)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
@inline(__always)
|
||||
private mutating func add(key: borrowing String, len: Int) -> UInt {
|
||||
_bb.ensureSpace(size: len)
|
||||
|
||||
var sloc: UInt = numericCast(writerIndex)
|
||||
key.withCString {
|
||||
_bb.writeBytes($0, len: len + 1)
|
||||
}
|
||||
|
||||
if flags > .shareKeys {
|
||||
let loc = keyPool[key.hashValue]
|
||||
if let loc {
|
||||
_bb.resetWriter(to: Int(sloc))
|
||||
sloc = loc
|
||||
} else {
|
||||
keyPool[key.hashValue] = sloc
|
||||
}
|
||||
}
|
||||
stack.append(Value(sloc: .u(numericCast(sloc)), type: .key, bitWidth: .w8))
|
||||
return sloc
|
||||
}
|
||||
|
||||
// MARK: - Storing Blobs
|
||||
@inline(__always)
|
||||
private mutating func storeBlob<T>(
|
||||
_ bytes: T,
|
||||
len: Int,
|
||||
type: FlexBufferType) -> UInt where T: ContiguousBytes
|
||||
{
|
||||
return bytes.withUnsafeBytes {
|
||||
storeBlob(pointer: $0.baseAddress!, len: len, type: type)
|
||||
}
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
@usableFromInline
|
||||
@inline(__always)
|
||||
mutating func storeBlob(
|
||||
pointer: borrowing UnsafeRawPointer,
|
||||
len: Int,
|
||||
trailing: Int = 0,
|
||||
type: FlexBufferType) -> UInt
|
||||
{
|
||||
_bb.ensureSpace(size: len &+ trailing)
|
||||
let bitWidth = widthU(numericCast(len))
|
||||
|
||||
let bytes = align(width: bitWidth)
|
||||
|
||||
var len = len
|
||||
_bb.writeBytes(&len, len: bytes)
|
||||
let sloc = writerIndex
|
||||
|
||||
_bb.writeBytes(pointer, len: len &+ trailing)
|
||||
stack.append(
|
||||
Value(
|
||||
sloc: .u(numericCast(sloc)),
|
||||
type: type,
|
||||
bitWidth: bitWidth))
|
||||
return numericCast(sloc)
|
||||
}
|
||||
|
||||
// MARK: Write Vectors
|
||||
@discardableResult
|
||||
@inline(__always)
|
||||
private mutating func create<T>(vector: [T], fixed: Bool) -> Int
|
||||
where T: Scalar
|
||||
{
|
||||
let length: UInt64 = numericCast(vector.count)
|
||||
let vectorType = getScalarType(type: T.self)
|
||||
let byteWidth = MemoryLayout<T>.size
|
||||
let bitWidth = BitWidth.widthB(byteWidth)
|
||||
|
||||
_bb.ensureSpace(size: vector.count &* Int(bitWidth.rawValue))
|
||||
|
||||
assert(widthU(length) <= bitWidth)
|
||||
|
||||
align(width: bitWidth)
|
||||
|
||||
if !fixed {
|
||||
write(value: length, byteWidth: byteWidth)
|
||||
}
|
||||
let vloc = _bb.writerIndex
|
||||
|
||||
for i in stride(from: 0, to: vector.count, by: 1) {
|
||||
write(value: vector[i], byteWidth: byteWidth)
|
||||
}
|
||||
|
||||
stack.append(
|
||||
Value(
|
||||
sloc: .u(numericCast(vloc)),
|
||||
type: toTypedVector(type: vectorType, length: fixed ? length : 0),
|
||||
bitWidth: bitWidth))
|
||||
return vloc
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
private mutating func createVector(
|
||||
start: Int,
|
||||
count: Int,
|
||||
step: Int,
|
||||
typed: Bool,
|
||||
fixed: Bool,
|
||||
keys: Value? = nil) -> Value
|
||||
{
|
||||
assert(
|
||||
!fixed || typed,
|
||||
"Typed false and fixed true is a combination not supported currently")
|
||||
|
||||
var bitWidth = BitWidth.max(minBitWidth, rhs: widthU(numericCast(count)))
|
||||
var prefixElements = 1
|
||||
if keys != nil {
|
||||
/// If this vector is part of a map, we will pre-fix an offset to the keys
|
||||
/// to this vector.
|
||||
bitWidth = max(bitWidth, keys!.elementWidth(size: writerIndex, index: 0))
|
||||
prefixElements += 2
|
||||
}
|
||||
var vectorType: FlexBufferType = .key
|
||||
|
||||
for i in stride(from: start, to: stack.count, by: step) {
|
||||
let elemWidth = stack[i].elementWidth(
|
||||
size: _bb.writerIndex,
|
||||
index: numericCast(i &- start &+ prefixElements))
|
||||
bitWidth = BitWidth.max(bitWidth, rhs: elemWidth)
|
||||
guard typed else { continue }
|
||||
if i == start {
|
||||
vectorType = stack[i].type
|
||||
} else {
|
||||
assert(
|
||||
vectorType == stack[i].type,
|
||||
"""
|
||||
If you get this assert you are writing a typed vector
|
||||
with elements that are not all the same type
|
||||
""")
|
||||
}
|
||||
}
|
||||
assert(
|
||||
!typed || isTypedVectorElementType(type: vectorType),
|
||||
"""
|
||||
If you get this assert, your typed types are not one of:
|
||||
Int / UInt / Float / Key.
|
||||
""")
|
||||
|
||||
let byteWidth = align(width: bitWidth)
|
||||
|
||||
let currentSize: Int = count &* step &* byteWidth
|
||||
let requiredSize: Int = if !typed {
|
||||
// We ensure that we have enough space
|
||||
// for loop two write operations &
|
||||
// 24 bytes for when its not fixed,
|
||||
// and keys isn't null. As an extra safe
|
||||
// guard
|
||||
(currentSize &* 2) &+ twentyFourBytes
|
||||
} else {
|
||||
currentSize
|
||||
}
|
||||
|
||||
_bb.ensureSpace(
|
||||
size: requiredSize)
|
||||
|
||||
if keys != nil {
|
||||
write(offset: keys!.u, byteWidth: byteWidth)
|
||||
write(value: UInt64.one << keys!.bitWidth.rawValue, byteWidth: byteWidth)
|
||||
}
|
||||
|
||||
if !fixed {
|
||||
write(value: count, byteWidth: byteWidth)
|
||||
}
|
||||
|
||||
let vloc = _bb.writerIndex
|
||||
|
||||
for i in stride(from: start, to: stack.count, by: step) {
|
||||
write(value: stack[i], byteWidth: byteWidth)
|
||||
}
|
||||
|
||||
if !typed {
|
||||
for i in stride(from: start, to: stack.count, by: step) {
|
||||
_bb.write(stack[i].storedPackedType(width: bitWidth), len: 1)
|
||||
}
|
||||
}
|
||||
|
||||
let type: FlexBufferType =
|
||||
if keys != nil {
|
||||
.map
|
||||
} else if typed {
|
||||
toTypedVector(type: vectorType, length: numericCast(fixed ? count : 0))
|
||||
} else {
|
||||
.vector
|
||||
}
|
||||
|
||||
return Value(sloc: .u(numericCast(vloc)), type: type, bitWidth: bitWidth)
|
||||
}
|
||||
|
||||
// MARK: Write Scalar functions
|
||||
@inline(__always)
|
||||
private mutating func write(offset: UInt64, byteWidth: Int) {
|
||||
let offset: UInt64 = numericCast(writerIndex) &- offset
|
||||
assert(byteWidth == 8 || offset < UInt64.one << (byteWidth * 8))
|
||||
withUnsafePointer(to: offset) {
|
||||
_bb.writeBytes($0, len: byteWidth)
|
||||
}
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
private mutating func write<T>(value: T, byteWidth: Int) where T: Scalar {
|
||||
withUnsafePointer(to: value) {
|
||||
_bb.writeBytes($0, len: byteWidth)
|
||||
}
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
private mutating func write(double value: Double, byteWidth: Int) {
|
||||
switch byteWidth {
|
||||
case 8: write(value: value, byteWidth: byteWidth)
|
||||
case 4: write(value: Float(value), byteWidth: byteWidth)
|
||||
default: assert(false, "Should never reach here")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Misc functions
|
||||
@discardableResult
|
||||
@inline(__always)
|
||||
private mutating func align(width: BitWidth) -> Int {
|
||||
let bytes: Int = numericCast(UInt32.one << width.rawValue)
|
||||
_bb.addPadding(bytes: bytes)
|
||||
return bytes
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
private mutating func sortMapByKeys(start: Int) -> Int {
|
||||
let len = mapElementCount(start: start)
|
||||
for index in stride(from: start, to: stack.count, by: 2) {
|
||||
assert(stack[index].type == .key)
|
||||
}
|
||||
|
||||
struct TwoValue: Equatable {
|
||||
let key, value: Value
|
||||
}
|
||||
|
||||
stack[start...].withUnsafeMutableBytes { buffer in
|
||||
var ptr = buffer.assumingMemoryBound(to: TwoValue.self)
|
||||
ptr.sort { a, b in
|
||||
let aMem = _bb.memory.advanced(by: numericCast(a.key.u))
|
||||
.assumingMemoryBound(to: CChar.self)
|
||||
let bMem = _bb.memory.advanced(by: numericCast(b.key.u))
|
||||
.assumingMemoryBound(to: CChar.self)
|
||||
let comp = strcmp(aMem, bMem)
|
||||
if (comp == 0) && a != b { hasDuplicatedKeys = true }
|
||||
return comp < 0
|
||||
}
|
||||
}
|
||||
return len
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
private func mapElementCount(start: Int) -> Int {
|
||||
var len = stack.count - start
|
||||
assert((len & 1) == 0)
|
||||
len /= 2
|
||||
return len
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Vectors helper functions
|
||||
extension FlexBuffersWriter {
|
||||
@discardableResult
|
||||
@inline(__always)
|
||||
public mutating func vector(
|
||||
key: String,
|
||||
_ closure: @escaping FlexBuffersWriterBuilder) -> UInt64
|
||||
{
|
||||
let start = startVector(key: key)
|
||||
closure(&self)
|
||||
return endVector(start: start)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
@inline(__always)
|
||||
public mutating func vector(_ closure: @escaping FlexBuffersWriterBuilder)
|
||||
-> UInt64
|
||||
{
|
||||
let start = startVector()
|
||||
closure(&self)
|
||||
return endVector(start: start)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Maps helper functions
|
||||
extension FlexBuffersWriter {
|
||||
@discardableResult
|
||||
@inline(__always)
|
||||
public mutating func map(
|
||||
key: String,
|
||||
_ closure: @escaping FlexBuffersWriterBuilder) -> UInt64
|
||||
{
|
||||
let start = startMap(key: key)
|
||||
closure(&self)
|
||||
return endMap(start: start)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
@inline(__always)
|
||||
public mutating func map(_ closure: @escaping FlexBuffersWriterBuilder)
|
||||
-> UInt64
|
||||
{
|
||||
let start = startMap()
|
||||
closure(&self)
|
||||
return endMap(start: start)
|
||||
}
|
||||
}
|
||||
222
swift/Sources/FlexBuffers/_InternalByteBuffer.swift
Normal file
222
swift/Sources/FlexBuffers/_InternalByteBuffer.swift
Normal file
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
* Copyright 2024 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
|
||||
|
||||
/// `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
|
||||
@usableFromInline
|
||||
struct _InternalByteBuffer {
|
||||
|
||||
/// Storage is a container that would hold the memory pointer to solve the issue of
|
||||
/// deallocating the memory that was held by (memory: UnsafeMutableRawPointer)
|
||||
@usableFromInline
|
||||
final class Storage {
|
||||
// This storage doesn't own the memory, therefore, we won't deallocate on deinit.
|
||||
private let unowned: Bool
|
||||
/// pointer to the start of the buffer object in memory
|
||||
var memory: UnsafeMutableRawPointer
|
||||
/// Capacity of UInt8 the buffer can hold
|
||||
var capacity: Int
|
||||
|
||||
@usableFromInline
|
||||
init(count: Int, alignment: Int) {
|
||||
memory = UnsafeMutableRawPointer.allocate(
|
||||
byteCount: count,
|
||||
alignment: alignment)
|
||||
capacity = count
|
||||
unowned = false
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
init(memory: UnsafeMutableRawPointer, capacity: Int, unowned: Bool) {
|
||||
self.memory = memory
|
||||
self.capacity = capacity
|
||||
self.unowned = unowned
|
||||
}
|
||||
|
||||
deinit {
|
||||
if !unowned {
|
||||
memory.deallocate()
|
||||
}
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
func copy(from ptr: UnsafeRawPointer, count: Int) {
|
||||
assert(
|
||||
!unowned,
|
||||
"copy should NOT be called on a buffer that is built by assumingMemoryBound")
|
||||
memory.copyMemory(from: ptr, byteCount: count)
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
func initialize(for size: Int) {
|
||||
assert(
|
||||
!unowned,
|
||||
"initalize should NOT be called on a buffer that is built by assumingMemoryBound")
|
||||
memset(memory, 0, size)
|
||||
}
|
||||
|
||||
/// Reallocates the buffer incase the object to be written doesnt fit in the current buffer
|
||||
/// - Parameter size: Size of the current object
|
||||
@usableFromInline
|
||||
func reallocate(_ size: Int, writerSize: Int, alignment: Int) {
|
||||
while capacity <= writerSize &+ size {
|
||||
capacity = capacity << 1
|
||||
}
|
||||
|
||||
/// solution take from Apple-NIO
|
||||
capacity = capacity.convertToPowerofTwo
|
||||
|
||||
let newData = UnsafeMutableRawPointer.allocate(
|
||||
byteCount: capacity,
|
||||
alignment: alignment)
|
||||
memset(newData, 0, capacity &- writerSize)
|
||||
memcpy(
|
||||
newData,
|
||||
memory,
|
||||
writerSize)
|
||||
memory.deallocate()
|
||||
memory = newData
|
||||
}
|
||||
}
|
||||
|
||||
@usableFromInline var _storage: Storage
|
||||
/// The size of the elements written to the buffer + their paddings
|
||||
var writerIndex: Int = 0
|
||||
/// Alignment of the current memory being written to the buffer
|
||||
private var alignment = 1
|
||||
/// Public Pointer to the buffer object in memory. This should NOT be modified for any reason
|
||||
public var memory: UnsafeMutableRawPointer { _storage.memory }
|
||||
/// Current capacity for the buffer
|
||||
public var capacity: Int { _storage.capacity }
|
||||
|
||||
/// Returns the written bytes into the ``ByteBuffer``
|
||||
public var underlyingBytes: [UInt8] {
|
||||
let start = memory.bindMemory(to: UInt8.self, capacity: writerIndex)
|
||||
|
||||
let ptr = UnsafeBufferPointer<UInt8>(start: start, count: writerIndex)
|
||||
return Array(ptr)
|
||||
}
|
||||
|
||||
/// Constructor that creates a Flatbuffer instance with a size
|
||||
/// - Parameter:
|
||||
/// - size: Length of the buffer
|
||||
/// - allowReadingUnalignedBuffers: allow reading from unaligned buffer
|
||||
init(initialSize size: Int) {
|
||||
let size = size.convertToPowerofTwo
|
||||
_storage = Storage(count: size, alignment: alignment)
|
||||
_storage.initialize(for: size)
|
||||
}
|
||||
|
||||
/// Clears the current instance of the buffer, replacing it with new memory
|
||||
@inline(__always)
|
||||
mutating public func clear() {
|
||||
writerIndex = 0
|
||||
alignment = 1
|
||||
_storage.initialize(for: _storage.capacity)
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
mutating public func resetWriter(to writer: Int) {
|
||||
writerIndex = writer
|
||||
}
|
||||
|
||||
/// Makes sure that buffer has enouch space for each of the objects that will be written into it
|
||||
/// - Parameter size: size of object
|
||||
@inline(__always)
|
||||
mutating func ensureSpace(size: Int) {
|
||||
guard size &+ writerIndex > _storage.capacity else { return }
|
||||
_storage.reallocate(size, writerSize: writerIndex, alignment: alignment)
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
mutating func addPadding(bytes: Int) {
|
||||
writerIndex = writerIndex &+ numericCast(padding(
|
||||
bufSize: numericCast(writerIndex),
|
||||
elementSize: numericCast(bytes)))
|
||||
ensureSpace(size: writerIndex)
|
||||
}
|
||||
|
||||
mutating func writeBytes(_ ptr: UnsafeRawPointer, len: Int) {
|
||||
memcpy(
|
||||
_storage.memory.advanced(by: writerIndex),
|
||||
ptr,
|
||||
len)
|
||||
writerIndex = writerIndex &+ len
|
||||
}
|
||||
|
||||
mutating func write<T>(_ v: T, len: Int) {
|
||||
withUnsafePointer(to: v) {
|
||||
memcpy(
|
||||
_storage.memory.advanced(by: writerIndex),
|
||||
$0,
|
||||
len)
|
||||
writerIndex = writerIndex &+ len
|
||||
}
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
@inline(__always)
|
||||
func withUnsafeBytes<T>(
|
||||
_ body: (UnsafeRawBufferPointer) throws
|
||||
-> T) rethrows -> T
|
||||
{
|
||||
try body(UnsafeRawBufferPointer(
|
||||
start: _storage.memory,
|
||||
count: capacity))
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
@inline(__always)
|
||||
func withUnsafeSlicedBytes<T>(
|
||||
_ body: (UnsafeRawBufferPointer) throws
|
||||
-> T) rethrows -> T
|
||||
{
|
||||
try body(UnsafeRawBufferPointer(
|
||||
start: _storage.memory,
|
||||
count: writerIndex))
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
@inline(__always)
|
||||
func withUnsafeRawPointer<T>(
|
||||
_ body: (UnsafeMutableRawPointer) throws
|
||||
-> T) rethrows -> T
|
||||
{
|
||||
try body(_storage.memory)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
@inline(__always)
|
||||
func readWithUnsafeRawPointer<T>(
|
||||
position: Int,
|
||||
_ body: (UnsafeRawPointer) throws -> T) rethrows -> T
|
||||
{
|
||||
try body(_storage.memory.advanced(by: position))
|
||||
}
|
||||
}
|
||||
|
||||
extension _InternalByteBuffer: CustomDebugStringConvertible {
|
||||
|
||||
public var debugDescription: String {
|
||||
"""
|
||||
buffer located at: \(_storage.memory), with capacity of \(_storage.capacity)
|
||||
{ writerIndex: \(writerIndex) }
|
||||
"""
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ import PackageDescription
|
||||
let package = Package(
|
||||
name: "FlatBuffers.Test.Swift",
|
||||
platforms: [
|
||||
.iOS(.v11),
|
||||
.iOS(.v12),
|
||||
.macOS(.v10_14),
|
||||
],
|
||||
dependencies: [
|
||||
@@ -29,7 +29,9 @@ let package = Package(
|
||||
// Prevent the build system from pulling 2.29.1 to prevent Swift 5.8 build breaks.
|
||||
// The patch update introduced code that uses "switch expression syntax" that wasn't valid until Swift 5.9 [1].
|
||||
// [1] https://github.com/swiftlang/swift-evolution/blob/main/proposals/0380-if-switch-expressions.md
|
||||
.package(url: "https://github.com/apple/swift-nio-ssl.git", exact: "2.29.0"),
|
||||
.package(
|
||||
url: "https://github.com/apple/swift-nio-ssl.git",
|
||||
exact: "2.29.0"),
|
||||
],
|
||||
targets: [
|
||||
.executableTarget(
|
||||
@@ -43,4 +45,9 @@ let package = Package(
|
||||
.product(name: "FlatBuffers", package: "flatbuffers"),
|
||||
.product(name: "GRPC", package: "grpc-swift"),
|
||||
]),
|
||||
.testTarget(
|
||||
name: "FlexBuffers.Test.SwiftTests",
|
||||
dependencies: [
|
||||
.product(name: "FlexBuffers", package: "flatbuffers"),
|
||||
]),
|
||||
])
|
||||
|
||||
@@ -49,7 +49,7 @@ class FlatBuffersMonsterWriterTests: XCTestCase {
|
||||
XCTAssertEqual(bytes.sizedByteArray, [48, 0, 0, 0, 77, 79, 78, 83, 0, 0, 0, 0, 36, 0, 72, 0, 40, 0, 0, 0, 38, 0, 32, 0, 0, 0, 28, 0, 0, 0, 27, 0, 20, 0, 16, 0, 12, 0, 4, 0, 0, 0, 0, 0, 0, 0, 11, 0, 36, 0, 0, 0, 164, 0, 0, 0, 0, 0, 0, 1, 60, 0, 0, 0, 68, 0, 0, 0, 76, 0, 0, 0, 0, 0, 0, 1, 88, 0, 0, 0, 120, 0, 0, 0, 0, 0, 80, 0, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 64, 2, 0, 5, 0, 6, 0, 0, 0, 2, 0, 0, 0, 64, 0, 0, 0, 48, 0, 0, 0, 2, 0, 0, 0, 30, 0, 40, 0, 10, 0, 20, 0, 152, 255, 255, 255, 4, 0, 0, 0, 4, 0, 0, 0, 70, 114, 101, 100, 0, 0, 0, 0, 5, 0, 0, 0, 0, 1, 2, 3, 4, 0, 0, 0, 5, 0, 0, 0, 116, 101, 115, 116, 50, 0, 0, 0, 5, 0, 0, 0, 116, 101, 115, 116, 49, 0, 0, 0, 9, 0, 0, 0, 77, 121, 77, 111, 110, 115, 116, 101, 114, 0, 0, 0, 3, 0, 0, 0, 20, 0, 0, 0, 36, 0, 0, 0, 4, 0, 0, 0, 240, 255, 255, 255, 32, 0, 0, 0, 248, 255, 255, 255, 36, 0, 0, 0, 12, 0, 8, 0, 0, 0, 0, 0, 0, 0, 4, 0, 12, 0, 0, 0, 28, 0, 0, 0, 5, 0, 0, 0, 87, 105, 108, 109, 97, 0, 0, 0, 6, 0, 0, 0, 66, 97, 114, 110, 101, 121, 0, 0, 5, 0, 0, 0, 70, 114, 111, 100, 111, 0, 0, 0])
|
||||
// swiftformat:enable all
|
||||
var buffer = bytes.buffer
|
||||
print(buffer)
|
||||
|
||||
let monster: MyGame_Example_Monster = getRoot(byteBuffer: &buffer)
|
||||
readMonster(monster: monster)
|
||||
mutateMonster(fb: bytes.buffer)
|
||||
@@ -318,7 +318,7 @@ class FlatBuffersMonsterWriterTests: XCTestCase {
|
||||
|
||||
func mutateMonster(fb: ByteBuffer) {
|
||||
var fb = fb
|
||||
print(fb)
|
||||
|
||||
let monster: Monster = getRoot(byteBuffer: &fb)
|
||||
XCTAssertFalse(monster.mutate(mana: 10))
|
||||
XCTAssertEqual(monster.testarrayoftables(at: 0)?.name, "Barney")
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
import XCTest
|
||||
@testable import Common
|
||||
@testable import FlatBuffers
|
||||
|
||||
final class FlatBuffersTests: XCTestCase {
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2024 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 Common
|
||||
import FlexBuffers
|
||||
import XCTest
|
||||
|
||||
final class FlexBuffersJSONTests: XCTestCase {
|
||||
func testEncodingJSON() throws {
|
||||
let buf: ByteBuffer = createProperBuffer().sizedByteBuffer
|
||||
let reference = try getRoot(buffer: buf)!
|
||||
|
||||
let json = reference.jsonString()
|
||||
// swiftformat:disable all
|
||||
XCTAssertEqual(
|
||||
json,
|
||||
"{\"bar\": [1, 2, 3], \"bar3\": [1, 2, 3], \"bool\": true, \"bools\": [true, false, true, false], \"foo\": 100.0, \"mymap\": {\"foo\": \"Fred\"}, \"vec\": [-100, \"Fred\", 4.0, \"M\", false, 4.0]}"
|
||||
)
|
||||
// swiftformat:enable all
|
||||
|
||||
let data = json.data(using: .utf8)!
|
||||
let decodedData = try JSONSerialization.jsonObject(
|
||||
with: data,
|
||||
options: []) as! [String: Any]
|
||||
|
||||
XCTAssertEqual(decodedData["bar"] as! [Int], [1, 2, 3])
|
||||
XCTAssertEqual(decodedData["bar3"] as! [Int], [1, 2, 3])
|
||||
|
||||
let vec: [Any] = decodedData["vec"] as! [Any]
|
||||
XCTAssertEqual(vec[0] as! Int, -100)
|
||||
XCTAssertEqual(vec[1] as! String, "Fred")
|
||||
XCTAssertEqual(vec[2] as! Double, 4.0)
|
||||
XCTAssertEqual(vec[3] as! String, "M")
|
||||
XCTAssertEqual(vec[4] as! Bool, false)
|
||||
XCTAssertEqual(vec[5] as! Double, 4.0)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright 2024 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 Common
|
||||
import FlexBuffers
|
||||
import XCTest
|
||||
|
||||
final class FlexBuffersReaderTests: XCTestCase {
|
||||
|
||||
func testReadingProperBuffer() throws {
|
||||
let buf: ByteBuffer = createProperBuffer().byteBuffer
|
||||
try validate(buffer: buf)
|
||||
}
|
||||
|
||||
func testReadingSizedBuffer() throws {
|
||||
let buf: ByteBuffer = createSizedBuffer()
|
||||
try validate(buffer: buf)
|
||||
}
|
||||
|
||||
private func validate(buffer buf: ByteBuffer) throws {
|
||||
let reference = try getRoot(buffer: buf)!
|
||||
XCTAssertEqual(reference.type, .map)
|
||||
let map = reference.map!
|
||||
XCTAssertEqual(map.count, 7)
|
||||
let vecRef = map["vec"]!
|
||||
XCTAssertEqual(vecRef.type, .vector)
|
||||
let vec = vecRef.vector!
|
||||
XCTAssertEqual(vec.count, 6)
|
||||
XCTAssertEqual(vec[0]?.type, .int)
|
||||
XCTAssertEqual(vec[0]?.int, -100)
|
||||
XCTAssertEqual(vec[1]?.type, .string)
|
||||
XCTAssertEqual(vec[1]?.cString, "Fred")
|
||||
XCTAssertNil(vec[1]?.int)
|
||||
XCTAssertEqual(vec[2]?.double, 4.0)
|
||||
XCTAssertTrue(vec[3]?.type == .blob)
|
||||
|
||||
let blob = vec[3]!.blob { pointer in
|
||||
Array(pointer)
|
||||
}
|
||||
|
||||
XCTAssertEqual(blob?.count, 1)
|
||||
XCTAssertEqual(blob?[0], 77)
|
||||
XCTAssertEqual(vec[4]?.type, .bool)
|
||||
XCTAssertEqual(vec[4]?.bool, false)
|
||||
XCTAssertEqual(vec[5]?.double, 4.0) // Shared with vec[2]
|
||||
|
||||
let barVec = map["bar"]!.typedVector!
|
||||
XCTAssertEqual(barVec.count, 3)
|
||||
XCTAssertEqual(barVec[2]?.int, 3)
|
||||
XCTAssertEqual(barVec[2]?.asInt(), UInt8(3))
|
||||
|
||||
let fixedVec = map["bar3"]!.fixedTypedVector!
|
||||
XCTAssertEqual(fixedVec.count, 3)
|
||||
XCTAssertEqual(fixedVec[2]?.int, 3)
|
||||
XCTAssertEqual(fixedVec[2]?.asInt(), UInt8(3))
|
||||
XCTAssertEqual(map["bool"]?.bool, true)
|
||||
|
||||
let boolsVector = map["bools"]!.typedVector!
|
||||
XCTAssertEqual(boolsVector.type, .bool)
|
||||
XCTAssertEqual(boolsVector[0]?.bool, true)
|
||||
XCTAssertEqual(boolsVector[1]?.bool, false)
|
||||
|
||||
let bools = [true, false, true, false]
|
||||
boolsVector.withUnsafeRawBufferPointer { buff in
|
||||
for i in 0..<boolsVector.count {
|
||||
XCTAssertEqual(buff.load(fromByteOffset: i, as: Bool.self), bools[i])
|
||||
}
|
||||
}
|
||||
XCTAssertEqual(map["foo"]?.double, 100)
|
||||
XCTAssertNil(map["unknown"])
|
||||
let mymap = map["mymap"]?.map
|
||||
|
||||
// Check if both addresses used are the same for keys and strings
|
||||
XCTAssertEqual(mymap?.keys[0]?.cString, map.keys[4]?.cString)
|
||||
map.keys[4]?.withUnsafeRawPointer { pointer in
|
||||
mymap?.keys[0]?.withUnsafeRawPointer { mymapPointer in
|
||||
XCTAssertEqual(pointer, mymapPointer)
|
||||
}
|
||||
}
|
||||
|
||||
XCTAssertEqual(mymap?.values[0]?.cString, vec[1]?.cString)
|
||||
vec[1]?.withUnsafeRawPointer { pointer in
|
||||
mymap?.values[0]?.withUnsafeRawPointer { mymapPointer in
|
||||
XCTAssertEqual(pointer, mymapPointer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var path: String {
|
||||
#if os(macOS)
|
||||
// Gets the current path of this test file then
|
||||
// strips out the nested directories.
|
||||
let filePath = URL(filePath: #file)
|
||||
.deletingLastPathComponent()
|
||||
.deletingLastPathComponent()
|
||||
.deletingLastPathComponent()
|
||||
return filePath.absoluteString
|
||||
#else
|
||||
return FileManager.default.currentDirectoryPath
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2024 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 Common
|
||||
import FlexBuffers
|
||||
import XCTest
|
||||
|
||||
final class FlexBuffersStringTests: XCTestCase {
|
||||
|
||||
func testEncodingUnicodeString() {
|
||||
let text = "プ画をみて✋"
|
||||
|
||||
let bytes = text.data(using: .unicode, allowLossyConversion: true)
|
||||
var flx = FlexBuffersWriter()
|
||||
flx.map { writer in
|
||||
writer.add(blob: bytes!, key: "text", length: bytes!.count)
|
||||
}
|
||||
flx.finish()
|
||||
let byteBuffer = flx.sizedByteBuffer
|
||||
|
||||
let reference = try! getRoot(buffer: byteBuffer)
|
||||
let root = reference?.map?["text"]
|
||||
let builtString = root?.blob {
|
||||
let data = Data(bytes: $0.baseAddress!, count: Int($0.count))
|
||||
return String(data: data, encoding: .unicode)
|
||||
}
|
||||
|
||||
XCTAssertEqual(builtString, text)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,236 @@
|
||||
/*
|
||||
* Copyright 2024 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 Common
|
||||
import FlexBuffers
|
||||
import XCTest
|
||||
|
||||
final class FlexBuffersWriterTests: XCTestCase {
|
||||
func testDeallocation() {
|
||||
let buf: ByteBuffer = {
|
||||
var fbx = FlexBuffersWriter()
|
||||
fbx.add(string: "Hello")
|
||||
fbx.finish()
|
||||
return fbx.sizedByteBuffer
|
||||
}()
|
||||
|
||||
buf.withUnsafeBytes {
|
||||
XCTAssertEqual(
|
||||
Array($0),
|
||||
[5, 72, 101, 108, 108, 111, 0, 6, 20, 1])
|
||||
}
|
||||
}
|
||||
|
||||
func testAddingVectorOfScalars() {
|
||||
var fbx = FlexBuffersWriter()
|
||||
fbx.vector {
|
||||
let arr: [Int32] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 20]
|
||||
$0.create(vector: arr)
|
||||
}
|
||||
fbx.finish()
|
||||
let buf: ByteBuffer = fbx.sizedByteBuffer
|
||||
|
||||
buf.withUnsafeBytes {
|
||||
// swiftformat:disable all
|
||||
XCTAssertEqual(
|
||||
Array($0),
|
||||
[10, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, 6, 0, 0, 0, 7, 0, 0, 0, 8, 0, 0, 0, 9, 0, 0, 0, 20, 0, 0, 0, 1, 41, 46, 2, 40, 1])
|
||||
// swiftformat:enable all
|
||||
}
|
||||
}
|
||||
|
||||
func testAddingVectorOfUnsignedScalars() {
|
||||
var fbx = FlexBuffersWriter()
|
||||
fbx.vector {
|
||||
let arr: [UInt] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 20]
|
||||
$0.create(vector: arr)
|
||||
}
|
||||
fbx.finish()
|
||||
let buf: ByteBuffer = fbx.sizedByteBuffer
|
||||
|
||||
buf.withUnsafeBytes {
|
||||
// swiftformat:disable all
|
||||
XCTAssertEqual(
|
||||
Array($0),
|
||||
[10, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 1, 81, 51, 2, 40, 1])
|
||||
// swiftformat:enable all
|
||||
}
|
||||
}
|
||||
|
||||
func testAddingVectorOfBools() {
|
||||
var fbx = FlexBuffersWriter()
|
||||
fbx.vector {
|
||||
let arr: [Bool] = [true, false, true, false]
|
||||
$0.create(vector: arr)
|
||||
}
|
||||
fbx.finish()
|
||||
let buf: ByteBuffer = fbx.sizedByteBuffer
|
||||
|
||||
buf.withUnsafeBytes {
|
||||
// swiftformat:disable all
|
||||
XCTAssertEqual(
|
||||
Array($0),
|
||||
[4, 1, 0, 1, 0, 1, 5, 144, 2, 40, 1])
|
||||
// swiftformat:enable all
|
||||
}
|
||||
}
|
||||
|
||||
func testSortingWithinMap() {
|
||||
var fbx = FlexBuffersWriter()
|
||||
fbx.map {
|
||||
$0.add(bool: false, key: "bool2")
|
||||
$0.add(bool: true, key: "bool1")
|
||||
}
|
||||
fbx.finish()
|
||||
let buf: ByteBuffer = fbx.sizedByteBuffer
|
||||
buf.withUnsafeBytes {
|
||||
// swiftformat:disable all
|
||||
XCTAssertEqual(
|
||||
Array($0),
|
||||
[98, 111, 111, 108, 50, 0, 98, 111, 111, 108, 49, 0, 2, 7, 14, 2, 1, 2, 1, 0, 104, 104, 4, 36, 1]
|
||||
)
|
||||
// swiftformat:enable all
|
||||
}
|
||||
}
|
||||
|
||||
func testSharingKeyWithinMap() {
|
||||
var fbx = FlexBuffersWriter(initialSize: 1000, flags: .shareKeysAndStrings)
|
||||
fbx.map {
|
||||
$0.add(string: "welcome", key: "welcome")
|
||||
$0.add(string: "welcome", key: "welcome")
|
||||
$0.add(string: "welcome", key: "welcome")
|
||||
}
|
||||
fbx.finish()
|
||||
let buf: ByteBuffer = fbx.sizedByteBuffer
|
||||
buf.withUnsafeBytes {
|
||||
// swiftformat:disable all
|
||||
XCTAssertEqual(
|
||||
Array($0),
|
||||
[119, 101, 108, 99, 111, 109, 101, 0, 7, 119, 101, 108, 99, 111, 109, 101, 0, 3, 18, 19, 20, 3, 1, 3, 15, 16, 17, 20, 20, 20, 6, 36, 1]
|
||||
)
|
||||
// swiftformat:enable all
|
||||
}
|
||||
}
|
||||
|
||||
func testNestingVectorInMap() {
|
||||
let buf: ByteBuffer = createSizedBuffer()
|
||||
|
||||
buf.withUnsafeBytes {
|
||||
// swiftformat:disable all
|
||||
XCTAssertEqual(
|
||||
Array($0),
|
||||
flexbufferGolden
|
||||
)
|
||||
// swiftformat:enable all
|
||||
}
|
||||
}
|
||||
|
||||
func testAddingNil() {
|
||||
var fbx = FlexBuffersWriter(
|
||||
initialSize: 8,
|
||||
flags: .shareKeysAndStrings)
|
||||
|
||||
fbx.map { map in
|
||||
map.addNil(key: "v")
|
||||
}
|
||||
|
||||
fbx.finish()
|
||||
let buf: ByteBuffer = fbx.sizedByteBuffer
|
||||
buf.withUnsafeBytes {
|
||||
// swiftformat:disable all
|
||||
XCTAssertEqual(
|
||||
Array($0),
|
||||
[118, 0, 1, 3, 1, 1, 1, 0, 0, 2, 36, 1]
|
||||
)
|
||||
// swiftformat:enable all
|
||||
}
|
||||
}
|
||||
|
||||
func testAddingManually() {
|
||||
var fbx = FlexBuffersWriter(
|
||||
initialSize: 8,
|
||||
flags: .shareKeysAndStrings)
|
||||
|
||||
let outerMap = fbx.startMap()
|
||||
|
||||
let vector = fbx.startVector(key: "vec")
|
||||
fbx.add(int64: -100)
|
||||
fbx.add(string: "Fred")
|
||||
fbx.indirect(float32: 4.0)
|
||||
let lv = fbx.lastValue()
|
||||
let blob: [UInt8] = [77]
|
||||
fbx.add(blob: blob, length: blob.count)
|
||||
fbx.add(bool: false)
|
||||
fbx.reuse(value: lv!)
|
||||
fbx.endVector(start: vector)
|
||||
|
||||
let ints: [Int32] = [1, 2, 3]
|
||||
fbx.create(vector: ints, key: "bar")
|
||||
fbx.createFixed(vector: ints, key: "bar3")
|
||||
let bools = [true, false, true, false]
|
||||
fbx.create(vector: bools, key: "bools")
|
||||
fbx.add(bool: true, key: "bool")
|
||||
fbx.add(double: 100, key: "foo")
|
||||
|
||||
let innerMap = fbx.startMap(key: "mymap")
|
||||
fbx.add(string: "Fred", key: "foo")
|
||||
fbx.endMap(start: innerMap)
|
||||
|
||||
fbx.endMap(start: outerMap)
|
||||
|
||||
|
||||
fbx.finish()
|
||||
let buf: ByteBuffer = fbx.sizedByteBuffer
|
||||
buf.withUnsafeBytes {
|
||||
// swiftformat:disable all
|
||||
XCTAssertEqual(
|
||||
Array($0),
|
||||
flexbufferGolden
|
||||
)
|
||||
// swiftformat:enable all
|
||||
}
|
||||
}
|
||||
|
||||
func testEncodingAllTypes() {
|
||||
var fbx = FlexBuffersWriter()
|
||||
fbx.vector {
|
||||
$0.indirect(int64: 9)
|
||||
$0.indirect(uint64: 9)
|
||||
$0.indirect(float32: 3)
|
||||
$0.indirect(double: 3)
|
||||
|
||||
$0.addNil()
|
||||
$0.add(bool: true)
|
||||
$0.add(int64: 9)
|
||||
$0.add(int64: -9)
|
||||
$0.add(uint64: 9)
|
||||
$0.add(double: 2.4)
|
||||
$0.add(float32: 2.4)
|
||||
$0.add(double: -2.4)
|
||||
$0.add(float32: -2.4)
|
||||
}
|
||||
fbx.finish()
|
||||
let buf: ByteBuffer = fbx.sizedByteBuffer
|
||||
|
||||
buf.withUnsafeBytes {
|
||||
// swiftformat:disable all
|
||||
XCTAssertEqual(
|
||||
Array($0),
|
||||
allTypesGolden)
|
||||
// swiftformat:enable all
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2024 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 FlexBuffers
|
||||
|
||||
// swiftformat:disable all
|
||||
let flexbufferGolden: [UInt8] = [118, 101, 99, 0, 4, 70, 114, 101, 100, 0, 0, 0, 0, 0, 128, 64, 1, 77, 6, 156, 15, 9, 5, 0, 12, 4, 20, 34, 100, 104, 34, 98, 97, 114, 0, 0, 3, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 98, 97, 114, 51, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 98, 111, 111, 108, 115, 0, 4, 1, 0, 1, 0, 98, 111, 111, 108, 0, 102, 111, 111, 0, 109, 121, 109, 97, 112, 0, 1, 11, 1, 1, 1, 98, 20, 7, 75, 55, 25, 37, 22, 19, 112, 0, 0, 0, 10, 0, 0, 0, 1, 0, 0, 0, 7, 0, 0, 0, 88, 0, 0, 0, 72, 0, 0, 0, 1, 0, 0, 0, 61, 0, 0, 0, 0, 0, 200, 66, 45, 0, 0, 0, 133, 0, 0, 0, 46, 78, 106, 144, 14, 36, 40, 35, 38, 1]
|
||||
|
||||
let allTypesGolden: [UInt8] = [9, 9, 0, 0, 0, 0, 64, 64, 0, 0, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 0, 31, 0, 0, 0, 0, 0, 0, 0, 36, 0, 0, 0, 0, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 247, 255, 255, 255, 255, 255, 255, 255, 9, 0, 0, 0, 0, 0, 0, 0, 51, 51, 51, 51, 51, 51, 3, 64, 0, 0, 0, 64, 51, 51, 3, 64, 51, 51, 51, 51, 51, 51, 3, 192, 0, 0, 0, 64, 51, 51, 3, 192, 24, 28, 34, 34, 3, 107, 7, 7, 11, 15, 15, 15, 15, 117, 43, 1]
|
||||
// swiftformat:enable all
|
||||
|
||||
@inline(__always)
|
||||
func createSizedBuffer() -> ByteBuffer {
|
||||
createProperBuffer().sizedByteBuffer
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
func createProperBuffer() -> FlexBuffersWriter {
|
||||
var fbx = FlexBuffersWriter(
|
||||
initialSize: 8,
|
||||
flags: .shareKeysAndStrings)
|
||||
|
||||
fbx.map { map in
|
||||
map.vector(key: "vec") { v in
|
||||
v.add(int64: -100)
|
||||
v.add(string: "Fred")
|
||||
v.indirect(float32: 4.0)
|
||||
let lv = v.lastValue()
|
||||
let blob: [UInt8] = [77]
|
||||
v.add(blob: blob, length: blob.count)
|
||||
v.add(bool: false)
|
||||
v.reuse(value: lv!)
|
||||
}
|
||||
let ints: [Int32] = [1, 2, 3]
|
||||
map.create(vector: ints, key: "bar")
|
||||
map.createFixed(vector: ints, key: "bar3")
|
||||
let bools = [true, false, true, false]
|
||||
map.create(vector: bools, key: "bools")
|
||||
map.add(bool: true, key: "bool")
|
||||
map.add(double: 100, key: "foo")
|
||||
map.map(key: "mymap") { m in
|
||||
m.add(string: "Fred", key: "foo")
|
||||
}
|
||||
}
|
||||
|
||||
fbx.finish()
|
||||
return fbx
|
||||
}
|
||||
Reference in New Issue
Block a user