summaryrefslogtreecommitdiff
path: root/reference/C/CONTRIB/SNIP/mdalloc.c
blob: 171d464b69d6dba6c6cbff35e09dd035bd66dbf7 (plain)
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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
/*   Written by Blair Haukedal 91/09 and placed in the public domain */

/*  mdalloc - a multi dimensional array allocator
 *  mdfree  - a companion function to mdalloc for freeing storage
 *  synopsis:
 *      void *mdalloc(int ndim, int width, ...);
 *          where:  ndim:   number of array dimensions
 *                  width:  size of elements in array
 *                  variable args are dimensions of array
 *          returns: n-way indirect pointer to allocated storage
 *                   or NULL if insufficient storage
 *
 *      void mdfree(void *p, ndim);
 *          where:  p:      pointer to storage obtained by mdalloc
 *                  ndim:   number of dimensions used in mdalloc
 *
 *  example:
 *      int ***tip;
 *      tip = mdalloc(3, sizeof(int), 2, 3, 4);
 *        tip will be a triple indirect pointer to a 3 dimensional array
 *        tip[0][0][0] refers to the first int in a contiguous area of
 *                     storage that is 2*3*4*sizeof(int) bytes long
 *        tip[0][0] is the address of the first int
 *      memset can be used to initialize array elements as follows:
 *        memset(tip[0][0], 0, 2*3*4*sizeof(int));
 *      mdfree is used to free storage obtained with mdalloc:
 *        mdfree(tip, 3)
 *
 *  notes:
 *      - must be compiled with appropriate memory model
 *      - memory is allocated for each dimension for indirect pointers
 *          eg. 3x4x5 array of longs
 *              (assuming 4 byte longs, small mem model)
 *              p = mdalloc(3, sizeof(long), 3, 4, 5)            - bytes
 *                  3       pointers allocated for 1st dimension -  6
 *                  3x4     pointers allocated for 2nd dimension -  24
 *                  3x4x5   longs allocated for array elements   -  240
 *              total of 270 bytes allocated
 *      - if insufficient memory, nothing will be allocated.
 *          ie. intermediate pointer arrays that were successfully
 *              allocated will be freed.
 *      - the intent of mdalloc is to facilitate dynamic array creation,
 *        it will use more memory than statically declared arrays, and
 *        the required dereferencing will be slower than the use of
 *        statically declared arrays.
 *      - this function assumes that sizeof(char) == 1.
 */

#include <stdarg.h>
#include <stdlib.h>

static void **md2(int n_units, int ndim, int *dims);
static void md3(char ***tip, int n_units, int ndim, int *dims);

static int w_units;

/* mdalloc: entry point for mdalloc function described above
 *      - reduces variable arg list to fixed list with last arg
 *      represented as pointer to int (array dimensions).
 *      Calls md2 to allocate storage.
 *      Calls md3 to initialize intermediate pointers.
 *      Returns pointer.
 */

void *mdalloc(int ndim, int width, ...)
{
      va_list argp;
      int *dims, i;
      char ***tip;

      va_start(argp, width);

      /* allocate storage for variable args (dimensions) */

      dims = malloc(ndim*sizeof(int));
      if(dims == NULL)
            return NULL;

      /* initialize dimensions array for subsequent calls */

      for(i=0; i<ndim; i++)
            dims[i] = va_arg(argp,int);

      w_units = width;    /* global used by md2 and md3 */

      /* allocate required pointer and array element storage */

      tip = (char ***)md2(dims[0], ndim, &dims[1]);

      if(ndim>1 && tip)
            md3(tip, dims[0], ndim-1, &dims[1]); /* init pointers */

      free(dims);
      return tip;
}

/* mdfree:  companion function to mdalloc
 *          frees storage obtained by mdalloc
 */

void mdfree(void *tip, int ndim)
{
      if(ndim == 1)
            free(tip);
      else
      {
            mdfree(((void **)tip)[0], ndim-1);
            free(tip);
      }
}

/* md2:  allocates storage for n-way indirect pointer arrays
 *       allocates storage for requested array elements
 */

static void **md2(int n_units, int ndim, int *dims)
{
      char **tip;

      if(ndim == 1)
            /* recursed to final dimension - allocate element storage */
            tip = malloc(n_units*w_units);
      else
      {
            /* allocate pointer array for dimension n */
            tip = malloc(n_units*sizeof(char *));
            if(tip)
            {
                  /* recurse until final dimension */
                  tip[0] = (char *)md2(n_units*dims[0], ndim-1, &dims[1]);
                  if(tip[0] == NULL)
                  {
                        /* allocate error - fall back up freeing everything */
                        free(tip);
                        tip = NULL;
                  }
            }
      }
      return (void **)tip;
}

/* md3: initializes indirect pointer arrays */

static void md3(char ***tip, int n_units, int ndim, int *dims)
{
      int i;

      for(i=1; i<n_units; i++)
      {
            if(ndim == 1)
                  /* final dimension - must scale by element width */
                  tip[i] = (char **)((char *)tip[0] + i*dims[0]*w_units);
            else
                  /* intermediate dimension - scale by pointer size */
                  tip[i] = tip[0] + i*dims[0];
      }
      if(ndim > 1)
            /* not at final dimension - continue to recurse */
            md3((char ***)tip[0], n_units*dims[0], ndim-1, &dims[1]);
}