Skip to content

Commit af3633f

Browse files
committed
Moves the internal stack to use a pointer stack instead of the native array for improved performance
1 parent 8914d06 commit af3633f

File tree

4 files changed

+162
-7
lines changed

4 files changed

+162
-7
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: 154 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,19 @@ public struct FlexBuffersWriter {
3737
private var hasDuplicatedKeys = false
3838
private var minBitWidth: BitWidth = .w8
3939
private var _bb: _InternalByteBuffer
40+
#if os(Wasm)
4041
private var stack: [Value] = []
42+
#else
43+
private var stack: Stack = Stack()
44+
#endif
4145
private var keyPool: [Int: UInt] = [:]
4246
private var stringPool: [Int: UInt] = [:]
4347
private var flags: BuilderFlag
4448

4549
public init(initialSize: Int = 1024, flags: BuilderFlag = .shareKeys) {
50+
assert(
51+
isLitteEndian,
52+
"Swift FlexBuffers currently only supports little-endian systems")
4653
_bb = _InternalByteBuffer(initialSize: initialSize)
4754
self.flags = flags
4855
}
@@ -148,7 +155,7 @@ public struct FlexBuffersWriter {
148155
typed: typed,
149156
fixed: fixed,
150157
keys: nil)
151-
stack = Array(stack[..<start])
158+
stack.popLast(start)
152159
stack.append(vec)
153160
return vec.u
154161
}
@@ -217,7 +224,7 @@ public struct FlexBuffersWriter {
217224
typed: false,
218225
fixed: false,
219226
keys: keys)
220-
stack = Array(stack[..<start])
227+
stack.popLast(start)
221228
stack.append(vec)
222229
return numericCast(vec.u)
223230
}
@@ -721,7 +728,7 @@ public struct FlexBuffersWriter {
721728
assert(
722729
vectorType == stack[i].type,
723730
"""
724-
If you get this assert you are writing a typed vector
731+
If you get this assert you are writing a typed vector
725732
with elements that are not all the same type
726733
""")
727734
}
@@ -830,13 +837,14 @@ public struct FlexBuffersWriter {
830837
let key, value: Value
831838
}
832839

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

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)