MATLAB: How to shrink a pre-allocated array in a C-Mex

cmexmxreallocpre-allocationshrink

What is a fast and secure way to shrink a pre-allocated array inside a C-Mex function?
#include "mex.h"
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) {
// PREREQUISITES: -----------------------------
double *p, *q;
int n = 1000, m;
// Pre-allocate an array for the maximum size of [1 x 1000]:
plhs[0] = mxCreateDoubleMatrix(1, n, mxReal);
p = mxGetPr(plhs[0]);
// Some dummy calculations: ...
p[0] = 1.0;
p[1] = 2.0;
m = 2; // Only 2 elements used here for example
// THE ACTUAL QUESTION: --------------------------
// Part 1: Set N only:
mxSetN(plhs[0], m);
// Part 2: re-allocate:
q = mxRealloc(p, m * sizeof(double));
// Part 3: Check success of realloc:
// A failing mxRealloc stops with an error in normal MEX functions,
// but not "in a MAT or engine standalone application" (see: doc mxRealloc)
if (q == NULL) {
mexErrMsgIdAndTxt("JSimon:TestFunc_MEX:ReAllocFail",
"Re-allocation of output failed.");
}
// Part 4: Update data pointer on demand:
if (q != p) { // Usually the smaller array is at the same location
mxSetPr(plhs[0], q);
// Part 5: Should I free the old memory?
// In a normal MEX function the memory manager cares, but in
// MAT or engine applications?
// mxFree(p);
// NO, DON'T DO THIS. See: doc mxRealloc:
// If the memory location changes, mxRealloc frees the original
// memory block pointed to by ptr.
}
}
Is Part 1 enough already, or are the other parts required for a stable code? Is this documented by MathWorks?

Best Answer

I am unaware of specific documentation on this, but Part 1 should be sufficient (and safe) as long as you don't care about the wasted memory at the end hanging around (and this is extremely fast of course). I have done this many times myself and have never run into an issue. I have even done this with only one copy of a group of shared data copies (different mxArray variables having different number of elements but pointing to the same data block). I do not believe the MATLAB Memory Manager uses the size information in a mxArray for anything. After all, it is perfectly legal to do this:
mxArray *mx;
double *pr;
mx = mxCreateDoubleMatrix( 0, 0, mxREAL );
pr = mxMalloc( 1000 * sizeof(*pr) );
mxSetPr(mx,pr);
mxSetM(mx,1);
mxSetN(mx,2);
// do stuff
mxSetN(mx,20);
// do more stuff
// etc.
So here you also have a mismatch between the allocated memory and the mxArray size information, but everything is OK as long as the memory is large enough to encompass the sizes present.
What I have had issues with in the past is with mxRealloc not always freeing up the memory when going from a large block to a small block. Or, at least the memory didn't always show up as available after the mxRealloc call (maybe the memory manager has it somehow marked internally as available but I didn't have access to the details so I couldn't really tell).
Side Issue:
The following comparison is technically non-conforming:
q != p
If p was free'd by the mxRealloc call, you can't legally use it in downstream code, even for comparison purposes (although you can probably get away with it on the machines we typically run on). So, you could just skip that test entirely and always just do the mxSetPr call regardless if you wanted to be a stickler about conforming code:
mxSetPr(plhs[0], q);
Finally, I don't think q can ever return NULL from a mxRealloc call in a mex routine. I think the doc is to be interpreted as
"... a MAT standalone application or engine standalone application ..."