mirror of
https://github.com/google/flatbuffers.git
synced 2026-06-01 19:58:15 +00:00
[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:
@@ -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) }
|
||||
"""
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user