Approaches for fixed-size arrays

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.

1 Like