Below is a ShapedArray
structure for working with 1D, 2D, and 3D arrays. The DataBuffer
class holds a reference to the underlying mutable buffer for data storage. A flatIndex
function determines the one-dimensional index for the subscript getter and setter properties.
struct ShapedArray<T> {
let shape: [Int]
private let data: DataBuffer<T>
var buffer: UnsafeMutableBufferPointer<T> {
get { self.data.buffer }
set { self.data.buffer = newValue }
}
init(_ array: [T]) {
self.shape = [array.count]
self.data = DataBuffer(array: array)
}
init(_ array: [T], shape: Int...) {
self.shape = shape
self.data = DataBuffer(array: array)
}
init(_ array2D: [[T]]) {
let rows = array2D.count
let columns = array2D[0].count
self.shape = [rows, columns]
self.data = DataBuffer(array: array2D.flatMap { $0 })
}
subscript(index: Int...) -> T {
get {
let idx = flatIndex(index: index, shape: self.shape)
return self.buffer[idx]
}
set {
let idx = flatIndex(index: index, shape: self.shape)
self.buffer[idx] = newValue
}
}
}
func flatIndex(index: [Int], shape: [Int]) -> Int {
var idx = 0
if shape.count == 1 {
idx = index[0] // index in 1D array
} else if shape.count == 2 {
let i = index[0] // row index in 2D array
let j = index[1] // column column 2D array
let ncols = shape[1]
idx = (i * ncols) + j
} else {
let k = index[0] // depth index in 3D array
let i = index[1] // row index in 3D array
let j = index[2] // column index in 3D array
let nrows = shape[1]
let ncols = shape[2]
idx = (i * ncols) + j + (k * nrows * ncols)
}
return idx
}
class DataBuffer<T> {
var buffer: UnsafeMutableBufferPointer<T>
init(array: [T]) {
self.buffer = UnsafeMutableBufferPointer<T>.allocate(capacity: array.count)
_ = self.buffer.initialize(fromContentsOf: array)
}
deinit {
self.buffer.deinitialize()
self.buffer.deallocate()
}
}
Here is a 3D array example:
let a = [1, 2, 3, 4, 5,
6, 7, 8, 9, 10,
11, 12, 13, 14 ,15,
16, 17, 18, 19, 20]
let b = ShapedArray(a, shape: 2, 2, 5)
print("shape is \(b.shape)")
print(b[0, 1, 4])
And here is a 4D array example where the subscript does not work:
let a = [1, 2, 3, 4,
5, 6, 7, 8,
9, 10, 11, 12,
13, 14, 15, 16,
17, 18, 19, 20,
21, 22, 23, 24,
25, 26, 27, 28,
29, 30, 31, 32,
33, 34, 35, 36,
37, 38, 39, 40,
41, 42, 43, 44,
45, 46, 47, 48]
let b = ShapedArray(a, shape: 2, 3, 2, 4)
The flatIndex
function works fine for 1D, 2D, 3D arrays but how do I adapt it to handle higher dimensions like the 4D example shown above?