PostgreSQL Source Code  git master
array_expanded.c File Reference
#include "postgres.h"
#include "access/tupmacs.h"
#include "utils/array.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
Include dependency graph for array_expanded.c:

Go to the source code of this file.

Functions

static Size EA_get_flat_size (ExpandedObjectHeader *eohptr)
 
static void EA_flatten_into (ExpandedObjectHeader *eohptr, void *result, Size allocated_size)
 
static void copy_byval_expanded_array (ExpandedArrayHeader *eah, ExpandedArrayHeader *oldeah)
 
Datum expand_array (Datum arraydatum, MemoryContext parentcontext, ArrayMetaState *metacache)
 
ExpandedArrayHeaderDatumGetExpandedArray (Datum d)
 
ExpandedArrayHeaderDatumGetExpandedArrayX (Datum d, ArrayMetaState *metacache)
 
AnyArrayTypeDatumGetAnyArrayP (Datum d)
 
void deconstruct_expanded_array (ExpandedArrayHeader *eah)
 

Variables

static const ExpandedObjectMethods EA_methods
 

Function Documentation

◆ copy_byval_expanded_array()

static void copy_byval_expanded_array ( ExpandedArrayHeader eah,
ExpandedArrayHeader oldeah 
)
static

Definition at line 185 of file array_expanded.c.

187 {
188  MemoryContext objcxt = eah->hdr.eoh_context;
189  int ndims = oldeah->ndims;
190  int dvalueslen = oldeah->dvalueslen;
191 
192  /* Copy array dimensionality information */
193  eah->ndims = ndims;
194  /* We can alloc both dimensionality arrays with one palloc */
195  eah->dims = (int *) MemoryContextAlloc(objcxt, ndims * 2 * sizeof(int));
196  eah->lbound = eah->dims + ndims;
197  /* .. but don't assume the source's arrays are contiguous */
198  memcpy(eah->dims, oldeah->dims, ndims * sizeof(int));
199  memcpy(eah->lbound, oldeah->lbound, ndims * sizeof(int));
200 
201  /* Copy element-type data */
202  eah->element_type = oldeah->element_type;
203  eah->typlen = oldeah->typlen;
204  eah->typbyval = oldeah->typbyval;
205  eah->typalign = oldeah->typalign;
206 
207  /* Copy the deconstructed representation */
208  eah->dvalues = (Datum *) MemoryContextAlloc(objcxt,
209  dvalueslen * sizeof(Datum));
210  memcpy(eah->dvalues, oldeah->dvalues, dvalueslen * sizeof(Datum));
211  if (oldeah->dnulls)
212  {
213  eah->dnulls = (bool *) MemoryContextAlloc(objcxt,
214  dvalueslen * sizeof(bool));
215  memcpy(eah->dnulls, oldeah->dnulls, dvalueslen * sizeof(bool));
216  }
217  else
218  eah->dnulls = NULL;
219  eah->dvalueslen = dvalueslen;
220  eah->nelems = oldeah->nelems;
221  eah->flat_size = oldeah->flat_size;
222 
223  /* we don't make a flat representation */
224  eah->fvalue = NULL;
225  eah->fstartptr = NULL;
226  eah->fendptr = NULL;
227 }
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:1181
uintptr_t Datum
Definition: postgres.h:64
char * fstartptr
Definition: array.h:166
char * fendptr
Definition: array.h:167
ExpandedObjectHeader hdr
Definition: array.h:118
Datum * dvalues
Definition: array.h:146
ArrayType * fvalue
Definition: array.h:165
MemoryContext eoh_context

References ExpandedArrayHeader::dims, ExpandedArrayHeader::dnulls, ExpandedArrayHeader::dvalues, ExpandedArrayHeader::dvalueslen, ExpandedArrayHeader::element_type, ExpandedObjectHeader::eoh_context, ExpandedArrayHeader::fendptr, ExpandedArrayHeader::flat_size, ExpandedArrayHeader::fstartptr, ExpandedArrayHeader::fvalue, ExpandedArrayHeader::hdr, ExpandedArrayHeader::lbound, MemoryContextAlloc(), ExpandedArrayHeader::ndims, ExpandedArrayHeader::nelems, ExpandedArrayHeader::typalign, ExpandedArrayHeader::typbyval, and ExpandedArrayHeader::typlen.

Referenced by expand_array().

◆ DatumGetAnyArrayP()

AnyArrayType* DatumGetAnyArrayP ( Datum  d)

Definition at line 401 of file array_expanded.c.

402 {
403  ExpandedArrayHeader *eah;
404 
405  /*
406  * If it's an expanded array (RW or RO), return the header pointer.
407  */
409  {
410  eah = (ExpandedArrayHeader *) DatumGetEOHP(d);
411  Assert(eah->ea_magic == EA_MAGIC);
412  return (AnyArrayType *) eah;
413  }
414 
415  /* Else do regular detoasting as needed */
416  return (AnyArrayType *) PG_DETOAST_DATUM(d);
417 }
#define EA_MAGIC
Definition: array.h:113
#define Assert(condition)
Definition: c.h:849
ExpandedObjectHeader * DatumGetEOHP(Datum d)
Definition: expandeddatum.c:29
#define PG_DETOAST_DATUM(datum)
Definition: fmgr.h:240
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:312
#define VARATT_IS_EXTERNAL_EXPANDED(PTR)
Definition: varatt.h:298

References Assert, DatumGetEOHP(), DatumGetPointer(), EA_MAGIC, ExpandedArrayHeader::ea_magic, PG_DETOAST_DATUM, and VARATT_IS_EXTERNAL_EXPANDED.

Referenced by array_map().

◆ DatumGetExpandedArray()

ExpandedArrayHeader* DatumGetExpandedArray ( Datum  d)

Definition at line 352 of file array_expanded.c.

353 {
354  /* If it's a writable expanded array already, just return it */
356  {
358 
359  Assert(eah->ea_magic == EA_MAGIC);
360  return eah;
361  }
362 
363  /* Else expand the hard way */
364  d = expand_array(d, CurrentMemoryContext, NULL);
365  return (ExpandedArrayHeader *) DatumGetEOHP(d);
366 }
Datum expand_array(Datum arraydatum, MemoryContext parentcontext, ArrayMetaState *metacache)
MemoryContext CurrentMemoryContext
Definition: mcxt.c:143
#define VARATT_IS_EXTERNAL_EXPANDED_RW(PTR)
Definition: varatt.h:296

References Assert, CurrentMemoryContext, DatumGetEOHP(), DatumGetPointer(), EA_MAGIC, ExpandedArrayHeader::ea_magic, expand_array(), and VARATT_IS_EXTERNAL_EXPANDED_RW.

Referenced by array_set_element_expanded(), and statext_expressions_load().

◆ DatumGetExpandedArrayX()

ExpandedArrayHeader* DatumGetExpandedArrayX ( Datum  d,
ArrayMetaState metacache 
)

Definition at line 372 of file array_expanded.c.

373 {
374  /* If it's a writable expanded array already, just return it */
376  {
378 
379  Assert(eah->ea_magic == EA_MAGIC);
380  /* Update cache if provided */
381  if (metacache)
382  {
383  metacache->element_type = eah->element_type;
384  metacache->typlen = eah->typlen;
385  metacache->typbyval = eah->typbyval;
386  metacache->typalign = eah->typalign;
387  }
388  return eah;
389  }
390 
391  /* Else expand using caller's cache if any */
392  d = expand_array(d, CurrentMemoryContext, metacache);
393  return (ExpandedArrayHeader *) DatumGetEOHP(d);
394 }
char typalign
Definition: array.h:241
int16 typlen
Definition: array.h:239
Oid element_type
Definition: array.h:238
bool typbyval
Definition: array.h:240

References Assert, CurrentMemoryContext, DatumGetEOHP(), DatumGetPointer(), EA_MAGIC, ExpandedArrayHeader::ea_magic, ExpandedArrayHeader::element_type, ArrayMetaState::element_type, expand_array(), ExpandedArrayHeader::typalign, ArrayMetaState::typalign, ExpandedArrayHeader::typbyval, ArrayMetaState::typbyval, ExpandedArrayHeader::typlen, ArrayMetaState::typlen, and VARATT_IS_EXTERNAL_EXPANDED_RW.

◆ deconstruct_expanded_array()

void deconstruct_expanded_array ( ExpandedArrayHeader eah)

Definition at line 424 of file array_expanded.c.

425 {
426  if (eah->dvalues == NULL)
427  {
429  Datum *dvalues;
430  bool *dnulls;
431  int nelems;
432 
433  dnulls = NULL;
435  eah->element_type,
436  eah->typlen, eah->typbyval, eah->typalign,
437  &dvalues,
438  ARR_HASNULL(eah->fvalue) ? &dnulls : NULL,
439  &nelems);
440 
441  /*
442  * Update header only after successful completion of this step. If
443  * deconstruct_array fails partway through, worst consequence is some
444  * leaked memory in the object's context. If the caller fails at a
445  * later point, that's fine, since the deconstructed representation is
446  * valid anyhow.
447  */
448  eah->dvalues = dvalues;
449  eah->dnulls = dnulls;
450  eah->dvalueslen = eah->nelems = nelems;
451  MemoryContextSwitchTo(oldcxt);
452  }
453 }
#define ARR_HASNULL(a)
Definition: array.h:291
void deconstruct_array(ArrayType *array, Oid elmtype, int elmlen, bool elmbyval, char elmalign, Datum **elemsp, bool **nullsp, int *nelemsp)
Definition: arrayfuncs.c:3631
MemoryContextSwitchTo(old_ctx)

References ARR_HASNULL, deconstruct_array(), ExpandedArrayHeader::dnulls, ExpandedArrayHeader::dvalues, ExpandedArrayHeader::dvalueslen, ExpandedArrayHeader::element_type, ExpandedObjectHeader::eoh_context, ExpandedArrayHeader::fvalue, ExpandedArrayHeader::hdr, MemoryContextSwitchTo(), ExpandedArrayHeader::nelems, ExpandedArrayHeader::typalign, ExpandedArrayHeader::typbyval, and ExpandedArrayHeader::typlen.

Referenced by array_contain_compare(), array_get_element_expanded(), array_set_element_expanded(), and statext_expressions_load().

◆ EA_flatten_into()

static void EA_flatten_into ( ExpandedObjectHeader eohptr,
void *  result,
Size  allocated_size 
)
static

Definition at line 293 of file array_expanded.c.

295 {
296  ExpandedArrayHeader *eah = (ExpandedArrayHeader *) eohptr;
297  ArrayType *aresult = (ArrayType *) result;
298  int nelems;
299  int ndims;
300  int32 dataoffset;
301 
302  Assert(eah->ea_magic == EA_MAGIC);
303 
304  /* Easy if we have a valid flattened value */
305  if (eah->fvalue)
306  {
307  Assert(allocated_size == ARR_SIZE(eah->fvalue));
308  memcpy(result, eah->fvalue, allocated_size);
309  return;
310  }
311 
312  /* Else allocation should match previous get_flat_size result */
313  Assert(allocated_size == eah->flat_size);
314 
315  /* Fill result array from dvalues/dnulls */
316  nelems = eah->nelems;
317  ndims = eah->ndims;
318 
319  if (eah->dnulls)
320  dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nelems);
321  else
322  dataoffset = 0; /* marker for no null bitmap */
323 
324  /* We must ensure that any pad space is zero-filled */
325  memset(aresult, 0, allocated_size);
326 
327  SET_VARSIZE(aresult, allocated_size);
328  aresult->ndim = ndims;
329  aresult->dataoffset = dataoffset;
330  aresult->elemtype = eah->element_type;
331  memcpy(ARR_DIMS(aresult), eah->dims, ndims * sizeof(int));
332  memcpy(ARR_LBOUND(aresult), eah->lbound, ndims * sizeof(int));
333 
334  CopyArrayEls(aresult,
335  eah->dvalues, eah->dnulls, nelems,
336  eah->typlen, eah->typbyval, eah->typalign,
337  false);
338 }
#define ARR_OVERHEAD_WITHNULLS(ndims, nitems)
Definition: array.h:312
#define ARR_SIZE(a)
Definition: array.h:289
#define ARR_DIMS(a)
Definition: array.h:294
#define ARR_LBOUND(a)
Definition: array.h:296
void CopyArrayEls(ArrayType *array, Datum *values, bool *nulls, int nitems, int typlen, bool typbyval, char typalign, bool freedata)
Definition: arrayfuncs.c:961
signed int int32
Definition: c.h:496
Oid elemtype
Definition: array.h:97
int ndim
Definition: array.h:95
int32 dataoffset
Definition: array.h:96
#define SET_VARSIZE(PTR, len)
Definition: varatt.h:305

References ARR_DIMS, ARR_LBOUND, ARR_OVERHEAD_WITHNULLS, ARR_SIZE, Assert, CopyArrayEls(), ArrayType::dataoffset, ExpandedArrayHeader::dims, ExpandedArrayHeader::dnulls, ExpandedArrayHeader::dvalues, EA_MAGIC, ExpandedArrayHeader::ea_magic, ExpandedArrayHeader::element_type, ArrayType::elemtype, ExpandedArrayHeader::flat_size, ExpandedArrayHeader::fvalue, ExpandedArrayHeader::lbound, ArrayType::ndim, ExpandedArrayHeader::ndims, ExpandedArrayHeader::nelems, SET_VARSIZE, ExpandedArrayHeader::typalign, ExpandedArrayHeader::typbyval, and ExpandedArrayHeader::typlen.

◆ EA_get_flat_size()

static Size EA_get_flat_size ( ExpandedObjectHeader eohptr)
static

Definition at line 233 of file array_expanded.c.

234 {
235  ExpandedArrayHeader *eah = (ExpandedArrayHeader *) eohptr;
236  int nelems;
237  int ndims;
238  Datum *dvalues;
239  bool *dnulls;
240  Size nbytes;
241  int i;
242 
243  Assert(eah->ea_magic == EA_MAGIC);
244 
245  /* Easy if we have a valid flattened value */
246  if (eah->fvalue)
247  return ARR_SIZE(eah->fvalue);
248 
249  /* If we have a cached size value, believe that */
250  if (eah->flat_size)
251  return eah->flat_size;
252 
253  /*
254  * Compute space needed by examining dvalues/dnulls. Note that the result
255  * array will have a nulls bitmap if dnulls isn't NULL, even if the array
256  * doesn't actually contain any nulls now.
257  */
258  nelems = eah->nelems;
259  ndims = eah->ndims;
260  Assert(nelems == ArrayGetNItems(ndims, eah->dims));
261  dvalues = eah->dvalues;
262  dnulls = eah->dnulls;
263  nbytes = 0;
264  for (i = 0; i < nelems; i++)
265  {
266  if (dnulls && dnulls[i])
267  continue;
268  nbytes = att_addlength_datum(nbytes, eah->typlen, dvalues[i]);
269  nbytes = att_align_nominal(nbytes, eah->typalign);
270  /* check for overflow of total request */
271  if (!AllocSizeIsValid(nbytes))
272  ereport(ERROR,
273  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
274  errmsg("array size exceeds the maximum allowed (%d)",
275  (int) MaxAllocSize)));
276  }
277 
278  if (dnulls)
279  nbytes += ARR_OVERHEAD_WITHNULLS(ndims, nelems);
280  else
281  nbytes += ARR_OVERHEAD_NONULLS(ndims);
282 
283  /* cache for next time */
284  eah->flat_size = nbytes;
285 
286  return nbytes;
287 }
#define ARR_OVERHEAD_NONULLS(ndims)
Definition: array.h:310
int ArrayGetNItems(int ndim, const int *dims)
Definition: arrayutils.c:57
size_t Size
Definition: c.h:596
int errcode(int sqlerrcode)
Definition: elog.c:853
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#define ERROR
Definition: elog.h:39
#define ereport(elevel,...)
Definition: elog.h:149
#define MaxAllocSize
Definition: fe_memutils.h:22
int i
Definition: isn.c:72
#define AllocSizeIsValid(size)
Definition: memutils.h:42
#define att_align_nominal(cur_offset, attalign)
Definition: tupmacs.h:129
#define att_addlength_datum(cur_offset, attlen, attdatum)
Definition: tupmacs.h:145

References AllocSizeIsValid, ARR_OVERHEAD_NONULLS, ARR_OVERHEAD_WITHNULLS, ARR_SIZE, ArrayGetNItems(), Assert, att_addlength_datum, att_align_nominal, ExpandedArrayHeader::dims, ExpandedArrayHeader::dnulls, ExpandedArrayHeader::dvalues, EA_MAGIC, ExpandedArrayHeader::ea_magic, ereport, errcode(), errmsg(), ERROR, ExpandedArrayHeader::flat_size, ExpandedArrayHeader::fvalue, i, MaxAllocSize, ExpandedArrayHeader::ndims, ExpandedArrayHeader::nelems, ExpandedArrayHeader::typalign, and ExpandedArrayHeader::typlen.

◆ expand_array()

Datum expand_array ( Datum  arraydatum,
MemoryContext  parentcontext,
ArrayMetaState metacache 
)

Definition at line 50 of file array_expanded.c.

52 {
53  ArrayType *array;
55  MemoryContext objcxt;
56  MemoryContext oldcxt;
57  ArrayMetaState fakecache;
58 
59  /*
60  * Allocate private context for expanded object. We start by assuming
61  * that the array won't be very large; but if it does grow a lot, don't
62  * constrain aset.c's large-context behavior.
63  */
64  objcxt = AllocSetContextCreate(parentcontext,
65  "expanded array",
67 
68  /* Set up expanded array header */
69  eah = (ExpandedArrayHeader *)
70  MemoryContextAlloc(objcxt, sizeof(ExpandedArrayHeader));
71 
72  EOH_init_header(&eah->hdr, &EA_methods, objcxt);
73  eah->ea_magic = EA_MAGIC;
74 
75  /* If the source is an expanded array, we may be able to optimize */
77  {
78  ExpandedArrayHeader *oldeah = (ExpandedArrayHeader *) DatumGetEOHP(arraydatum);
79 
80  Assert(oldeah->ea_magic == EA_MAGIC);
81 
82  /*
83  * Update caller's cache if provided; we don't need it this time, but
84  * next call might be for a non-expanded source array. Furthermore,
85  * if the caller didn't provide a cache area, use some local storage
86  * to cache anyway, thereby avoiding a catalog lookup in the case
87  * where we fall through to the flat-copy code path.
88  */
89  if (metacache == NULL)
90  metacache = &fakecache;
91  metacache->element_type = oldeah->element_type;
92  metacache->typlen = oldeah->typlen;
93  metacache->typbyval = oldeah->typbyval;
94  metacache->typalign = oldeah->typalign;
95 
96  /*
97  * If element type is pass-by-value and we have a Datum-array
98  * representation, just copy the source's metadata and Datum/isnull
99  * arrays. The original flat array, if present at all, adds no
100  * additional information so we need not copy it.
101  */
102  if (oldeah->typbyval && oldeah->dvalues != NULL)
103  {
104  copy_byval_expanded_array(eah, oldeah);
105  /* return a R/W pointer to the expanded array */
106  return EOHPGetRWDatum(&eah->hdr);
107  }
108 
109  /*
110  * Otherwise, either we have only a flat representation or the
111  * elements are pass-by-reference. In either case, the best thing
112  * seems to be to copy the source as a flat representation and then
113  * deconstruct that later if necessary. For the pass-by-ref case, we
114  * could perhaps save some cycles with custom code that generates the
115  * deconstructed representation in parallel with copying the values,
116  * but it would be a lot of extra code for fairly marginal gain. So,
117  * fall through into the flat-source code path.
118  */
119  }
120 
121  /*
122  * Detoast and copy source array into private context, as a flat array.
123  *
124  * Note that this coding risks leaking some memory in the private context
125  * if we have to fetch data from a TOAST table; however, experimentation
126  * says that the leak is minimal. Doing it this way saves a copy step,
127  * which seems worthwhile, especially if the array is large enough to need
128  * external storage.
129  */
130  oldcxt = MemoryContextSwitchTo(objcxt);
131  array = DatumGetArrayTypePCopy(arraydatum);
132  MemoryContextSwitchTo(oldcxt);
133 
134  eah->ndims = ARR_NDIM(array);
135  /* note these pointers point into the fvalue header! */
136  eah->dims = ARR_DIMS(array);
137  eah->lbound = ARR_LBOUND(array);
138 
139  /* Save array's element-type data for possible use later */
140  eah->element_type = ARR_ELEMTYPE(array);
141  if (metacache && metacache->element_type == eah->element_type)
142  {
143  /* We have a valid cache of representational data */
144  eah->typlen = metacache->typlen;
145  eah->typbyval = metacache->typbyval;
146  eah->typalign = metacache->typalign;
147  }
148  else
149  {
150  /* No, so look it up */
152  &eah->typlen,
153  &eah->typbyval,
154  &eah->typalign);
155  /* Update cache if provided */
156  if (metacache)
157  {
158  metacache->element_type = eah->element_type;
159  metacache->typlen = eah->typlen;
160  metacache->typbyval = eah->typbyval;
161  metacache->typalign = eah->typalign;
162  }
163  }
164 
165  /* we don't make a deconstructed representation now */
166  eah->dvalues = NULL;
167  eah->dnulls = NULL;
168  eah->dvalueslen = 0;
169  eah->nelems = 0;
170  eah->flat_size = 0;
171 
172  /* remember we have a flat representation */
173  eah->fvalue = array;
174  eah->fstartptr = ARR_DATA_PTR(array);
175  eah->fendptr = ((char *) array) + ARR_SIZE(array);
176 
177  /* return a R/W pointer to the expanded array */
178  return EOHPGetRWDatum(&eah->hdr);
179 }
#define DatumGetArrayTypePCopy(X)
Definition: array.h:262
#define ARR_NDIM(a)
Definition: array.h:290
#define ARR_DATA_PTR(a)
Definition: array.h:322
#define ARR_ELEMTYPE(a)
Definition: array.h:292
static void copy_byval_expanded_array(ExpandedArrayHeader *eah, ExpandedArrayHeader *oldeah)
static const ExpandedObjectMethods EA_methods
void EOH_init_header(ExpandedObjectHeader *eohptr, const ExpandedObjectMethods *methods, MemoryContext obj_context)
Definition: expandeddatum.c:48
static Datum EOHPGetRWDatum(const struct ExpandedObjectHeader *eohptr)
void get_typlenbyvalalign(Oid typid, int16 *typlen, bool *typbyval, char *typalign)
Definition: lsyscache.c:2271
#define AllocSetContextCreate
Definition: memutils.h:129
#define ALLOCSET_START_SMALL_SIZES
Definition: memutils.h:177

References ALLOCSET_START_SMALL_SIZES, AllocSetContextCreate, ARR_DATA_PTR, ARR_DIMS, ARR_ELEMTYPE, ARR_LBOUND, ARR_NDIM, ARR_SIZE, Assert, copy_byval_expanded_array(), DatumGetArrayTypePCopy, DatumGetEOHP(), DatumGetPointer(), ExpandedArrayHeader::dims, ExpandedArrayHeader::dnulls, ExpandedArrayHeader::dvalues, ExpandedArrayHeader::dvalueslen, EA_MAGIC, ExpandedArrayHeader::ea_magic, EA_methods, ExpandedArrayHeader::element_type, ArrayMetaState::element_type, EOH_init_header(), EOHPGetRWDatum(), ExpandedArrayHeader::fendptr, ExpandedArrayHeader::flat_size, ExpandedArrayHeader::fstartptr, ExpandedArrayHeader::fvalue, get_typlenbyvalalign(), ExpandedArrayHeader::hdr, ExpandedArrayHeader::lbound, MemoryContextAlloc(), MemoryContextSwitchTo(), ExpandedArrayHeader::ndims, ExpandedArrayHeader::nelems, ExpandedArrayHeader::typalign, ArrayMetaState::typalign, ExpandedArrayHeader::typbyval, ArrayMetaState::typbyval, ExpandedArrayHeader::typlen, ArrayMetaState::typlen, and VARATT_IS_EXTERNAL_EXPANDED.

Referenced by construct_empty_expanded_array(), DatumGetExpandedArray(), DatumGetExpandedArrayX(), exec_assign_value(), and plpgsql_exec_function().

Variable Documentation

◆ EA_methods

const ExpandedObjectMethods EA_methods
static
Initial value:
=
{
}
static Size EA_get_flat_size(ExpandedObjectHeader *eohptr)
static void EA_flatten_into(ExpandedObjectHeader *eohptr, void *result, Size allocated_size)

Definition at line 28 of file array_expanded.c.

Referenced by expand_array().