Last active
February 3, 2024 07:09
-
-
Save carlynorama/1957ac97907e296b3f89406cd12a1f78 to your computer and use it in GitHub Desktop.
FixedArray
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// FixedWidth.swift | |
// TestingTwo | |
// | |
// Created by Carlyn Maw on 2/2/24. | |
// Inspired by 25:52 of WWDC 2020 "Safely Manage Pointers in Swift." | |
// In the subscript, using .load(fromByteOffset:as) prevents rebinding of memory for access. This struct can point to memory bound as a different type without overriding. | |
//TODO: Add tuplebride to help with C | |
//examples: https://github.com/carlynorama/UnsafeWrapCSampler/blob/main/Sources/Swift/TupleBridge.swift | |
import Foundation | |
struct FixedSizeArray<Element> : RandomAccessCollection { | |
let count:Int | |
var startIndex: Int { 0 } | |
var endIndex: Int { count } | |
//What is the best storage type? | |
private var dataBlob:Data | |
} | |
extension FixedSizeArray { | |
init(_ count:Int, default d:Element, initializer:() -> [Element] = { [] }) { | |
self.count = count | |
var result = initializer().prefix(count) | |
//if result.count > count { return nil } | |
for _ in 0...(count - result.count) { | |
result.append(d) | |
} | |
self.dataBlob = result.withUnsafeMutableBufferPointer { pointer in | |
Data(buffer: pointer) | |
} | |
} | |
init(initializer:() -> [Element]) { | |
var tmp = initializer() | |
self.dataBlob = tmp.withUnsafeMutableBufferPointer { pointer in | |
Data(buffer: pointer) | |
} | |
self.count = dataBlob.withUnsafeBytes { bytes in | |
let tmpCount = bytes.count / MemoryLayout<Element>.stride | |
precondition(tmpCount * MemoryLayout<Element>.stride == bytes.count) | |
precondition(Int(bitPattern: bytes.baseAddress).isMultiple(of: MemoryLayout<Element>.alignment)) | |
return tmpCount | |
} | |
} | |
init(dataBlob: Data, as: Element.Type) { | |
self.dataBlob = dataBlob | |
self.count = dataBlob.withUnsafeBytes { bytes in | |
let tmpCount = bytes.count / MemoryLayout<Element>.stride | |
precondition(tmpCount * MemoryLayout<Element>.stride == bytes.count) | |
precondition(Int(bitPattern: bytes.baseAddress).isMultiple(of: MemoryLayout<Element>.alignment)) | |
return tmpCount | |
} | |
} | |
} | |
extension FixedSizeArray { | |
subscript(position: Int) -> Element { | |
get { | |
dataBlob.withUnsafeBytes { rawPointer in | |
let bufferPointer = rawPointer.assumingMemoryBound(to: Element.self) | |
return bufferPointer[position] | |
} | |
} | |
set { | |
let startIndex = dataBlob.startIndex + position * MemoryLayout<Element>.stride | |
let endIndex = startIndex + MemoryLayout<Element>.stride | |
withUnsafePointer(to: newValue) { sourceValuePointer in | |
dataBlob.replaceSubrange(startIndex..<endIndex, with: sourceValuePointer, count: MemoryLayout<Element>.stride) | |
} | |
} | |
} | |
var all:[Element] { | |
loadFixedSizeCArray(source: dataBlob, ofType: Element.self) ?? [] | |
} | |
//Okay to use assumingMemoryBound here IF using type ACTUALLY bound to. | |
//Else see UnsafeBufferView struct example using .loadBytes to recast read values | |
private func fetchFixedSizeCArray<T, R>(source:T, boundToType:R.Type) -> [R] { | |
withUnsafeBytes(of: source) { (rawPointer) -> [R] in | |
let bufferPointer = rawPointer.assumingMemoryBound(to: boundToType) | |
return [R](bufferPointer) | |
} | |
} | |
//TODO: Test non-numerics | |
private func loadFixedSizeCArray<T, R>(source:T, ofType:R.Type) -> [R]? { | |
withUnsafeBytes(of: source) { (rawPointer) -> [R]? in | |
rawPointer.baseAddress?.load(as: [R].self) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment