Skip to content

Commit

Permalink
Allow reading unaligned buffers starting from swift 5.7, while keepin…
Browse files Browse the repository at this point in the history
…g the creating aligned since its created by the library (#8061)

Addresses a warning on xcode 15 regarding copying a pointer without safeguards

Address PR comments regarding initializing buffers with flag

Adds a test case for copying unaligned buffers

Formatting code
  • Loading branch information
mustiikhalil authored Sep 27, 2023
1 parent 4b7d8e0 commit 6f71b76
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 9 deletions.
5 changes: 2 additions & 3 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// swift-tools-version:5.2
// swift-tools-version:5.6
/*
* Copyright 2020 Google Inc. All rights reserved.
*
Expand Down Expand Up @@ -32,6 +32,5 @@ let package = Package(
.target(
name: "FlatBuffers",
dependencies: [],
path: "swift/Sources",
exclude: ["Documentation.docc/Resources/code/swift"]),
path: "swift/Sources"),
])
37 changes: 31 additions & 6 deletions swift/Sources/FlatBuffers/ByteBuffer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -117,13 +117,16 @@ public struct ByteBuffer {
public var memory: UnsafeMutableRawPointer { _storage.memory }
/// Current capacity for the buffer
public var capacity: Int { _storage.capacity }
/// Crash if the trying to read an unaligned buffer instead of allowing users to read them.
public let allowReadingUnalignedBuffers: Bool

/// Constructor that creates a Flatbuffer object from a UInt8
/// - Parameter bytes: Array of UInt8
public init(bytes: [UInt8]) {
var b = bytes
_storage = Storage(count: bytes.count, alignment: alignment)
_writerSize = _storage.capacity
allowReadingUnalignedBuffers = false
b.withUnsafeMutableBytes { bufferPointer in
self._storage.copy(from: bufferPointer.baseAddress!, count: bytes.count)
}
Expand All @@ -136,6 +139,7 @@ public struct ByteBuffer {
var b = data
_storage = Storage(count: data.count, alignment: alignment)
_writerSize = _storage.capacity
allowReadingUnalignedBuffers = false
b.withUnsafeMutableBytes { bufferPointer in
self._storage.copy(from: bufferPointer.baseAddress!, count: data.count)
}
Expand All @@ -148,6 +152,7 @@ public struct ByteBuffer {
let size = size.convertToPowerofTwo
_storage = Storage(count: size, alignment: alignment)
_storage.initialize(for: size)
allowReadingUnalignedBuffers = false
}

#if swift(>=5.0) && !os(WASI)
Expand All @@ -161,6 +166,7 @@ public struct ByteBuffer {
{
_storage = Storage(count: count, alignment: alignment)
_writerSize = _storage.capacity
allowReadingUnalignedBuffers = false
contiguousBytes.withUnsafeBytes { buf in
_storage.copy(from: buf.baseAddress!, count: buf.count)
}
Expand All @@ -172,10 +178,12 @@ public struct ByteBuffer {
/// - Parameter capacity: The size of the given memory region
public init(
assumingMemoryBound memory: UnsafeMutableRawPointer,
capacity: Int)
capacity: Int,
allowReadingUnalignedBuffers allowUnalignedBuffers: Bool = false)
{
_storage = Storage(memory: memory, capacity: capacity, unowned: true)
_writerSize = capacity
allowReadingUnalignedBuffers = allowUnalignedBuffers
}

/// Creates a copy of the buffer that's being built by calling sizedBuffer
Expand All @@ -186,6 +194,7 @@ public struct ByteBuffer {
_storage = Storage(count: count, alignment: alignment)
_storage.copy(from: memory, count: count)
_writerSize = _storage.capacity
allowReadingUnalignedBuffers = false
}

/// Creates a copy of the existing flatbuffer, by copying it to a different memory.
Expand All @@ -201,6 +210,7 @@ public struct ByteBuffer {
_storage = Storage(count: count, alignment: alignment)
_storage.copy(from: memory, count: count)
_writerSize = removeBytes
allowReadingUnalignedBuffers = false
}

/// Fills the buffer with padding by adding to the writersize
Expand Down Expand Up @@ -234,8 +244,13 @@ public struct ByteBuffer {
mutating func push<T: NativeStruct>(struct value: T, size: Int) {
ensureSpace(size: size)
var v = value
memcpy(_storage.memory.advanced(by: writerIndex &- size), &v, size)
_writerSize = _writerSize &+ size
withUnsafeBytes(of: &v) {
memcpy(
_storage.memory.advanced(by: writerIndex &- size),
$0.baseAddress!,
size)
self._writerSize = self._writerSize &+ size
}
}

/// Adds an object of type Scalar into the buffer
Expand All @@ -247,8 +262,13 @@ public struct ByteBuffer {
mutating func push<T: Scalar>(value: T, len: Int) {
ensureSpace(size: len)
var v = value
memcpy(_storage.memory.advanced(by: writerIndex &- len), &v, len)
_writerSize = _writerSize &+ len
withUnsafeBytes(of: &v) {
memcpy(
_storage.memory.advanced(by: writerIndex &- len),
$0.baseAddress!,
len)
self._writerSize = self._writerSize &+ len
}
}

/// Adds a string to the buffer using swift.utf8 object
Expand Down Expand Up @@ -352,7 +372,12 @@ public struct ByteBuffer {
/// - position: the index of the object in the buffer
@inline(__always)
public func read<T>(def: T.Type, position: Int) -> T {
_storage.memory.advanced(by: position).load(as: T.self)
#if swift(>=5.7)
if allowReadingUnalignedBuffers {
return _storage.memory.advanced(by: position).loadUnaligned(as: T.self)
}
#endif
return _storage.memory.advanced(by: position).load(as: T.self)
}

/// Reads a slice from the memory assuming a type of T
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,64 @@ class FlatBuffersMonsterWriterTests: XCTestCase {
byteBuffer: &byteBuffer) as MyGame_Example_Monster))
}

func testUnalignedRead() {
// Aligned read
let fbb = createMonster(withPrefix: false)
let testAligned: () -> Bool = {
var buffer = fbb.sizedBuffer
var monster: Monster = getRoot(byteBuffer: &buffer)
self.readFlatbufferMonster(monster: &monster)
return true
}
XCTAssertEqual(testAligned(), true)
let testUnaligned: () -> Bool = {
var bytes: [UInt8] = [0x00]
bytes.append(contentsOf: fbb.sizedByteArray)
return bytes.withUnsafeMutableBytes { ptr in
guard var baseAddress = ptr.baseAddress else {
XCTFail("Base pointer is not defined")
return false
}
baseAddress = baseAddress.advanced(by: 1)
let unlignedPtr = UnsafeMutableRawPointer(baseAddress)
var bytes = ByteBuffer(
assumingMemoryBound: unlignedPtr,
capacity: ptr.count - 1,
allowReadingUnalignedBuffers: true)
var monster: Monster = getRoot(byteBuffer: &bytes)
self.readFlatbufferMonster(monster: &monster)
return true
}
}
XCTAssertEqual(testUnaligned(), true)
}

func testCopyUnalignedToAlignedBuffers() {
// Aligned read
let fbb = createMonster(withPrefix: true)
let testUnaligned: () -> Bool = {
var bytes: [UInt8] = [0x00]
bytes.append(contentsOf: fbb.sizedByteArray)
return bytes.withUnsafeMutableBytes { ptr in
guard var baseAddress = ptr.baseAddress else {
XCTFail("Base pointer is not defined")
return false
}
baseAddress = baseAddress.advanced(by: 1)
let unlignedPtr = UnsafeMutableRawPointer(baseAddress)
let bytes = ByteBuffer(
assumingMemoryBound: unlignedPtr,
capacity: ptr.count - 1,
allowReadingUnalignedBuffers: false)
var newBuf = FlatBuffersUtils.removeSizePrefix(bb: bytes)
var monster: Monster = getRoot(byteBuffer: &newBuf)
self.readFlatbufferMonster(monster: &monster)
return true
}
}
XCTAssertEqual(testUnaligned(), true)
}

func readMonster(monster: Monster) {
var monster = monster
readFlatbufferMonster(monster: &monster)
Expand Down

0 comments on commit 6f71b76

Please sign in to comment.