Skip to content

Commit 866521e

Browse files
committed
Moves the internal stack to use a pointer stack instead of the native array for improved performance
Remove custom flags for native arrays when using flexbuffers on Wasm
1 parent c21bda1 commit 866521e

File tree

4 files changed

+144
-9
lines changed

4 files changed

+144
-9
lines changed

swift/Sources/FlexBuffers/Reader/Reference.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17+
import Common
1718
import Foundation
1819

1920
enum FlexBuffersErrors: Error {
@@ -23,6 +24,10 @@ enum FlexBuffersErrors: Error {
2324

2425
@inline(__always)
2526
public func getRoot(buffer: ByteBuffer) throws -> Reference? {
27+
assert(
28+
isLitteEndian,
29+
"Swift FlexBuffers currently only supports little-endian systems")
30+
2631
let end = buffer.count
2732
if buffer.count < 3 {
2833
throw FlexBuffersErrors.sizeOfBufferIsTooSmall

swift/Sources/FlexBuffers/Writer/FlexBuffersWriter.swift

Lines changed: 136 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,15 @@ public struct FlexBuffersWriter {
3737
private var hasDuplicatedKeys = false
3838
private var minBitWidth: BitWidth = .w8
3939
private var _bb: _InternalByteBuffer
40-
private var stack: [Value] = []
40+
private var stack: Stack = Stack()
4141
private var keyPool: [Int: UInt] = [:]
4242
private var stringPool: [Int: UInt] = [:]
4343
private var flags: BuilderFlag
4444

4545
public init(initialSize: Int = 1024, flags: BuilderFlag = .shareKeys) {
46+
assert(
47+
isLitteEndian,
48+
"Swift FlexBuffers currently only supports little-endian systems")
4649
_bb = _InternalByteBuffer(initialSize: initialSize)
4750
self.flags = flags
4851
}
@@ -148,7 +151,7 @@ public struct FlexBuffersWriter {
148151
typed: typed,
149152
fixed: fixed,
150153
keys: nil)
151-
stack = Array(stack[..<start])
154+
stack.popLast(start)
152155
stack.append(vec)
153156
return vec.u
154157
}
@@ -217,7 +220,7 @@ public struct FlexBuffersWriter {
217220
typed: false,
218221
fixed: false,
219222
keys: keys)
220-
stack = Array(stack[..<start])
223+
stack.popLast(start)
221224
stack.append(vec)
222225
return numericCast(vec.u)
223226
}
@@ -721,7 +724,7 @@ public struct FlexBuffersWriter {
721724
assert(
722725
vectorType == stack[i].type,
723726
"""
724-
If you get this assert you are writing a typed vector
727+
If you get this assert you are writing a typed vector
725728
with elements that are not all the same type
726729
""")
727730
}
@@ -757,7 +760,7 @@ public struct FlexBuffersWriter {
757760
}
758761

759762
if !fixed {
760-
write(value: count, byteWidth: byteWidth)
763+
write(value: UInt64(count), byteWidth: byteWidth)
761764
}
762765

763766
let vloc = _bb.writerIndex
@@ -830,13 +833,14 @@ public struct FlexBuffersWriter {
830833
let key, value: Value
831834
}
832835

833-
stack[start...].withUnsafeMutableBytes { buffer in
836+
stack.withUnsafeMutableBytes(start: start) { buffer in
834837
var ptr = buffer.assumingMemoryBound(to: TwoValue.self)
835838
ptr.sort { a, b in
836839
let aMem = _bb.memory.advanced(by: numericCast(a.key.u))
837840
.assumingMemoryBound(to: CChar.self)
838841
let bMem = _bb.memory.advanced(by: numericCast(b.key.u))
839842
.assumingMemoryBound(to: CChar.self)
843+
840844
let comp = strcmp(aMem, bMem)
841845
if (comp == 0) && a != b { hasDuplicatedKeys = true }
842846
return comp < 0
@@ -897,3 +901,129 @@ extension FlexBuffersWriter {
897901
return endMap(start: start)
898902
}
899903
}
904+
905+
fileprivate struct Stack: RandomAccessCollection {
906+
typealias Element = Value
907+
typealias Index = Int
908+
909+
private final class Storage {
910+
var memory: UnsafeMutableRawPointer
911+
912+
init(capacity: Int, alignment: Int) {
913+
memory = .allocate(byteCount: capacity, alignment: alignment)
914+
memset(memory, 0, capacity)
915+
}
916+
917+
deinit {
918+
memory.deallocate()
919+
}
920+
}
921+
922+
private static let initialCapacity = 10 &* MemoryLayout<Value>.stride
923+
private let storage: Storage
924+
private var capacity: Int
925+
private(set) var count: Int
926+
927+
var startIndex: Int {
928+
0
929+
}
930+
931+
var endIndex: Int {
932+
count
933+
}
934+
935+
init() {
936+
count = 0
937+
capacity = Self.initialCapacity
938+
939+
storage = Storage(
940+
capacity: capacity,
941+
alignment: MemoryLayout<Value>.alignment)
942+
}
943+
944+
@inline(__always)
945+
subscript(position: Int) -> Value {
946+
get {
947+
storage.memory.advanced(by: position &* MemoryLayout<Value>.stride)
948+
.assumingMemoryBound(to: Value.self).pointee
949+
}
950+
set {
951+
storage.memory.advanced(by: position &* MemoryLayout<Value>.stride)
952+
.assumingMemoryBound(to: Value.self).pointee = newValue
953+
}
954+
}
955+
956+
@inline(__always)
957+
mutating func popLast(_ val: Int) {
958+
count = if val < 0 {
959+
0
960+
} else {
961+
val
962+
}
963+
}
964+
965+
mutating func append(_ value: Value) {
966+
let writePosition = count &* MemoryLayout<Value>.stride
967+
if writePosition >= capacity {
968+
reallocate(writePosition: writePosition)
969+
}
970+
971+
storage.memory.advanced(by: writePosition).storeBytes(
972+
of: value,
973+
as: Value.self)
974+
count += 1
975+
}
976+
977+
mutating func removeAll(keepingCapacity keepCapacity: Bool = false) {
978+
count = 0
979+
if !keepCapacity {
980+
capacity = Self.initialCapacity
981+
storage.memory = UnsafeMutableRawPointer.allocate(
982+
byteCount: capacity,
983+
alignment: MemoryLayout<Value>.alignment)
984+
}
985+
memset(storage.memory, 0, capacity)
986+
}
987+
988+
@discardableResult
989+
mutating func withUnsafeMutableBytes<R>(
990+
start: Int,
991+
_ body: (UnsafeMutableRawBufferPointer) throws -> R) rethrows -> R
992+
{
993+
let startingPosition = start &* MemoryLayout<Value>.stride
994+
let pointer = storage.memory.advanced(by: startingPosition)
995+
return try body(UnsafeMutableRawBufferPointer(
996+
start: pointer,
997+
count: (count &* MemoryLayout<Value>.stride) &- startingPosition))
998+
}
999+
1000+
@discardableResult
1001+
mutating func withUnsafeMutableBytes<R>(
1002+
_ body: (UnsafeMutableRawBufferPointer) throws
1003+
-> R) rethrows -> R
1004+
{
1005+
return try body(UnsafeMutableRawBufferPointer(
1006+
start: storage.memory,
1007+
count: count &* MemoryLayout<Value>.stride))
1008+
}
1009+
1010+
mutating private func reallocate(writePosition: Int) {
1011+
while capacity <= writePosition {
1012+
capacity = capacity << 1
1013+
}
1014+
1015+
/// solution take from Apple-NIO
1016+
capacity = capacity.convertToPowerofTwo
1017+
1018+
let newData = UnsafeMutableRawPointer.allocate(
1019+
byteCount: capacity,
1020+
alignment: MemoryLayout<Value>.alignment)
1021+
memset(newData, 0, capacity)
1022+
memcpy(
1023+
newData,
1024+
storage.memory,
1025+
writePosition)
1026+
storage.memory.deallocate()
1027+
storage.memory = newData
1028+
}
1029+
}

swift/Sources/FlexBuffers/_InternalByteBuffer.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ struct _InternalByteBuffer {
6060
let newData = UnsafeMutableRawPointer.allocate(
6161
byteCount: capacity,
6262
alignment: alignment)
63-
memset(newData, 0, capacity &- writerSize)
63+
memset(newData, 0, capacity)
6464
memcpy(
6565
newData,
6666
memory,

tests/swift/Wasm.tests/Package.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ let package = Package(
2424
],
2525
dependencies: [
2626
.package(path: "../../.."),
27-
.package(url: "https://github.com/swiftwasm/WasmKit", exact: "0.1.6")
27+
.package(url: "https://github.com/swiftwasm/WasmKit", exact: "0.1.6"),
2828
],
2929
targets: [
3030
.target(name: "Wasm"),
@@ -37,5 +37,5 @@ let package = Package(
3737
name: "FlexBuffers.Test.Swift.WasmTests",
3838
dependencies: [
3939
.product(name: "FlexBuffers", package: "flatbuffers"),
40-
])
40+
]),
4141
])

0 commit comments

Comments
 (0)