Skip to content

Commit

Permalink
[Swift] Push contiguous bytes (#8157)
Browse files Browse the repository at this point in the history
* Add version of push which takes ContiguousBytes

* Ensure overloads aren't ambiguous

* Add version of createVector

* Add version of push which takes ContiguousBytes

* Ensure overloads aren't ambiguous

* Add version of createVector

* Add similar conditional to other use of ContiguousBytes

* Attempt CI fix

* Use memcpy instead of copyMemory

memcpy is faster in tests

* Add testContiguousBytes

* Add benchmarks

* Add version of createVector

* Add benchmarks

* Update push to copy memory

Since we don't care about endianness, we can simply memcpy the array of scalars

* Remove function and benchmarks

Since we don't care about endianness, a FixedWidthInteger version of createVector isn't needed

* Improve naming

* Add doc comment
  • Loading branch information
wtholliday authored Nov 20, 2023
1 parent 7d6d99c commit b08abbb
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 4 deletions.
28 changes: 24 additions & 4 deletions swift/Sources/FlatBuffers/ByteBuffer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -228,12 +228,32 @@ public struct ByteBuffer {
@inline(__always)
@usableFromInline
mutating func push<T: Scalar>(elements: [T]) {
let size = elements.count &* MemoryLayout<T>.size
ensureSpace(size: size)
elements.reversed().forEach { s in
push(value: s, len: MemoryLayout.size(ofValue: s))
elements.withUnsafeBytes { ptr in
ensureSpace(size: ptr.count)
memcpy(
_storage.memory.advanced(by: writerIndex &- ptr.count),
UnsafeRawPointer(ptr.baseAddress!),
ptr.count)
self._writerSize = self._writerSize &+ ptr.count
}
}

/// Adds a `ContiguousBytes` to buffer memory
/// - Parameter value: bytes to copy
#if swift(>=5.0) && !os(WASI)
@inline(__always)
@usableFromInline
mutating func push(bytes: ContiguousBytes) {
bytes.withUnsafeBytes { ptr in
ensureSpace(size: ptr.count)
memcpy(
_storage.memory.advanced(by: writerIndex &- ptr.count),
UnsafeRawPointer(ptr.baseAddress!),
ptr.count)
self._writerSize = self._writerSize &+ ptr.count
}
}
#endif

/// Adds an object of type NativeStruct into the buffer
/// - Parameters:
Expand Down
16 changes: 16 additions & 0 deletions swift/Sources/FlatBuffers/FlatBufferBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,22 @@ public struct FlatBufferBuilder {
return endVector(len: size)
}

#if swift(>=5.0) && !os(WASI)
@inline(__always)
/// Creates a vector of bytes in the buffer.
///
/// Allows creating a vector from `Data` without copying to a `[UInt8]`
///
/// - 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)
}
#endif

/// Creates a vector of type ``Enum`` into the ``ByteBuffer``
///
/// ``createVector(_:)-9h189`` writes a vector of type ``Enum`` into
Expand Down
15 changes: 15 additions & 0 deletions tests/swift/benchmarks/Sources/benchmarks/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,20 @@ benchmark("100Strings") {
}
}

benchmark("100Bytes") {
var fb = FlatBufferBuilder(initialSize: 1<<20)
for _ in 0..<1_000_000 {
_ = fb.createVector(bytes)
}
}

benchmark("100Bytes-ContiguousBytes") {
var fb = FlatBufferBuilder(initialSize: 1<<20)
for _ in 0..<1_000_000 {
_ = fb.createVector(bytes: bytes)
}
}

benchmark("FlatBufferBuilder.add") {
var fb = FlatBufferBuilder(initialSize: 1024 * 1024 * 32)
for _ in 0..<1_000_000 {
Expand Down Expand Up @@ -73,6 +87,7 @@ benchmark("structs") {
}

let str = (0...99).map { _ -> String in "x" }.joined()
let bytes: [UInt8] = Array(repeating: 42, count: 100)

@usableFromInline
struct AA: NativeStruct {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -484,4 +484,21 @@ class FlatBuffersMonsterWriterTests: XCTestCase {
#endif
}

func testContiguousBytes() {
let byteArray: [UInt8] = [3, 1, 4, 1, 5, 9]
var fbb = FlatBufferBuilder(initialSize: 1)
let name = fbb.create(string: "Frodo")
let bytes = fbb.createVector(bytes: byteArray)
let root = Monster.createMonster(
&fbb,
nameOffset: name,
inventoryVectorOffset: bytes)
fbb.finish(offset: root)
var buffer = fbb.sizedBuffer
let monster: Monster = getRoot(byteBuffer: &buffer)
let values = monster.inventory

XCTAssertEqual(byteArray, values)
}

}

0 comments on commit b08abbb

Please sign in to comment.