Add Z-buffer, fixes issue with pyramidal studs
[render.git] / array.h
1 #ifndef _ARRAY_H
2 #define _ARRAY_H
3
4 #include <assert.h>
5 #include <stdlib.h>
6 #include <string.h>
7
8 #define ALIGN 0x10 
9
10 // stride array has length n_dims + 1
11 // stride[0] is size of the data
12 // stride[n_dims] == 1
13 // note that a zero-dimensional array is a scalar (with size 1 byte)
14 struct array {
15   int n_dims;
16   void *data;
17   size_t *stride;
18   size_t dim[0];
19 };
20
21 // following constructor is geared towards being called from array_dupN()
22 // which already has the strides available, so it doesn't calculate them
23 static inline __attribute__((always_inline)) struct array *array_new(
24   int n_dims,
25   const size_t *dim,
26   const size_t *stride
27 ) {
28 #ifndef NDEBUG
29   {
30     size_t s = 1;
31     int i = n_dims;
32     while (i >= 1) {
33       assert(stride[i] == s);
34       s *= dim[--i];
35     }
36     assert(stride[i] == s);
37   }
38 #endif
39
40   int header = (
41     sizeof(struct array) +
42     sizeof(size_t) +
43     ALIGN -
44     1 +
45     n_dims * (2 * sizeof(size_t))
46   ) & ~(ALIGN - 1);
47   struct array *p = aligned_alloc(ALIGN, header + stride[0]);
48   if (p == NULL) {
49     perror("aligned_alloc()");
50     exit(EXIT_FAILURE);
51   }
52
53   p->n_dims = n_dims;
54   p->data = (char *)p + header;
55   p->stride = p->dim + n_dims;
56   memcpy(p->dim, dim, n_dims * sizeof(size_t));
57   memcpy(p->stride, stride, (n_dims + 1) * sizeof(size_t));
58   return p;
59 }
60
61 static inline __attribute__((always_inline)) void array_free(
62   struct array *self
63 ) {
64   free(self);
65 }
66
67 // following are more optimized constructors for when number of dimensions
68 // is known but strides are not known and must be calculated from dimensions
69 static inline __attribute__((always_inline)) struct array *array_new0(void) {
70   struct array *p = aligned_alloc(
71     ALIGN,
72     (
73       (sizeof(struct array) + sizeof(size_t) + ALIGN - 1) & ~(ALIGN - 1)
74     ) + 1
75   );
76   if (p == NULL) {
77     perror("aligned_alloc()");
78     exit(EXIT_FAILURE);
79   }
80
81   p->n_dims = 0;
82   p->data = (char *)p + (
83     (sizeof(struct array) + sizeof(size_t) + ALIGN - 1) & ~(ALIGN - 1)
84   );
85   p->stride = p->dim;
86   p->stride[0] = 1;
87   return p;
88 }
89
90 static inline __attribute__((always_inline)) struct array *array_new0_init(
91   const void *src
92 ) {
93   struct array *p = array_new0();
94   memcpy(p->data, src, p->stride[0]);
95   return p;
96 }
97
98 static inline __attribute__((always_inline)) struct array *array_new0_zero(void) {
99   struct array *p = array_new0();
100   memset(p->data, 0, p->stride[0]);
101   return p;
102 }
103
104 static inline __attribute__((always_inline)) struct array *array_new1(
105   size_t dim0
106 ) {
107   struct array *p = aligned_alloc(
108     ALIGN,
109     (
110       (sizeof(struct array) + 3 * sizeof(size_t) + ALIGN - 1) & ~(ALIGN - 1)
111     ) + dim0
112   );
113   if (p == NULL) {
114     perror("aligned_alloc()");
115     exit(EXIT_FAILURE);
116   }
117
118   p->n_dims = 1;
119   p->data = (char *)p + (
120     (sizeof(struct array) + 3 * sizeof(size_t) + ALIGN - 1) & ~(ALIGN - 1)
121   );
122   p->stride = p->dim + 1;
123   p->dim[0] = dim0;
124   p->stride[1] = 1;
125   p->stride[0] = dim0;
126   return p;
127 }
128
129 static inline __attribute__((always_inline)) struct array *array_new1_init(
130   size_t dim0,
131   const void *src
132 ) {
133   struct array *p = array_new1(dim0);
134   memcpy(p->data, src, p->stride[0]);
135   return p;
136 }
137
138 static inline __attribute__((always_inline)) struct array *array_new1_zero(
139   size_t dim0
140 ) {
141   struct array *p = array_new1(dim0);
142   memset(p->data, 0, p->stride[0]);
143   return p;
144 }
145  
146 static inline __attribute__((always_inline)) struct array *array_new2(
147   size_t dim0,
148   size_t dim1
149 ) {
150   struct array *p = aligned_alloc(
151     ALIGN,
152     (
153       (sizeof(struct array) + 5 * sizeof(size_t) + ALIGN - 1) & ~(ALIGN - 1)
154     ) + dim0 * dim1
155   );
156   if (p == NULL) {
157     perror("aligned_alloc()");
158     exit(EXIT_FAILURE);
159   }
160
161   p->n_dims = 2;
162   p->data = (char *)p + (
163     (sizeof(struct array) + 5 * sizeof(size_t) + ALIGN - 1) & ~(ALIGN - 1)
164   );
165   p->stride = p->dim + 2;
166   p->dim[0] = dim0;
167   p->dim[1] = dim1;
168   p->stride[2] = 1;
169   p->stride[1] = dim1;
170   p->stride[0] = dim0 * dim1;
171   return p;
172 }
173
174 static inline __attribute__((always_inline)) struct array *array_new2_init(
175   size_t dim0,
176   size_t dim1,
177   const void *src
178 ) {
179   struct array *p = array_new2(dim0, dim1);
180   memcpy(p->data, src, p->stride[0]);
181   return p;
182 }
183
184 static inline __attribute__((always_inline)) struct array *array_new2_zero(
185   size_t dim0,
186   size_t dim1
187 ) {
188   struct array *p = array_new2(dim0, dim1);
189   memset(p->data, 0, p->stride[0]);
190   return p;
191 }
192  
193 static inline __attribute__((always_inline)) struct array *array_new3(
194   size_t dim0,
195   size_t dim1,
196   size_t dim2
197 ) {
198   struct array *p = aligned_alloc(
199     ALIGN,
200     (
201       (sizeof(struct array) + 7 * sizeof(size_t) + ALIGN - 1) & ~(ALIGN - 1)
202     ) + dim0 * dim1 * dim2
203   );
204   if (p == NULL) {
205     perror("aligned_alloc()");
206     exit(EXIT_FAILURE);
207   }
208
209   p->n_dims = 3;
210   p->data = (char *)p + (
211     (sizeof(struct array) + 7 * sizeof(size_t) + ALIGN - 1) & ~(ALIGN - 1)
212   );
213   p->stride = p->dim + 3;
214   p->dim[0] = dim0;
215   p->dim[1] = dim1;
216   p->dim[2] = dim2;
217   p->stride[3] = 1;
218   p->stride[2] = dim2;
219   p->stride[1] = dim1 * dim2;
220   p->stride[0] = dim0 * p->stride[1];
221   return p;
222 }
223
224 static inline __attribute__((always_inline)) struct array *array_new3_init(
225   size_t dim0,
226   size_t dim1,
227   size_t dim2,
228   const void *src
229 ) {
230   struct array *p = array_new3(dim0, dim1, dim2);
231   memcpy(p->data, src, p->stride[0]);
232   return p;
233 }
234
235 static inline __attribute__((always_inline)) struct array *array_new3_zero(
236   size_t dim0,
237   size_t dim1,
238   size_t dim2
239 ) {
240   struct array *p = array_new3(dim0, dim1, dim2);
241   memset(p->data, 0, p->stride[0]);
242   return p;
243 }
244  
245 static inline __attribute__((always_inline)) struct array *array_new4(
246   size_t dim0,
247   size_t dim1,
248   size_t dim2,
249   size_t dim3
250 ) {
251   struct array *p = aligned_alloc(
252     ALIGN,
253     (
254       (sizeof(struct array) + 9 * sizeof(size_t) + ALIGN - 1) & ~(ALIGN - 1)
255     ) + dim0 * dim1 * dim2 * dim3
256   );
257   if (p == NULL) {
258     perror("aligned_alloc()");
259     exit(EXIT_FAILURE);
260   }
261
262   p->n_dims = 4;
263   p->data = (char *)p + (
264     (sizeof(struct array) + 9 * sizeof(size_t) + ALIGN - 1) & ~(ALIGN - 1)
265   );
266   p->stride = p->dim + 4;
267   p->dim[0] = dim0;
268   p->dim[1] = dim1;
269   p->dim[2] = dim2;
270   p->dim[3] = dim3;
271   p->stride[4] = 1;
272   p->stride[3] = dim3;
273   p->stride[2] = dim2 * dim3;
274   p->stride[1] = dim1 * p->stride[2];
275   p->stride[0] = dim0 * p->stride[1];
276   return p;
277 }
278
279 static inline __attribute__((always_inline)) struct array *array_new4_init(
280   size_t dim0,
281   size_t dim1,
282   size_t dim2,
283   size_t dim3,
284   const void *src
285 ) {
286   struct array *p = array_new4(dim0, dim1, dim2, dim3);
287   memcpy(p->data, src, p->stride[0]);
288   return p;
289 }
290
291 static inline __attribute__((always_inline)) struct array *array_new4_zero(
292   size_t dim0,
293   size_t dim1,
294   size_t dim2,
295   size_t dim3
296 ) {
297   struct array *p = array_new4(dim0, dim1, dim2, dim3);
298   memset(p->data, 0, p->stride[0]);
299   return p;
300 }
301
302 static inline __attribute__((always_inline)) void *array_index0(
303   const struct array *self
304 ) {
305   assert(self->n_dims >= 0);
306   return self->data;
307 }
308
309 static inline __attribute__((always_inline)) void *array_index1(
310   const struct array *self,
311   size_t i0
312 ) {
313   assert(self->n_dims >= 1);
314   assert(i0 >= 0 && i0 < self->dim[0]);
315   return
316     (char *)self->data +
317     i0 * self->stride[1];
318 }
319
320 static inline __attribute__((always_inline)) void *array_index2(
321   const struct array *self,
322   size_t i0,
323   size_t i1
324 ) {
325   assert(self->n_dims >= 2);
326   assert(i0 >= 0 && i0 < self->dim[0]);
327   assert(i1 >= 0 && i1 < self->dim[1]);
328   return
329     (char *)self->data +
330     i1 * self->stride[2] +
331     i0 * self->stride[1];
332 }
333
334 static inline __attribute__((always_inline)) void *array_index3(
335   const struct array *self,
336   size_t i0,
337   size_t i1,
338   size_t i2
339 ) {
340   assert(self->n_dims >= 3);
341   assert(i0 >= 0 && i0 < self->dim[0]);
342   assert(i1 >= 0 && i1 < self->dim[1]);
343   assert(i2 >= 0 && i2 < self->dim[2]);
344   return
345     (char *)self->data +
346     i2 * self->stride[3] +
347     i1 * self->stride[2] +
348     i0 * self->stride[1];
349 }
350
351 static inline __attribute__((always_inline)) void *array_index4(
352   const struct array *self,
353   size_t i0,
354   size_t i1,
355   size_t i2,
356   size_t i3
357 ) {
358   assert(self->n_dims >= 4);
359   assert(i0 >= 0 && i0 < self->dim[0]);
360   assert(i1 >= 0 && i1 < self->dim[1]);
361   assert(i2 >= 0 && i2 < self->dim[2]);
362   assert(i3 >= 0 && i3 < self->dim[3]);
363   return
364     (char *)self->data +
365     i3 * self->stride[4] +
366     i2 * self->stride[3] +
367     i1 * self->stride[2] +
368     i0 * self->stride[1];
369 }
370
371 static inline __attribute__((always_inline)) void array_get0(
372   const struct array *self,
373   void *dest
374 ) {
375   memcpy(dest, array_index0(self), self->stride[0]);
376 }
377
378 static inline __attribute__((always_inline)) void array_get1(
379   const struct array *self,
380   void *dest,
381   size_t i0
382 ) {
383   memcpy(dest, array_index1(self, i0), self->stride[1]);
384 }
385
386 static inline __attribute__((always_inline)) void array_get2(
387   const struct array *self,
388   void *dest,
389   size_t i0,
390   size_t i1
391 ) {
392   memcpy(dest, array_index2(self, i0, i1), self->stride[2]);
393 }
394
395 static inline __attribute__((always_inline)) void array_get3(
396   const struct array *self,
397   void *dest,
398   size_t i0,
399   size_t i1,
400   size_t i2
401 ) {
402   memcpy(dest, array_index3(self, i0, i1, i2), self->stride[3]);
403 }
404
405 static inline __attribute__((always_inline)) void array_get4(
406   const struct array *self,
407   void *dest,
408   size_t i0,
409   size_t i1,
410   size_t i2,
411   size_t i3
412 ) {
413   memcpy(dest, array_index4(self, i0, i1, i2, i3), self->stride[4]);
414 }
415
416 static inline __attribute__((always_inline)) void array_set0(
417   struct array *self,
418   const void *src
419 ) {
420   memcpy(array_index0(self), src, self->stride[0]);
421 }
422
423 static inline __attribute__((always_inline)) void array_set1(
424   struct array *self,
425   size_t i0,
426   const void *src
427 ) {
428   memcpy(array_index1(self, i0), src, self->stride[1]);
429 }
430
431 static inline __attribute__((always_inline)) void array_set2(
432   struct array *self,
433   size_t i0,
434   size_t i1,
435   const void *src
436 ) {
437   memcpy(array_index2(self, i0, i1), src, self->stride[2]);
438 }
439
440 static inline __attribute__((always_inline)) void array_set3(
441   struct array *self,
442   size_t i0,
443   size_t i1,
444   size_t i2,
445   const void *src
446 ) {
447   memcpy(array_index3(self, i0, i1, i2), src, self->stride[3]);
448 }
449
450 static inline __attribute__((always_inline)) void array_set4(
451   struct array *self,
452   size_t i0,
453   size_t i1,
454   size_t i2,
455   size_t i3,
456   const void *src
457 ) {
458   memcpy(array_index4(self, i0, i1, i2, i3), src, self->stride[4]);
459 }
460
461 static inline __attribute__((always_inline)) void array_clear0(
462   struct array *self
463 ) {
464   memset(array_index0(self), 0, self->stride[0]);
465 }
466
467 static inline __attribute__((always_inline)) void array_clear1(
468   struct array *self,
469   size_t i0
470 ) {
471   memset(array_index1(self, i0), 0, self->stride[1]);
472 }
473
474 static inline __attribute__((always_inline)) void array_clear2(
475   struct array *self,
476   size_t i0,
477   size_t i1
478 ) {
479   memset(array_index2(self, i0, i1), 0, self->stride[2]);
480 }
481
482 static inline __attribute__((always_inline)) void array_clear3(
483   struct array *self,
484   size_t i0,
485   size_t i1,
486   size_t i2
487 ) {
488   memset(array_index3(self, i0, i1, i2), 0, self->stride[3]);
489 }
490
491 static inline __attribute__((always_inline)) void array_clear4(
492   struct array *self,
493   size_t i0,
494   size_t i1,
495   size_t i2,
496   size_t i3
497 ) {
498   memset(array_index4(self, i0, i1, i2, i3), 0, self->stride[4]);
499 }
500
501 static inline __attribute__((always_inline)) struct array *array_dup0(
502   const struct array *self
503 ) {
504   assert(self->n_dims >= 0);
505   struct array *p = array_new(
506     self->n_dims,
507     self->dim,
508     self->stride
509   );
510   array_get0(self, p->data);
511   return p;
512 }
513
514 static inline __attribute__((always_inline)) struct array *array_dup1(
515   const struct array *self,
516   size_t i0
517 ) {
518   assert(self->n_dims >= 1);
519   struct array *p = array_new(
520     self->n_dims - 1,
521     self->dim + 1,
522     self->stride + 1
523   );
524   array_get1(self, p->data, i0);
525   return p;
526 }
527
528 static inline __attribute__((always_inline)) struct array *array_dup2(
529   const struct array *self,
530   size_t i0,
531   size_t i1
532 ) {
533   assert(self->n_dims >= 2);
534   struct array *p = array_new(
535     self->n_dims - 2,
536     self->dim + 2,
537     self->stride + 2
538   );
539   array_get2(self, p->data, i0, i1);
540   return p;
541 }
542
543 static inline __attribute__((always_inline)) struct array *array_dup3(
544   const struct array *self,
545   size_t i0,
546   size_t i1,
547   size_t i2
548 ) {
549   assert(self->n_dims >= 3);
550   struct array *p = array_new(
551     self->n_dims - 3,
552     self->dim + 3,
553     self->stride + 3
554   );
555   array_get3(self, p->data, i0, i1, i2);
556   return p;
557 }
558
559 static inline __attribute__((always_inline)) struct array *array_dup4(
560   const struct array *self,
561   size_t i0,
562   size_t i1,
563   size_t i2,
564   size_t i3
565 ) {
566   assert(self->n_dims >= 4);
567   struct array *p = array_new(
568     self->n_dims - 4,
569     self->dim + 4,
570     self->stride + 4
571   );
572   array_get4(self, p->data, i0, i1, i2, i3);
573   return p;
574 }
575
576 #endif