[Swift] Adds new API to reduce memory copying within swift (#8484)

* Adds new API to reduce memory copying within swift

Adds new storage container _InternalByteBuffer which
will be holding the data that will be created within the swift
lib, however reading data will be redirected to ByteBuffer, which
should be able to handle all types of data that swift provide without
the need to copy the data itself. This is due to holding a reference to
the data.

Replaces assumingMemoryBinding with bindMemory which is safer

Adds function that provides access to a UnsafeBufferPointer for
scalars and NativeStructs within swift

Updates docs

Suppress compilation warnings by replacing var with let

Using overflow operators within swift to improve performance

Adds tests for GRPC message creation from a retained _InternalByteBuffer
This commit is contained in:
mustiikhalil
2025-03-18 07:48:39 +01:00
committed by GitHub
parent 1c514626e8
commit bd1b2d0baf
29 changed files with 1470 additions and 463 deletions

View File

@@ -32,7 +32,7 @@ public struct FlatBufferBuilder {
/// Storage for the Vtables used in the buffer are stored in here, so they would be written later in EndTable
@usableFromInline internal var _vtableStorage = VTableStorage()
/// Flatbuffer data will be written into
@usableFromInline internal var _bb: ByteBuffer
@usableFromInline internal var _bb: _InternalByteBuffer
/// Reference Vtables that were already written to the buffer
private var _vtables: [UOffset] = []
@@ -54,6 +54,9 @@ public struct FlatBufferBuilder {
/// Gives a read access to the buffer's size
public var size: UOffset { _bb.size }
/// Current allocated capacity within the storage
@inline(__always)
public var capacity: Int { _bb.capacity }
#if !os(WASI)
/// Data representation of the buffer
@@ -61,9 +64,15 @@ public struct FlatBufferBuilder {
/// Should only be used after ``finish(offset:addPrefix:)`` is called
public var data: Data {
assert(finished, "Data shouldn't be called before finish()")
return Data(
bytes: _bb.memory.advanced(by: _bb.writerIndex),
count: _bb.capacity &- _bb.writerIndex)
return _bb.withUnsafeSlicedBytes { ptr in
var data = Data()
data.append(
ptr.baseAddress!.bindMemory(
to: UInt8.self,
capacity: _bb.capacity),
count: _bb.capacity)
return data
}
}
#endif
@@ -71,10 +80,9 @@ public struct FlatBufferBuilder {
///
/// Note: This should be used with caution.
public var fullSizedByteArray: [UInt8] {
let ptr = UnsafeBufferPointer(
start: _bb.memory.assumingMemoryBound(to: UInt8.self),
count: _bb.capacity)
return Array(ptr)
_bb.withUnsafeBytes { ptr in
Array(ptr)
}
}
/// Returns the written bytes into the ``ByteBuffer``
@@ -82,14 +90,18 @@ public struct FlatBufferBuilder {
/// Should only be used after ``finish(offset:addPrefix:)`` is called
public var sizedByteArray: [UInt8] {
assert(finished, "Data shouldn't be called before finish()")
return _bb.underlyingBytes
return _bb.withUnsafeSlicedBytes { ptr in
Array(ptr)
}
}
/// Returns the original ``ByteBuffer``
///
/// Returns the current buffer that was just created
/// with the offsets, and data written to it.
public var buffer: ByteBuffer { _bb }
public var buffer: ByteBuffer {
ByteBuffer(byteBuffer: _bb)
}
/// Returns a newly created sized ``ByteBuffer``
///
@@ -97,9 +109,11 @@ public struct FlatBufferBuilder {
/// to the main buffer
public var sizedBuffer: ByteBuffer {
assert(finished, "Data shouldn't be called before finish()")
return ByteBuffer(
memory: _bb.memory.advanced(by: _bb.reader),
count: Int(_bb.size))
return _bb.withUnsafeSlicedBytes { ptr in
ByteBuffer(
copyingMemoryBound: ptr.baseAddress!,
capacity: ptr.count)
}
}
// MARK: - Init
@@ -122,7 +136,7 @@ public struct FlatBufferBuilder {
"Reading/Writing a buffer in big endian machine is not supported on swift")
}
serializeDefaults = force
_bb = ByteBuffer(initialSize: Int(initialSize))
_bb = _InternalByteBuffer(initialSize: Int(initialSize))
}
/// Clears the builder and the buffer from the written data.
@@ -149,9 +163,10 @@ public struct FlatBufferBuilder {
for index in stride(from: 0, to: fields.count, by: 1) {
let start = _bb.capacity &- Int(table.o)
let startTable = start &- Int(_bb.read(def: Int32.self, position: start))
let isOkay = _bb.read(
def: VOffset.self,
position: startTable &+ Int(fields[index])) != 0
let isOkay =
_bb.read(
def: VOffset.self,
position: startTable &+ Int(fields[index])) != 0
assert(isOkay, "Flatbuffers requires the following field")
}
}
@@ -249,7 +264,8 @@ public struct FlatBufferBuilder {
///
/// - Parameter startOffset:Start point of the object written
/// - returns: The root of the table
mutating public func endTable(at startOffset: UOffset) -> UOffset {
@inline(__always)
mutating public func endTable(at startOffset: UOffset) -> UOffset {
assert(isNested, "Calling endtable without calling starttable")
let sizeofVoffset = MemoryLayout<VOffset>.size
let vTableOffset = push(element: SOffset(0))
@@ -313,7 +329,7 @@ public struct FlatBufferBuilder {
/// Asserts to see if the object is not nested
@inline(__always)
@usableFromInline
mutating internal func notNested() {
mutating internal func notNested() {
assert(!isNested, "Object serialization must not be nested")
}
@@ -337,7 +353,7 @@ public struct FlatBufferBuilder {
bufSize: UInt32,
elementSize: UInt32) -> UInt32
{
((~bufSize) &+ 1) & (elementSize - 1)
((~bufSize) &+ 1) & (elementSize &- 1)
}
/// Prealigns the buffer before writting a new object into the buffer
@@ -348,9 +364,11 @@ 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: Int(
padding(
bufSize: _bb.size &+ UOffset(len),
elementSize: UOffset(alignment))))
}
/// Prealigns the buffer before writting a new object into the buffer
@@ -478,10 +496,11 @@ public struct FlatBufferBuilder {
/// - Parameter bytes: bytes to be written into the buffer
/// - Returns: ``Offset`` of the vector
mutating public func createVector(bytes: ContiguousBytes) -> Offset {
let size = bytes.withUnsafeBytes { ptr in ptr.count }
startVector(size, elementSize: MemoryLayout<UInt8>.size)
_bb.push(bytes: bytes)
return endVector(len: size)
bytes.withUnsafeBytes {
startVector($0.count, elementSize: MemoryLayout<UInt8>.size)
_bb.push(bytes: $0)
return endVector(len: $0.count)
}
}
#endif
@@ -822,6 +841,10 @@ public struct FlatBufferBuilder {
return _bb.size
}
@inline(__always)
public func read<T>(def: T.Type, position: Int) -> T {
_bb.read(def: def, position: position)
}
}
extension FlatBufferBuilder: CustomDebugStringConvertible {