That of course wasn't very useful. This however could be actually used in practice:
typealias X1234<T> = (X1000<T>, X200<T>, X30<T>, X4<T>)
to represent 1234 element array.
A minimal sketch implementation of the FixedArray
type:
struct FixedArray<Element, Zone> {
typealias Index = Int
private var zone: Zone?
static var count: Int {
precondition(MemoryLayout<Zone>.size >= MemoryLayout<Element>.size)
// NOTE: a naïve MemoryLayout<Zone>.size / MemoryLayout<Element>.stride is not correct.
return 1 + (MemoryLayout<Zone>.size - MemoryLayout<Element>.size) / MemoryLayout<Element>.stride
}
var count: Int { Self.count }
init(repeating element: Element, in _: Zone.Type) {
precondition(_isPOD(Element.self))
precondition(_isPOD(Zone.self))
// hack ahead: this magically makes zone to be `.some`
withUnsafeMutableBytes(of: &zone) { p in
_ = memset(p.baseAddress!, 0, MemoryLayout<Zone?>.size)
}
precondition(zone != nil, "redo the above hack")
for i in 0 ..< count {
self[i] = element
}
}
subscript(_ index: Index) -> Element {
get {
precondition(index >= 0 && index < count)
return withUnsafeBytes(of: zone) {
$0.baseAddress!.assumingMemoryBound(to: Element.self)[index]
}
}
set {
precondition(index >= 0 && index < count)
withUnsafeMutableBytes(of: &zone) {
$0.baseAddress!.assumingMemoryBound(to: Element.self)[index] = newValue
}
}
}
}
Some internals
struct X1<T> { var x: (T) }
struct X2<T> { var x: (T, T) }
struct X3<T> { var x: (T, T, T) }
struct X4<T> { var x: (T, T, T, T) }
struct X5<T> { var x: (T, T, T, T, T) }
struct X6<T> { var x: (T, T, T, T, T, T) }
struct X7<T> { var x: (T, T, T, T, T, T, T) }
struct X8<T> { var x: (T, T, T, T, T, T, T, T) }
struct X9<T> { var x: (T, T, T, T, T, T, T, T, T) }
struct X10<T> { var x: (T, T, T, T, T, T, T, T, T, T) }
typealias X20<T> = X2<X10<T>>
typealias X30<T> = X3<X10<T>>
typealias X40<T> = X4<X10<T>>
typealias X50<T> = X5<X10<T>>
typealias X60<T> = X6<X10<T>>
typealias X70<T> = X7<X10<T>>
typealias X80<T> = X8<X10<T>>
typealias X90<T> = X9<X10<T>>
typealias X100<T> = X10<X10<T>>
typealias X200<T> = X2<X100<T>>
typealias X300<T> = X3<X100<T>>
typealias X400<T> = X4<X100<T>>
typealias X500<T> = X5<X100<T>>
typealias X600<T> = X6<X100<T>>
typealias X700<T> = X7<X100<T>>
typealias X800<T> = X8<X100<T>>
typealias X900<T> = X9<X100<T>>
typealias X1000<T> = X10<X100<T>>
Usage example:
// Usage example:
struct S {
var x: Int16 = 1111
var y: UInt8 = 22
}
let elementCount = 1243
typealias X1243<T> = (X1000<T>, X200<T>, X40<T>, X3<T>)
var array = FixedArray(repeating: S(), in: X1243<S>.self)
print(MemoryLayout<S>.size, MemoryLayout<S>.stride, MemoryLayout<S>.alignment) // 3 4 2
let expectedZoneSize = (elementCount - 1) * MemoryLayout<S>.stride + MemoryLayout<S>.size
precondition(MemoryLayout<X1243<S>>.size == expectedZoneSize)
precondition(MemoryLayout<FixedArray<S, X1243<S>>>.size == expectedZoneSize + 1)
precondition(array.count == elementCount)
print(array[elementCount - 1]) // S(x: 1111, y: 22)
array[elementCount - 1] = S(x: 3333, y: 44)
print(array[elementCount - 1]) // S(x: 3333, y: 44)
The size of FixedArray is one byte more than the size of the actual payload, this is because I'm using an optional:
private var zone: Zone?
I haven't figured out how to not have this optional (and thus the extra byte) in an ergonomic way.
Edited the code above.