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"),
-      ])
+      ]),
   ])