Allow reading unaligned buffers starting from swift 5.7, while keeping 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
This commit is contained in:
mustiikhalil
2023-09-27 07:50:03 +02:00
committed by GitHub
parent 4b7d8e0df9
commit 6f71b76e6f
3 changed files with 91 additions and 9 deletions

View File

@@ -1,4 +1,4 @@
// swift-tools-version:5.2 // swift-tools-version:5.6
/* /*
* Copyright 2020 Google Inc. All rights reserved. * Copyright 2020 Google Inc. All rights reserved.
* *
@@ -32,6 +32,5 @@ let package = Package(
.target( .target(
name: "FlatBuffers", name: "FlatBuffers",
dependencies: [], dependencies: [],
path: "swift/Sources", path: "swift/Sources"),
exclude: ["Documentation.docc/Resources/code/swift"]),
]) ])

View File

@@ -117,6 +117,8 @@ public struct ByteBuffer {
public var memory: UnsafeMutableRawPointer { _storage.memory } public var memory: UnsafeMutableRawPointer { _storage.memory }
/// Current capacity for the buffer /// Current capacity for the buffer
public var capacity: Int { _storage.capacity } 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 /// Constructor that creates a Flatbuffer object from a UInt8
/// - Parameter bytes: Array of UInt8 /// - Parameter bytes: Array of UInt8
@@ -124,6 +126,7 @@ public struct ByteBuffer {
var b = bytes var b = bytes
_storage = Storage(count: bytes.count, alignment: alignment) _storage = Storage(count: bytes.count, alignment: alignment)
_writerSize = _storage.capacity _writerSize = _storage.capacity
allowReadingUnalignedBuffers = false
b.withUnsafeMutableBytes { bufferPointer in b.withUnsafeMutableBytes { bufferPointer in
self._storage.copy(from: bufferPointer.baseAddress!, count: bytes.count) self._storage.copy(from: bufferPointer.baseAddress!, count: bytes.count)
} }
@@ -136,6 +139,7 @@ public struct ByteBuffer {
var b = data var b = data
_storage = Storage(count: data.count, alignment: alignment) _storage = Storage(count: data.count, alignment: alignment)
_writerSize = _storage.capacity _writerSize = _storage.capacity
allowReadingUnalignedBuffers = false
b.withUnsafeMutableBytes { bufferPointer in b.withUnsafeMutableBytes { bufferPointer in
self._storage.copy(from: bufferPointer.baseAddress!, count: data.count) self._storage.copy(from: bufferPointer.baseAddress!, count: data.count)
} }
@@ -148,6 +152,7 @@ public struct ByteBuffer {
let size = size.convertToPowerofTwo let size = size.convertToPowerofTwo
_storage = Storage(count: size, alignment: alignment) _storage = Storage(count: size, alignment: alignment)
_storage.initialize(for: size) _storage.initialize(for: size)
allowReadingUnalignedBuffers = false
} }
#if swift(>=5.0) && !os(WASI) #if swift(>=5.0) && !os(WASI)
@@ -161,6 +166,7 @@ public struct ByteBuffer {
{ {
_storage = Storage(count: count, alignment: alignment) _storage = Storage(count: count, alignment: alignment)
_writerSize = _storage.capacity _writerSize = _storage.capacity
allowReadingUnalignedBuffers = false
contiguousBytes.withUnsafeBytes { buf in contiguousBytes.withUnsafeBytes { buf in
_storage.copy(from: buf.baseAddress!, count: buf.count) _storage.copy(from: buf.baseAddress!, count: buf.count)
} }
@@ -172,10 +178,12 @@ public struct ByteBuffer {
/// - Parameter capacity: The size of the given memory region /// - Parameter capacity: The size of the given memory region
public init( public init(
assumingMemoryBound memory: UnsafeMutableRawPointer, assumingMemoryBound memory: UnsafeMutableRawPointer,
capacity: Int) capacity: Int,
allowReadingUnalignedBuffers allowUnalignedBuffers: Bool = false)
{ {
_storage = Storage(memory: memory, capacity: capacity, unowned: true) _storage = Storage(memory: memory, capacity: capacity, unowned: true)
_writerSize = capacity _writerSize = capacity
allowReadingUnalignedBuffers = allowUnalignedBuffers
} }
/// Creates a copy of the buffer that's being built by calling sizedBuffer /// Creates a copy of the buffer that's being built by calling sizedBuffer
@@ -186,6 +194,7 @@ public struct ByteBuffer {
_storage = Storage(count: count, alignment: alignment) _storage = Storage(count: count, alignment: alignment)
_storage.copy(from: memory, count: count) _storage.copy(from: memory, count: count)
_writerSize = _storage.capacity _writerSize = _storage.capacity
allowReadingUnalignedBuffers = false
} }
/// Creates a copy of the existing flatbuffer, by copying it to a different memory. /// Creates a copy of the existing flatbuffer, by copying it to a different memory.
@@ -201,6 +210,7 @@ public struct ByteBuffer {
_storage = Storage(count: count, alignment: alignment) _storage = Storage(count: count, alignment: alignment)
_storage.copy(from: memory, count: count) _storage.copy(from: memory, count: count)
_writerSize = removeBytes _writerSize = removeBytes
allowReadingUnalignedBuffers = false
} }
/// Fills the buffer with padding by adding to the writersize /// Fills the buffer with padding by adding to the writersize
@@ -234,8 +244,13 @@ public struct ByteBuffer {
mutating func push<T: NativeStruct>(struct value: T, size: Int) { mutating func push<T: NativeStruct>(struct value: T, size: Int) {
ensureSpace(size: size) ensureSpace(size: size)
var v = value var v = value
memcpy(_storage.memory.advanced(by: writerIndex &- size), &v, size) withUnsafeBytes(of: &v) {
_writerSize = _writerSize &+ size memcpy(
_storage.memory.advanced(by: writerIndex &- size),
$0.baseAddress!,
size)
self._writerSize = self._writerSize &+ size
}
} }
/// Adds an object of type Scalar into the buffer /// Adds an object of type Scalar into the buffer
@@ -247,8 +262,13 @@ public struct ByteBuffer {
mutating func push<T: Scalar>(value: T, len: Int) { mutating func push<T: Scalar>(value: T, len: Int) {
ensureSpace(size: len) ensureSpace(size: len)
var v = value var v = value
memcpy(_storage.memory.advanced(by: writerIndex &- len), &v, len) withUnsafeBytes(of: &v) {
_writerSize = _writerSize &+ len 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 /// Adds a string to the buffer using swift.utf8 object
@@ -352,7 +372,12 @@ public struct ByteBuffer {
/// - position: the index of the object in the buffer /// - position: the index of the object in the buffer
@inline(__always) @inline(__always)
public func read<T>(def: T.Type, position: Int) -> T { 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 /// Reads a slice from the memory assuming a type of T

View File

@@ -154,6 +154,64 @@ class FlatBuffersMonsterWriterTests: XCTestCase {
byteBuffer: &byteBuffer) as MyGame_Example_Monster)) 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) { func readMonster(monster: Monster) {
var monster = monster var monster = monster
readFlatbufferMonster(monster: &monster) readFlatbufferMonster(monster: &monster)