Improve array package so that dimensions are stored separately to strides
authorNick Downing <nick@ndcode.org>
Tue, 19 Apr 2022 05:22:28 +0000 (15:22 +1000)
committerNick Downing <nick@ndcode.org>
Tue, 19 Apr 2022 05:22:37 +0000 (15:22 +1000)
array.h
render.c

diff --git a/array.h b/array.h
index df20440..c494403 100644 (file)
--- a/array.h
+++ b/array.h
@@ -7,39 +7,83 @@
 
 #define ALIGN 0x10 
 
-// data is at struct address + header
 // stride array has length n_dims + 1
 // stride[0] is size of the data
-// stride[n_dims] == 1 except if n_dims == 0
-// dimension i has size stride[i] / stride[i + 1], where i < n_dims
+// stride[n_dims] == 1
+// note that a zero-dimensional array is a scalar (with size 1 byte)
 struct array {
-  int header;
   int n_dims;
-  size_t stride[0];
+  void *data;
+  size_t *stride;
+  size_t dim[0];
 };
 
+static inline __attribute__((always_inline)) void *array_new(
+  int n_dims,
+  size_t *dim,
+  size_t *stride
+) {
+#ifndef NDEBUG
+  {
+    size_t s = 1;
+    int i = n_dims;
+    while (i >= 1) {
+      assert(stride[i] == s);
+      s *= dim[--i];
+    }
+    assert(stride[i] == s);
+  }
+#endif
+
+  int header = (
+    sizeof(struct array) +
+    sizeof(size_t) +
+    ALIGN -
+    1 +
+    n_dims * (2 * sizeof(size_t))
+  ) & ~ALIGN;
+  struct array *p = aligned_alloc(ALIGN, header + stride[0]);
+  if (p == NULL) {
+    perror("aligned_alloc()");
+    exit(EXIT_FAILURE);
+  }
+
+  p->n_dims = n_dims;
+  p->data = (char *)p + header;
+  p->stride = p->dim + n_dims;
+  memcpy(p->dim, dim, n_dims * sizeof(size_t));
+  memcpy(p->stride, stride, (n_dims + 1) * sizeof(size_t));
+  return p;
+}
+
 static inline __attribute__((always_inline)) void *array_new0(void) {
   struct array *p = aligned_alloc(
     ALIGN,
-    sizeof(struct array) + sizeof(size_t)
+    (
+      (sizeof(struct array) + sizeof(size_t) + ALIGN - 1) & ~ALIGN
+    ) + 1
   );
   if (p == NULL) {
     perror("aligned_alloc()");
     exit(EXIT_FAILURE);
   }
 
-  p->header =
-    (sizeof(struct array) + sizeof(size_t) + ALIGN - 1) & ~ALIGN;
   p->n_dims = 0;
-  p->stride[0] = 0;
+  p->data = (char *)p + (
+    (sizeof(struct array) + sizeof(size_t) + ALIGN - 1) & ~ALIGN
+  );
+  p->stride = p->dim;
+  p->stride[0] = 1;
   return p;
 }
 
-static inline __attribute__((always_inline)) void *array_new1(size_t dim0) {
+static inline __attribute__((always_inline)) void *array_new1(
+  size_t dim0
+) {
   struct array *p = aligned_alloc(
     ALIGN,
     (
-      (sizeof(struct array) + 2 * sizeof(size_t) + ALIGN - 1) & ~ALIGN
+      (sizeof(struct array) + 3 * sizeof(size_t) + ALIGN - 1) & ~ALIGN
     ) + dim0
   );
   if (p == NULL) {
@@ -47,19 +91,25 @@ static inline __attribute__((always_inline)) void *array_new1(size_t dim0) {
     exit(EXIT_FAILURE);
   }
 
-  p->header =
-    (sizeof(struct array) + 2 * sizeof(size_t) + ALIGN - 1) & ~ALIGN;
   p->n_dims = 1;
+  p->data = (char *)p + (
+    (sizeof(struct array) + 3 * sizeof(size_t) + ALIGN - 1) & ~ALIGN
+  );
+  p->stride = p->dim + 1;
+  p->dim[0] = dim0;
   p->stride[1] = 1;
   p->stride[0] = dim0;
   return p;
 }
 
-static inline __attribute__((always_inline)) void *array_new2(size_t dim0, size_t dim1) {
+static inline __attribute__((always_inline)) void *array_new2(
+  size_t dim0,
+  size_t dim1
+) {
   struct array *p = aligned_alloc(
     ALIGN,
     (
-      (sizeof(struct array) + 3 * sizeof(size_t) + ALIGN - 1) & ~ALIGN
+      (sizeof(struct array) + 5 * sizeof(size_t) + ALIGN - 1) & ~ALIGN
     ) + dim0 * dim1
   );
   if (p == NULL) {
@@ -67,20 +117,28 @@ static inline __attribute__((always_inline)) void *array_new2(size_t dim0, size_
     exit(EXIT_FAILURE);
   }
 
-  p->header =
-    (sizeof(struct array) + 3 * sizeof(size_t) + ALIGN - 1) & ~ALIGN;
   p->n_dims = 2;
+  p->data = (char *)p + (
+    (sizeof(struct array) + 5 * sizeof(size_t) + ALIGN - 1) & ~ALIGN
+  );
+  p->stride = p->dim + 2;
+  p->dim[0] = dim0;
+  p->dim[1] = dim1;
   p->stride[2] = 1;
   p->stride[1] = dim1;
   p->stride[0] = dim0 * dim1;
   return p;
 }
 
-static inline __attribute__((always_inline)) void *array_new3(size_t dim0, size_t dim1, size_t dim2) {
+static inline __attribute__((always_inline)) void *array_new3(
+  size_t dim0,
+  size_t dim1,
+  size_t dim2
+) {
   struct array *p = aligned_alloc(
     ALIGN,
     (
-      (sizeof(struct array) + 4 * sizeof(size_t) + ALIGN - 1) & ~ALIGN
+      (sizeof(struct array) + 7 * sizeof(size_t) + ALIGN - 1) & ~ALIGN
     ) + dim0 * dim1 * dim2
   );
   if (p == NULL) {
@@ -88,9 +146,14 @@ static inline __attribute__((always_inline)) void *array_new3(size_t dim0, size_
     exit(EXIT_FAILURE);
   }
 
-  p->header =
-    (sizeof(struct array) + 4 * sizeof(size_t) + ALIGN - 1) & ~ALIGN;
   p->n_dims = 3;
+  p->data = (char *)p + (
+    (sizeof(struct array) + 7 * sizeof(size_t) + ALIGN - 1) & ~ALIGN
+  );
+  p->stride = p->dim + 3;
+  p->dim[0] = dim0;
+  p->dim[1] = dim1;
+  p->dim[2] = dim2;
   p->stride[3] = 1;
   p->stride[2] = dim2;
   p->stride[1] = dim1 * dim2;
@@ -98,11 +161,16 @@ static inline __attribute__((always_inline)) void *array_new3(size_t dim0, size_
   return p;
 }
 
-static inline __attribute__((always_inline)) void *array_new4(size_t dim0, size_t dim1, size_t dim2, size_t dim3) {
+static inline __attribute__((always_inline)) void *array_new4(
+  size_t dim0,
+  size_t dim1,
+  size_t dim2,
+  size_t dim3
+) {
   struct array *p = aligned_alloc(
     ALIGN,
     (
-      (sizeof(struct array) + 5 * sizeof(size_t) + ALIGN - 1) & ~ALIGN
+      (sizeof(struct array) + 9 * sizeof(size_t) + ALIGN - 1) & ~ALIGN
     ) + dim0 * dim1 * dim2 * dim3
   );
   if (p == NULL) {
@@ -110,9 +178,15 @@ static inline __attribute__((always_inline)) void *array_new4(size_t dim0, size_
     exit(EXIT_FAILURE);
   }
 
-  p->header =
-    (sizeof(struct array) + 5 * sizeof(size_t) + ALIGN - 1) & ~ALIGN;
-  p->n_dims = 3;
+  p->n_dims = 4;
+  p->data = (char *)p + (
+    (sizeof(struct array) + 9 * sizeof(size_t) + ALIGN - 1) & ~ALIGN
+  );
+  p->stride = p->dim + 3;
+  p->dim[0] = dim0;
+  p->dim[1] = dim1;
+  p->dim[2] = dim2;
+  p->dim[3] = dim3;
   p->stride[4] = 1;
   p->stride[3] = dim3;
   p->stride[2] = dim2 * dim3;
@@ -121,72 +195,56 @@ static inline __attribute__((always_inline)) void *array_new4(size_t dim0, size_
   return p;
 }
 
-static inline __attribute__((always_inline)) void array_free(struct array *self) {
+static inline __attribute__((always_inline)) void array_free(
+  struct array *self
+) {
   free(self);
 }
 
 static inline __attribute__((always_inline)) void *array_index0(
-  struct array *self,
-  size_t *size
+  struct array *self
 ) {
-  *size = self->stride[0];
-  return
-    (char *)self +
-    self->header;
+  assert(self->n_dims >= 0);
+  return self->data;
 }
 
 static inline __attribute__((always_inline)) void *array_index1(
   struct array *self,
-  size_t *size,
   size_t i0
 ) {
   assert(self->n_dims >= 1);
-#ifndef NDEBUG
-  assert(i0 >= 0 && i0 < self->stride[0] / self->stride[1]);
-#endif
-  *size = self->stride[1];
+  assert(i0 >= 0 && i0 < self->dim[0]);
   return
-    (char *)self +
-    self->header +
+    (char *)self->data +
     i0 * self->stride[1];
 }
 
 static inline __attribute__((always_inline)) void *array_index2(
   struct array *self,
-  size_t *size,
   size_t i0,
   size_t i1
 ) {
   assert(self->n_dims >= 2);
-#ifndef NDEBUG
-  assert(i0 >= 0 && i0 < self->stride[0] / self->stride[1]);
-  assert(i1 >= 0 && i1 < self->stride[1] / self->stride[2]);
-#endif
-  *size = self->stride[2];
+  assert(i0 >= 0 && i0 < self->dim[0]);
+  assert(i1 >= 0 && i1 < self->dim[1]);
   return
-    (char *)self +
-    self->header +
+    (char *)self->data +
     i1 * self->stride[2] +
     i0 * self->stride[1];
 }
 
 static inline __attribute__((always_inline)) void *array_index3(
   struct array *self,
-  size_t *size,
   size_t i0,
   size_t i1,
   size_t i2
 ) {
   assert(self->n_dims >= 3);
-#ifndef NDEBUG
-  assert(i0 >= 0 && i0 < self->stride[0] / self->stride[1]);
-  assert(i1 >= 0 && i1 < self->stride[1] / self->stride[2]);
-  assert(i2 >= 0 && i2 < self->stride[2] / self->stride[3]);
-#endif
-  *size = self->stride[3];
+  assert(i0 >= 0 && i0 < self->dim[0]);
+  assert(i1 >= 0 && i1 < self->dim[1]);
+  assert(i2 >= 0 && i2 < self->dim[2]);
   return
-    (char *)self +
-    self->header +
+    (char *)self->data +
     i2 * self->stride[3] +
     i1 * self->stride[2] +
     i0 * self->stride[1];
@@ -194,23 +252,18 @@ static inline __attribute__((always_inline)) void *array_index3(
 
 static inline __attribute__((always_inline)) void *array_index4(
   struct array *self,
-  size_t *size,
   size_t i0,
   size_t i1,
   size_t i2,
   size_t i3
 ) {
   assert(self->n_dims >= 4);
-#ifndef NDEBUG
-  assert(i0 >= 0 && i0 < self->stride[0] / self->stride[1]);
-  assert(i1 >= 0 && i1 < self->stride[1] / self->stride[2]);
-  assert(i2 >= 0 && i2 < self->stride[2] / self->stride[3]);
-  assert(i3 >= 0 && i2 < self->stride[3] / self->stride[4]);
-#endif
-  *size = self->stride[4];
+  assert(i0 >= 0 && i0 < self->dim[0]);
+  assert(i1 >= 0 && i1 < self->dim[1]);
+  assert(i2 >= 0 && i2 < self->dim[2]);
+  assert(i3 >= 0 && i3 < self->dim[3]);
   return
-    (char *)self +
-    self->header +
+    (char *)self->data +
     i3 * self->stride[4] +
     i2 * self->stride[3] +
     i1 * self->stride[2] +
@@ -221,9 +274,7 @@ static inline __attribute__((always_inline)) void array_get0(
   struct array *self,
   void *dest
 ) {
-  size_t size;
-  void *p = array_index0(self, &size);
-  memcpy(dest, p, size);
+  memcpy(dest, array_index0(self), self->stride[0]);
 }
 
 static inline __attribute__((always_inline)) void array_get1(
@@ -231,9 +282,7 @@ static inline __attribute__((always_inline)) void array_get1(
   void *dest,
   size_t i0
 ) {
-  size_t size;
-  void *p = array_index1(self, &size, i0);
-  memcpy(dest, p, size);
+  memcpy(dest, array_index1(self, i0), self->stride[1]);
 }
 
 static inline __attribute__((always_inline)) void array_get2(
@@ -242,9 +291,7 @@ static inline __attribute__((always_inline)) void array_get2(
   size_t i0,
   size_t i1
 ) {
-  size_t size;
-  void *p = array_index2(self, &size, i0, i1);
-  memcpy(dest, p, size);
+  memcpy(dest, array_index2(self, i0, i1), self->stride[2]);
 }
 
 static inline __attribute__((always_inline)) void array_get3(
@@ -254,9 +301,7 @@ static inline __attribute__((always_inline)) void array_get3(
   size_t i1,
   size_t i2
 ) {
-  size_t size;
-  void *p = array_index3(self, &size, i0, i1, i2);
-  memcpy(dest, p, size);
+  memcpy(dest, array_index3(self, i0, i1, i2), self->stride[3]);
 }
 
 static inline __attribute__((always_inline)) void array_get4(
@@ -267,18 +312,14 @@ static inline __attribute__((always_inline)) void array_get4(
   size_t i2,
   size_t i3
 ) {
-  size_t size;
-  void *p = array_index4(self, &size, i0, i1, i2, i3);
-  memcpy(dest, p, size);
+  memcpy(dest, array_index4(self, i0, i1, i2, i3), self->stride[4]);
 }
 
 static inline __attribute__((always_inline)) void array_set0(
   struct array *self,
   const void *src
 ) {
-  size_t size;
-  void *p = array_index0(self, &size);
-  memcpy(p, src, size);
+  memcpy(array_index0(self), src, self->stride[0]);
 }
 
 static inline __attribute__((always_inline)) void array_set1(
@@ -286,9 +327,7 @@ static inline __attribute__((always_inline)) void array_set1(
   const void *src,
   size_t i0
 ) {
-  size_t size;
-  void *p = array_index1(self, &size, i0);
-  memcpy(p, src, size);
+  memcpy(array_index1(self, i0), src, self->stride[1]);
 }
 
 static inline __attribute__((always_inline)) void array_set2(
@@ -297,9 +336,7 @@ static inline __attribute__((always_inline)) void array_set2(
   size_t i0,
   size_t i1
 ) {
-  size_t size;
-  void *p = array_index2(self, &size, i0, i1);
-  memcpy(p, src, size);
+  memcpy(array_index2(self, i0, i1), src, self->stride[2]);
 }
 
 static inline __attribute__((always_inline)) void array_set3(
@@ -309,9 +346,7 @@ static inline __attribute__((always_inline)) void array_set3(
   size_t i1,
   size_t i2
 ) {
-  size_t size;
-  void *p = array_index3(self, &size, i0, i1, i2);
-  memcpy(p, src, size);
+  memcpy(array_index3(self, i0, i1, i2), src, self->stride[3]);
 }
 
 static inline __attribute__((always_inline)) void array_set4(
@@ -322,26 +357,20 @@ static inline __attribute__((always_inline)) void array_set4(
   size_t i2,
   size_t i3
 ) {
-  size_t size;
-  void *p = array_index4(self, &size, i0, i1, i2, i3);
-  memcpy(p, src, size);
+  memcpy(array_index4(self, i0, i1, i2, i3), src, self->stride[4]);
 }
 
 static inline __attribute__((always_inline)) void array_clear0(
   struct array *self
 ) {
-  size_t size;
-  void *p = array_index0(self, &size);
-  memset(p, 0, size);
+  memset(array_index0(self), 0, self->stride[0]);
 }
 
 static inline __attribute__((always_inline)) void array_clear1(
   struct array *self,
   size_t i0
 ) {
-  size_t size;
-  void *p = array_index1(self, &size, i0);
-  memset(p, 0, size);
+  memset(array_index1(self, i0), 0, self->stride[1]);
 }
 
 static inline __attribute__((always_inline)) void array_clear2(
@@ -349,9 +378,7 @@ static inline __attribute__((always_inline)) void array_clear2(
   size_t i0,
   size_t i1
 ) {
-  size_t size;
-  void *p = array_index2(self, &size, i0, i1);
-  memset(p, 0, size);
+  memset(array_index2(self, i0, i1), 0, self->stride[2]);
 }
 
 static inline __attribute__((always_inline)) void array_clear3(
@@ -360,9 +387,7 @@ static inline __attribute__((always_inline)) void array_clear3(
   size_t i1,
   size_t i2
 ) {
-  size_t size;
-  void *p = array_index3(self, &size, i0, i1, i2);
-  memset(p, 0, size);
+  memset(array_index3(self, i0, i1, i2), 0, self->stride[3]);
 }
 
 static inline __attribute__((always_inline)) void array_clear4(
@@ -372,9 +397,82 @@ static inline __attribute__((always_inline)) void array_clear4(
   size_t i2,
   size_t i3
 ) {
-  size_t size;
-  void *p = array_index4(self, &size, i0, i1, i2, i3);
-  memset(p, 0, size);
+  memset(array_index4(self, i0, i1, i2, i3), 0, self->stride[4]);
+}
+
+static inline __attribute__((always_inline)) struct array *array_dup0(
+  struct array *self
+) {
+  assert(self->n_dims >= 0);
+  struct array *p = array_new(
+    self->n_dims,
+    self->dim,
+    self->stride
+  );
+  array_get0(self, p->data);
+  return p;
+}
+
+static inline __attribute__((always_inline)) struct array *array_dup1(
+  struct array *self,
+  size_t i0
+) {
+  assert(self->n_dims >= 1);
+  struct array *p = array_new(
+    self->n_dims - 1,
+    self->dim + 1,
+    self->stride + 1
+  );
+  array_get1(self, p->data, i0);
+  return p;
+}
+
+static inline __attribute__((always_inline)) struct array *array_dup2(
+  struct array *self,
+  size_t i0,
+  size_t i1
+) {
+  assert(self->n_dims >= 2);
+  struct array *p = array_new(
+    self->n_dims - 2,
+    self->dim + 2,
+    self->stride + 2
+  );
+  array_get2(self, p->data, i0, i1);
+  return p;
+}
+
+static inline __attribute__((always_inline)) struct array *array_dup3(
+  struct array *self,
+  size_t i0,
+  size_t i1,
+  size_t i2
+) {
+  assert(self->n_dims >= 3);
+  struct array *p = array_new(
+    self->n_dims - 3,
+    self->dim + 3,
+    self->stride + 3
+  );
+  array_get3(self, p->data, i0, i1, i2);
+  return p;
+}
+
+static inline __attribute__((always_inline)) struct array *array_dup4(
+  struct array *self,
+  size_t i0,
+  size_t i1,
+  size_t i2,
+  size_t i3
+) {
+  assert(self->n_dims >= 4);
+  struct array *p = array_new(
+    self->n_dims - 4,
+    self->dim + 4,
+    self->stride + 4
+  );
+  array_get4(self, p->data, i0, i1, i2, i3);
+  return p;
 }
 
 #endif
index de2fa8d..00d8674 100644 (file)
--- a/render.c
+++ b/render.c
@@ -133,8 +133,7 @@ void rotate(const float (*v)[4], struct array *transform) {
   array_set0(t0, v);
   struct array *t1 = array_new3(4, 4, sizeof(float));
   mul_mat44_mat44(t0, transform, t1);
-  size_t size;
-  array_set0(transform, array_index0(t1, &size));
+  array_set0(transform, array_index0(t1));
 }
 
 int main(void) {