Moves the internal stack to use a pointer stack instead of the native array for improved performance (#8891)
Remove custom flags for native arrays when using flexbuffers on Wasm
Co-authored-by: Wouter van Oortmerssen <aardappel@gmail.com>
diff --git a/swift/Sources/FlexBuffers/Reader/Reference.swift b/swift/Sources/FlexBuffers/Reader/Reference.swift
index f771ba2..e78efec 100644
--- a/swift/Sources/FlexBuffers/Reader/Reference.swift
+++ b/swift/Sources/FlexBuffers/Reader/Reference.swift
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+import Common
import Foundation
enum FlexBuffersErrors: Error {
@@ -23,6 +24,10 @@
@inline(__always)
public func getRoot(buffer: ByteBuffer) throws -> Reference? {
+ assert(
+ isLitteEndian,
+ "Swift FlexBuffers currently only supports little-endian systems")
+
let end = buffer.count
if buffer.count < 3 {
throw FlexBuffersErrors.sizeOfBufferIsTooSmall
diff --git a/swift/Sources/FlexBuffers/Writer/FlexBuffersWriter.swift b/swift/Sources/FlexBuffers/Writer/FlexBuffersWriter.swift
index 6836ec7..449ab58 100644
--- a/swift/Sources/FlexBuffers/Writer/FlexBuffersWriter.swift
+++ b/swift/Sources/FlexBuffers/Writer/FlexBuffersWriter.swift
@@ -37,12 +37,15 @@
private var hasDuplicatedKeys = false
private var minBitWidth: BitWidth = .w8
private var _bb: _InternalByteBuffer
- private var stack: [Value] = []
+ private var stack: Stack = Stack()
private var keyPool: [Int: UInt] = [:]
private var stringPool: [Int: UInt] = [:]
private var flags: BuilderFlag
public init(initialSize: Int = 1024, flags: BuilderFlag = .shareKeys) {
+ assert(
+ isLitteEndian,
+ "Swift FlexBuffers currently only supports little-endian systems")
_bb = _InternalByteBuffer(initialSize: initialSize)
self.flags = flags
}
@@ -148,7 +151,7 @@
typed: typed,
fixed: fixed,
keys: nil)
- stack = Array(stack[..<start])
+ stack.popLast(start)
stack.append(vec)
return vec.u
}
@@ -217,7 +220,7 @@
typed: false,
fixed: false,
keys: keys)
- stack = Array(stack[..<start])
+ stack.popLast(start)
stack.append(vec)
return numericCast(vec.u)
}
@@ -721,7 +724,7 @@
assert(
vectorType == stack[i].type,
"""
- If you get this assert you are writing a typed vector
+ If you get this assert you are writing a typed vector
with elements that are not all the same type
""")
}
@@ -757,7 +760,7 @@
}
if !fixed {
- write(value: count, byteWidth: byteWidth)
+ write(value: UInt64(count), byteWidth: byteWidth)
}
let vloc = _bb.writerIndex
@@ -830,13 +833,14 @@
let key, value: Value
}
- stack[start...].withUnsafeMutableBytes { buffer in
+ stack.withUnsafeMutableBytes(start: start) { buffer in
var ptr = buffer.assumingMemoryBound(to: TwoValue.self)
ptr.sort { a, b in
let aMem = _bb.memory.advanced(by: numericCast(a.key.u))
.assumingMemoryBound(to: CChar.self)
let bMem = _bb.memory.advanced(by: numericCast(b.key.u))
.assumingMemoryBound(to: CChar.self)
+
let comp = strcmp(aMem, bMem)
if (comp == 0) && a != b { hasDuplicatedKeys = true }
return comp < 0
@@ -897,3 +901,129 @@
return endMap(start: start)
}
}
+
+fileprivate struct Stack: RandomAccessCollection {
+ typealias Element = Value
+ typealias Index = Int
+
+ private final class Storage {
+ var memory: UnsafeMutableRawPointer
+
+ init(capacity: Int, alignment: Int) {
+ memory = .allocate(byteCount: capacity, alignment: alignment)
+ memset(memory, 0, capacity)
+ }
+
+ deinit {
+ memory.deallocate()
+ }
+ }
+
+ private static let initialCapacity = 10 &* MemoryLayout<Value>.stride
+ private let storage: Storage
+ private var capacity: Int
+ private(set) var count: Int
+
+ var startIndex: Int {
+ 0
+ }
+
+ var endIndex: Int {
+ count
+ }
+
+ init() {
+ count = 0
+ capacity = Self.initialCapacity
+
+ storage = Storage(
+ capacity: capacity,
+ alignment: MemoryLayout<Value>.alignment)
+ }
+
+ @inline(__always)
+ subscript(position: Int) -> Value {
+ get {
+ storage.memory.advanced(by: position &* MemoryLayout<Value>.stride)
+ .assumingMemoryBound(to: Value.self).pointee
+ }
+ set {
+ storage.memory.advanced(by: position &* MemoryLayout<Value>.stride)
+ .assumingMemoryBound(to: Value.self).pointee = newValue
+ }
+ }
+
+ @inline(__always)
+ mutating func popLast(_ val: Int) {
+ count = if val < 0 {
+ 0
+ } else {
+ val
+ }
+ }
+
+ mutating func append(_ value: Value) {
+ let writePosition = count &* MemoryLayout<Value>.stride
+ if writePosition >= capacity {
+ reallocate(writePosition: writePosition)
+ }
+
+ storage.memory.advanced(by: writePosition).storeBytes(
+ of: value,
+ as: Value.self)
+ count += 1
+ }
+
+ mutating func removeAll(keepingCapacity keepCapacity: Bool = false) {
+ count = 0
+ if !keepCapacity {
+ capacity = Self.initialCapacity
+ storage.memory = UnsafeMutableRawPointer.allocate(
+ byteCount: capacity,
+ alignment: MemoryLayout<Value>.alignment)
+ }
+ memset(storage.memory, 0, capacity)
+ }
+
+ @discardableResult
+ mutating func withUnsafeMutableBytes<R>(
+ start: Int,
+ _ body: (UnsafeMutableRawBufferPointer) throws -> R) rethrows -> R
+ {
+ let startingPosition = start &* MemoryLayout<Value>.stride
+ let pointer = storage.memory.advanced(by: startingPosition)
+ return try body(UnsafeMutableRawBufferPointer(
+ start: pointer,
+ count: (count &* MemoryLayout<Value>.stride) &- startingPosition))
+ }
+
+ @discardableResult
+ mutating func withUnsafeMutableBytes<R>(
+ _ body: (UnsafeMutableRawBufferPointer) throws
+ -> R) rethrows -> R
+ {
+ return try body(UnsafeMutableRawBufferPointer(
+ start: storage.memory,
+ count: count &* MemoryLayout<Value>.stride))
+ }
+
+ mutating private func reallocate(writePosition: Int) {
+ while capacity <= writePosition {
+ capacity = capacity << 1
+ }
+
+ /// solution take from Apple-NIO
+ capacity = capacity.convertToPowerofTwo
+
+ let newData = UnsafeMutableRawPointer.allocate(
+ byteCount: capacity,
+ alignment: MemoryLayout<Value>.alignment)
+ memset(newData, 0, capacity)
+ memcpy(
+ newData,
+ storage.memory,
+ writePosition)
+ storage.memory.deallocate()
+ storage.memory = newData
+ }
+}
diff --git a/swift/Sources/FlexBuffers/_InternalByteBuffer.swift b/swift/Sources/FlexBuffers/_InternalByteBuffer.swift
index fcf7577..5df4ff3 100644
--- a/swift/Sources/FlexBuffers/_InternalByteBuffer.swift
+++ b/swift/Sources/FlexBuffers/_InternalByteBuffer.swift
@@ -60,7 +60,7 @@
let newData = UnsafeMutableRawPointer.allocate(
byteCount: capacity,
alignment: alignment)
- memset(newData, 0, capacity &- writerSize)
+ memset(newData, 0, capacity)
memcpy(
newData,
memory,
diff --git a/tests/swift/Wasm.tests/Package.swift b/tests/swift/Wasm.tests/Package.swift
index c5e34a6..24a41c8 100644
--- a/tests/swift/Wasm.tests/Package.swift
+++ b/tests/swift/Wasm.tests/Package.swift
@@ -24,7 +24,7 @@
],
dependencies: [
.package(path: "../../.."),
- .package(url: "https://github.com/swiftwasm/WasmKit", exact: "0.1.6")
+ .package(url: "https://github.com/swiftwasm/WasmKit", exact: "0.1.6"),
],
targets: [
.target(name: "Wasm"),
@@ -37,5 +37,5 @@
name: "FlexBuffers.Test.Swift.WasmTests",
dependencies: [
.product(name: "FlexBuffers", package: "flatbuffers"),
- ])
+ ]),
])