00001 /* 00002 * mextool.c: simple tools for Matlab MEX files. 00003 * 00004 * all function names begin mxt_ 00005 * 00006 * most functions here pertain to initializing, allocating or 00007 * indexing of scalars, vectors, matrices, and 3d arrays. 00008 * 00009 * Michael Turmon, April 1997 00010 * Modified for Matlab 5 API, August 2000 00011 * enhanced 2002 00012 * 00013 */ 00014 00015 /*LINTLIBRARY*/ 00016 00017 #include <stdlib.h> 00018 #include <stdio.h> 00019 #include <string.h> 00020 #include "mex.h" 00021 #include "mextool.h" /* function templates: current file */ 00022 #include "num_strings.h" /* for do_transpose */ 00023 #include "ieee_consts.h" /* for mxt_getnand() */ 00024 #include "mexargcheck.h" /* for IsEmpty, etc. */ 00025 00026 /* forward declaration */ 00027 static void * 00028 mxt_index_block_alloc(mwSize n_dbl, mwSize n_ptr, double **data); 00029 00030 /* mxt_PackSignature: wrap up information in the mexFunction's metadata 00031 * (NARG{IN,OUT}_{MIN_MAX}, in_names, in_specs, out_names, docstring) 00032 * into a mxArray for transmission out of the mexFunction. 00033 * This is used to print usage information (docstring) or to retrieve 00034 * input/output names (xio interface). 00035 * 00036 * Returns NULL on failure, otherwise a mxArray * summarizing the "index". 00037 */ 00038 00039 mxArray * 00040 mxt_PackSignature(mxt_Signature index, 00041 int in_lo, int in_hi, int out_lo, int out_hi, 00042 const char **in_names, 00043 const char **in_specs, 00044 const char **out_names, 00045 const char *docstring) 00046 { 00047 mxArray *a = NULL; 00048 00049 switch (index) { 00050 case mxt_SignatureNARG: 00051 /* return a 1x4 matrix of NARG* */ 00052 a = mxCreateDoubleMatrix(1, 4, mxREAL); 00053 if (!a) break; 00054 mxGetPr(a)[0] = (double) in_lo; 00055 mxGetPr(a)[1] = (double) in_hi; 00056 mxGetPr(a)[2] = (double) out_lo; 00057 mxGetPr(a)[3] = (double) out_hi; 00058 break; 00059 case mxt_SignatureDocstring: 00060 a = mxCreateString(docstring); 00061 break; 00062 case mxt_SignatureInNames: 00063 a = mxCreateCharMatrixFromStrings(in_hi, in_names); 00064 break; 00065 case mxt_SignatureInSpecs: 00066 a = mxCreateCharMatrixFromStrings(in_hi, in_specs); 00067 break; 00068 case mxt_SignatureOutNames: 00069 a = mxCreateCharMatrixFromStrings(out_hi, out_names); 00070 break; 00071 } 00072 return a; 00073 } 00074 00075 /* mxt_ArrayToStrings: convert mxArray to C char ** 00076 * This is the inverse function of mxCreateCharMatrixFromStrings. 00077 * Note that the individual strings in "a" must be the same length 00078 * because they are stored as a matrix. This routine eats whitespace 00079 * on the end of each string to compensage for the blank-padding done 00080 * when the strings are stored as a matrix. 00081 * As a convenience, the returned list of string pointers is one 00082 * entry longer than necessary, and the last character pointer is NULL. 00083 */ 00084 char ** 00085 mxt_ArrayToStrings(const mxArray *a) 00086 { 00087 char **s; 00088 mxChar *sa; /* the data pointer within "a" points to mxChar's */ 00089 mwSize M, N; /* the size of a */ 00090 mwSignedIndex m, n; /* allow to be < 0 */ 00091 00092 if (!a || !mxIsChar(a)) 00093 return NULL; 00094 M = mxGetM(a); /* number of strings */ 00095 N = mxGetN(a); /* their shared length */ 00096 sa = (mxChar *) mxGetChars(a); /* head of the char block in a */ 00097 if ((s = calloc(M+1, sizeof(char *))) == NULL) 00098 return NULL; 00099 for (m = 0; m < M; m++) { 00100 /* make space for string #m (one extra for \0) */ 00101 if ((s[m] = calloc(N+1, sizeof(char))) == NULL) 00102 return NULL; 00103 /* copy string m, which is not contiguous */ 00104 for (n = 0; n < N; n++) 00105 s[m][n] = (char) sa[m+n*M]; 00106 /* eat spaces from back of string m: N-1, ..., 0 */ 00107 for (n = N-1; (n >= 0) && (s[m][n] == ' '); n--) 00108 s[m][n] = '\0'; 00109 } 00110 return s; 00111 } 00112 00113 00114 00115 00116 00117 /* mxt_put_matrix: for debugging, puts data into global workspace 00118 * as an mxn matrix. takes only a pointer to the head of the 00119 * data, which must be contiguous, but could be of any size 00120 * or dimensions. 00121 * The data is put into the workspace as a 2d quantity named, for example, 00122 * x42 if name = "x" and inx = 42. If inx < 0, inx is not used for 00123 * the name at all. 00124 */ 00125 int 00126 mxt_put_matrix(char *name, 00127 int inx, 00128 double *dat, 00129 mwSize m, 00130 mwSize n) 00131 { 00132 mxArray *arr; 00133 char full_name[mxMAXNAM]; 00134 00135 /* return 1; / * if uncommented, disables the routine */ 00136 if ((arr = mxCreateDoubleMatrix(m, n, mxREAL)) == NULL) 00137 return 0; /* could not get the memory */ 00138 memcpy(mxGetPr(arr), dat, m*n*sizeof(double)); /* array <- dat */ 00139 if (inx >= 0) 00140 snprintf(full_name, sizeof(full_name), "%s%d", name, inx); 00141 else 00142 snprintf(full_name, sizeof(full_name), "%s", name); 00143 mexPutVariable("base", full_name, arr); 00144 mxDestroyArray(arr); /* eliminate the array */ 00145 return 1; 00146 } 00147 00148 /* 00149 * mxt_make_scalar 00150 * From mxArray structure, extract a scalar. If input is empty, 00151 * use default. If not empty or a scalar, raise an error. 00152 */ 00153 double 00154 mxt_make_scalar( 00155 const mxArray *pm, 00156 double default_val) 00157 00158 { 00159 if (!IsRealLooseScalar(pm)) { 00160 mexErrMsgTxt("! internal error: cannot mxt_make_scalar from that"); 00161 return mxt_getnand(); /* not actually reached */ 00162 } else if (IsEmpty(pm)) 00163 return default_val; 00164 else 00165 return mxGetScalar(pm); 00166 } 00167 00168 00169 /* 00170 * mxt_make_vector 00171 * From mxArray structure, extract a vector. If input is shorter 00172 * than desired length M, use corresponding entry in default 00173 * vector. If default runs out, fill remainder by replicating 00174 * last default. Letting desired length equal -1 means use whatever 00175 * size the input is (except if this causes an error, below). 00176 * If input not a real vector (length zero OK) or has more entries 00177 * than desired, raise an error. 00178 * If output vector size does not equal requested size, a 00179 * block is calloc'ed , else original mxArray data is used. 00180 * This means that unless the caller knows for certain that 00181 * a size match will or will not occur, they cannot mxFree the 00182 * returned vector. It might or might not have been calloc'd 00183 * In particular, if M is given as -1 no calloc will occur. 00184 * 00185 * possibly useful functionality to add: if pm==NULL, make and return a 00186 * copy of the default. 00187 */ 00188 double * 00189 mxt_make_vector( 00190 const mxArray *pm, 00191 mwSignedIndex M, /* can be < 0 */ 00192 double *default_val, 00193 mwSize default_len) 00194 { 00195 double *chunk; 00196 double *data_val; 00197 mwSize M_present; 00198 mwSize m; 00199 00200 if (!IsRealLooseVector(pm)) { 00201 /* something very wrong */ 00202 mexErrMsgTxt("! internal error: cannot make_vector from non-numerics"); 00203 return NULL; /* not actually reached */ 00204 } else if ((M != -1) && (mxGetNumberOfElements(pm) > M)) { 00205 /* more numbers supplied than needed */ 00206 mexErrMsgTxt("! internal error: cannot make_vector, input too long"); 00207 return NULL; /* not actually reached */ 00208 } else if ((M < 0) || (M == mxGetNumberOfElements(pm))) 00209 /* just pass on what we have */ 00210 return(mxGetPr(pm)); 00211 else { 00212 /* fill in as needed (note M >= 0 now) */ 00213 chunk = (double *) mxCalloc((size_t) M, sizeof(double)); 00214 if (!chunk) return NULL; 00215 M_present = mxGetNumberOfElements(pm); 00216 data_val = mxGetPr(pm); 00217 for (m = 0; m < M; m++) 00218 if (m < M_present) /* normal copying */ 00219 chunk[m] = data_val[m]; 00220 else if (m < default_len) /* fill in from current default */ 00221 chunk[m] = default_val[m]; 00222 else if (default_len > 0) /* fill in from last default present */ 00223 chunk[m] = default_val[default_len-1]; 00224 else /* no defaults present: fill in with zero */ 00225 chunk[m] = 0.0; 00226 return(chunk); 00227 } 00228 } 00229 00230 /* 00231 * mxt_make_matrix1 00232 * From mxArray structure, extract a matrix. If input is scalar or 00233 * null (and desired size is not scalar or null!), make a default 00234 * of a diagonal matrix with the supplied scalar, or the default 00235 * if input is empty, along the diagonal. (Non-square target 00236 * outputs are OK.) Letting desired length equal (-1,-1) means 00237 * use whatever size the input is. 00238 * Note, this can accept a d>2 dimensional input -- in this case 00239 * a 2d matrix is made with all the input elements, and M = dims(1), 00240 * and N = prod(dims(2:end)). 00241 * If input not a real matrix of size equal to requested size, 00242 * and is neither empty nor a scalar, raise an error. 00243 * Returned result is a vector of pointers to the columns of 00244 * the input, so that m[a][b] is m(b,a) in the normal (row,col) 00245 * indexing scheme of mathematics. 00246 * In all cases, the pointer-vector is calloc'ed. 00247 * If output matrix size does not equal requested size, a 00248 * block is calloc'ed for the matrix, else original mxArray data 00249 * is used. Because of this, in either case all storage allocated 00250 * in this routine can be freed with one call to mxFree. 00251 * See also make_matrix2, which handle default_val differently. 00252 */ 00253 double ** 00254 mxt_make_matrix1( 00255 const mxArray *pm, 00256 mwSignedIndex M, 00257 mwSignedIndex N, 00258 double default_val) 00259 { 00260 double *chunk; 00261 double **chunk2; 00262 int do_fillin, size_match; 00263 mwSize pm_N, pm_M, pm_MN; 00264 mwSize N_target, M_target; 00265 mwSize m, n; 00266 00267 /* note, this allows making a 2d matrix from a d-dimensional input, 00268 * because mxGetM * mxGetN = mxGetNumberOfElements in this case */ 00269 if (!IsFullRealArray(pm)) { /* something very wrong */ 00270 mexErrMsgTxt("! internal error: cannot make_matrix1 from that"); 00271 return NULL; /* not actually reached */ 00272 } 00273 /* FIND USEFUL SIZES */ 00274 /* size of input mxArray */ 00275 pm_M = mxGetM(pm); 00276 pm_N = mxGetN(pm); 00277 pm_MN = pm_M * pm_N; 00278 /* size of data to be produced by this routine */ 00279 N_target = (M == -1) ? pm_N : N; 00280 M_target = (M == -1) ? pm_M : M; 00281 /* FILL DATA IF NEEDED */ 00282 /* will size(in) == size(out) ? */ 00283 size_match = (M == -1) || ((pm_M == M) && (pm_N == N)); 00284 /* need to fill data (using scalar input, or the default_val)? */ 00285 do_fillin = !size_match && (pm_MN <= 1); 00286 if (!size_match && !do_fillin) { /* bad # of numbers supplied */ 00287 mexErrMsgTxt("error: mxt_make_matrix1: size conflict when defaulting"); 00288 return NULL; /* not actually reached */ 00289 } 00290 /* have detected all errors: now set up data */ 00291 if (size_match) { 00292 chunk = mxGetPr(pm); /* easy case: use what is there */ 00293 chunk2 = (double **) mxCalloc((size_t) N_target, 00294 sizeof(double *)); /* col ptrs */ 00295 } else { 00296 /* here do_fillin == 1 => must fill in */ 00297 /* note here N_target == N, etc. */ 00298 /* get the block (chunk2) plus the data block (chunk) within it */ 00299 chunk2 = (double **) mxt_index_block_alloc(M*N, N, &chunk); 00300 if (!chunk2) return NULL; /* failed calloc */ 00301 /* now, fill in data */ 00302 if (pm_MN == 1) default_val = mxGetScalar(pm); /* replace */ 00303 for (n = 0; n < N; n++) 00304 for (m = 0; m < M; m++) 00305 chunk[n*M+m] = m == n ? default_val : 0.0; /* plug in to diags */ 00306 } 00307 /* SET UP COLUMN POINTERS */ 00308 for (n = 0; n < N_target; n++) 00309 chunk2[n] = chunk + M_target*n; 00310 return chunk2; 00311 } 00312 00313 00314 /* 00315 * mxt_make_matrix2 00316 * From mxArray structure, extract a matrix. If input is scalar or 00317 * null (and desired size is not scalar or null!), make a default 00318 * replicating the supplied scalar, or the default if input is empty, 00319 * throughout the matrix. Letting desired length equal (-1,-1) means 00320 * use whatever size the input is. 00321 * Note, this can accept a d>2 dimensional input -- in this case 00322 * a 2d matrix is made with all the input elements, and M = dims(1), 00323 * and N = prod(dims(2:end)). 00324 * If input not a real matrix of size equal to requested size, 00325 * and is neither empty nor a scalar, raise an error (since a default 00326 * value is requested and a valid one cannot be found). 00327 * Returned result is a vector of pointers to the columns of 00328 * the input, so that m[a][b] is m(b,a) in the normal (row,col) 00329 * indexing scheme of mathematics. 00330 * In all cases, the pointer-vector is calloc'ed. 00331 * If output matrix size does not equal requested size, a 00332 * block is calloc'ed for the matrix, else original mxArray data 00333 * is used. Because of this, in either case all storage allocated 00334 * in this routine can be freed with one call to mxFree. 00335 * See also make_matrix1, which handle default_val differently. 00336 */ 00337 double ** 00338 mxt_make_matrix2( 00339 const mxArray *pm, 00340 mwSignedIndex M, 00341 mwSignedIndex N, 00342 double default_val) 00343 { 00344 double *chunk; 00345 double **chunk2; 00346 int do_fillin, size_match; 00347 mwSize pm_N, pm_M, pm_MN; 00348 mwSize N_target, M_target; 00349 mwSize n; 00350 00351 /* note, this allows making a 2d matrix from a d-dimensional input, 00352 * because mxGetM * mxGetN = mxGetNumberOfElements in this case */ 00353 if (!IsFullRealArray(pm)) /* something very wrong */ 00354 mexErrMsgTxt("! internal error: cannot mxt_make_matrix2 from that"); 00355 /* FIND USEFUL SIZES */ 00356 /* size of input mxArray */ 00357 pm_M = mxGetM(pm); 00358 pm_N = mxGetN(pm); 00359 pm_MN = pm_M * pm_N; 00360 /* size of data to be produced by this routine */ 00361 N_target = (M == -1) ? pm_N : N; 00362 M_target = (M == -1) ? pm_M : M; 00363 /* FILL DATA IF NEEDED */ 00364 /* will size(in) == size(out) ? */ 00365 size_match = (M == -1) || ((pm_M == M) && (pm_N == N)); 00366 /* need to fill data (using scalar input, or the default_val)? */ 00367 do_fillin = !size_match && (pm_MN <= 1); 00368 if (!size_match && !do_fillin) /* bad # of numbers supplied */ 00369 mexErrMsgTxt("error: mxt_make_matrix2: size conflict when defaulting"); 00370 /* have detected all errors: now set up data from input or defaults */ 00371 if (size_match) { 00372 chunk = mxGetPr(pm); /* easy case: use what is there */ 00373 chunk2 = (double **) mxCalloc((size_t) N_target, 00374 sizeof(double *)); /* col ptrs */ 00375 if (!chunk2) return NULL; /* failed calloc */ 00376 } else { 00377 /* here do_fillin == 1 => must fill in */ 00378 /* note here N_target == N, etc. */ 00379 /* get the block (chunk2) plus the data block (chunk) within it */ 00380 chunk2 = (double **) mxt_index_block_alloc(M*N, N, &chunk); 00381 if (!chunk2) return NULL; /* failed calloc */ 00382 /* now, fill in data */ 00383 if (pm_MN == 1) default_val = mxGetScalar(pm); /* replace */ 00384 for (n = 0; n < M*N; n++) 00385 chunk[n] = default_val; /* plug in all over the matrix */ 00386 } 00387 /* SET UP COLUMN POINTERS */ 00388 for (n = 0; n < N_target; n++) 00389 chunk2[n] = chunk + M_target*n; 00390 return(chunk2); 00391 } 00392 00393 /* 00394 * mxt_matrix_index: allocate column pointers for a m by n 00395 * matrix, returning the pointers. They will enable indexing 00396 * the matrix via handle[j][i] where j < n and i < m. (Note 00397 * the bracket reversal due to differences in C and Matlab indexing.) 00398 * Pointers are freed using a single call to mxfree(); this 00399 * does not free the data they point to! 00400 * Returns null on error (failed calloc) 00401 */ 00402 double ** 00403 mxt_matrix_index(double *data, mwSize m, mwSize n) 00404 { 00405 double **handle; /* whole block of memory (data + col ptrs) */ 00406 mwSize j; 00407 00408 /* room for col ptrs */ 00409 handle = mxCalloc(n, sizeof(double *)); 00410 if (!handle) return NULL; /* trouble */ 00411 /* set up ptrs */ 00412 for (j = 0; j < n; j++) 00413 handle[j] = data + j*m; 00414 return handle; 00415 } 00416 00417 00418 /* 00419 * mxt_matrix3_index: allocate slice and column pointers for a mXnXp 00420 * matrix, returning the pointers. They will enable indexing 00421 * the matrix via handle[k][j][i] where k < p, j < n, i < m. (Note 00422 * the bracket reversal due to differences in C and Matlab indexing.) 00423 * Both layers of pointers are freed using a single call to mxfree(); 00424 * this does not free the data they point to! 00425 * Note that about n*p pointer slots are allocated. 00426 * Returns null on error (failed calloc) 00427 */ 00428 double *** 00429 mxt_matrix3_index(double *data, 00430 mwSize m, 00431 mwSize n, 00432 mwSize p) 00433 { 00434 double ***handle; /* whole block of memory (slice + col ptrs) */ 00435 double **handle2; /* start of col ptrs */ 00436 size_t units; /* number of bytes needed */ 00437 mwSize j, k; /* indexing */ 00438 00439 /* room for slice + col ptrs */ 00440 units = p * sizeof(double **) + p*n * sizeof(double *); 00441 handle = mxCalloc(units, (size_t) 1); 00442 if (!handle) return NULL; 00443 /* set up ptrs */ 00444 handle2 = (double **) (handle + p); /* starts second layer of n*p pointers */ 00445 for (k = 0; k < p; k++) { 00446 handle[k] = handle2 + k*n; /* leave room for n col ptrs at handle[k] */ 00447 for (j = 0; j < n; j++) 00448 handle[k][j] = data + k*(n*m) + j*m; 00449 } 00450 return handle; 00451 } 00452 00453 00454 /* 00455 * mxt_matrix_alloc: allocate data and pointers for a m by n 00456 * matrix, returning the col pointers. Both the pointers 00457 * and the data section are a contiguous block, so they 00458 * are freed using a single call to mxfree() 00459 * If data is non-null, is is copied into the data segment 00460 * of the new matrix handle. 00461 * Returns null on error (failed calloc) 00462 */ 00463 double ** 00464 mxt_matrix_alloc(double *data, mwSize m, mwSize n) 00465 { 00466 double **handle; /* whole block of memory (data + col ptrs) */ 00467 double *chunk; /* points to head of numeric part */ 00468 mwSize j; 00469 00470 /* get the block (handle) plus the data block (chunk) within it */ 00471 handle = (double **) mxt_index_block_alloc(m*n, n, &chunk); 00472 if (!handle) return NULL; 00473 /* set up ptrs */ 00474 for (j = 0; j < n; j++) 00475 handle[j] = chunk + j*m; 00476 /* plug in data if it exists */ 00477 if (data) 00478 memcpy(chunk, data, (size_t) m*n*sizeof(double)); 00479 return handle; 00480 } 00481 00482 00483 /* 00484 * mxt_matrix3_alloc: allocate data and pointers for a mXnXp 00485 * matrix, returning the col pointers. All the pointers 00486 * and the data section are a contiguous block, so they 00487 * are freed using a single call to mxfree() 00488 * If data is non-null, is is copied into the data segment 00489 * of the new matrix handle. 00490 * Returns null on error (failed calloc) 00491 */ 00492 double *** 00493 mxt_matrix3_alloc(double *data, mwSize m, mwSize n, mwSize p) 00494 { 00495 double ***handle; /* whole block of memory (data + col ptrs) */ 00496 double **handle2; /* points to head of column pointers (p*n ptrs) */ 00497 double *chunk; /* points to head of numeric part */ 00498 mwSize j, k; 00499 00500 /* room for data + col ptrs, slice ptrs */ 00501 /* get the block (handle) plus the data block (chunk) within it */ 00502 handle = (double ***) mxt_index_block_alloc(m*n*p, n*p+p, &chunk); 00503 if (!handle) return NULL; /* failed calloc */ 00504 /* set up ptrs */ 00505 handle2 = (double **) (handle + p); /* go p double **'s beyond handle */ 00506 for (k = 0; k < p; k++) { 00507 handle[k] = handle2 + k*n; /* leave room for n col ptrs at handle[k] */ 00508 for (j = 0; j < n; j++) 00509 handle[k][j] = chunk + k*(n*m) + j*m; 00510 } 00511 /* plug in data if it exists */ 00512 if (data) 00513 memcpy(chunk, data, (size_t) m*n*p*sizeof(double)); 00514 return handle; 00515 } 00516 00517 /* 00518 * Transposition 00519 */ 00520 00521 /* 00522 * transpose a matrix structure "in place" 00523 * 00524 * The shell of the mxArray remains intact. However, its data 00525 * segment is reset to point to the transposed data, and the old 00526 * segment is freed. 00527 */ 00528 int 00529 mxt_transpose_double_mxArray(mxArray *pa) 00530 { 00531 mwSize ndim; 00532 mwSize *old_dims; 00533 mwSize *new_dims; 00534 double *old_pr; 00535 double *new_pr; 00536 mwSize d; 00537 00538 /* make flipped dimensions */ 00539 ndim = mxGetNumberOfDimensions(pa); /* dimensionality */ 00540 old_dims = (mwSize *) mxGetDimensions(pa); 00541 new_dims = (mwSize *) mxCalloc(ndim, sizeof(*new_dims)); 00542 if (!new_dims) return(0); /* calloc fails */ 00543 for (d = 0; d < ndim; d++) 00544 new_dims[d] = old_dims[ndim-d-1]; /* copy in reverse order */ 00545 /* make flipped data */ 00546 new_pr = (double *) mxCalloc(mxGetNumberOfElements(pa), sizeof(double)); 00547 if (!new_pr) { 00548 mxFree(new_dims); 00549 return 0; /* calloc fails */ 00550 } 00551 old_pr = mxGetPr(pa); 00552 if (!do_transpose(old_pr, new_pr, ndim, new_dims)) { 00553 mxFree(new_dims); 00554 mxFree(new_pr); 00555 return 0; /* trouble */ 00556 } 00557 /* install flipped parameters */ 00558 mxSetDimensions(pa, new_dims, ndim); /* new dimensions */ 00559 mxSetPr(pa, new_pr); /* new data heap */ 00560 /* free alloc'd memory */ 00561 mxFree(old_pr); /* old data segment */ 00562 mxFree(new_dims); /* mxSetDimensions alloc's its own space for dims */ 00563 mxFree(old_dims); /* this has been overwritten */ 00564 return 1; /* OK */ 00565 } 00566 00567 00568 /* 00569 * mxt_index_block_alloc: utility routine, allocate ptr/data blocks 00570 * 00571 * allocate storage for n_dbl doubles (data) that will be indexed by 00572 * n_ptr double* pointers. Because the pointers may be of a different 00573 * length than the doubles they point to, to share the block we must be 00574 * careful about alignment. After callocing the combined chunk, we 00575 * locate the right place within it to begin the data; this is output 00576 * via *data. The head of the whole block is returned via the 00577 * function call (NULL for calloc failure). 00578 */ 00579 00580 static void * 00581 mxt_index_block_alloc(mwSize n_dbl, mwSize n_ptr, double **data) 00582 { 00583 double **block_head; /* head of combined block */ 00584 size_t units; /* bytes of the combined ptr+data segment */ 00585 size_t ptr_bytes; /* bytes of the ptr segment alone */ 00586 size_t bump; /* difference to align the data part */ 00587 00588 ptr_bytes = n_ptr * sizeof(double *); 00589 /* bump ptr_bytes up to a multiple of sizeof(double) */ 00590 bump = ptr_bytes % sizeof(double); 00591 if (bump != 0) 00592 ptr_bytes += sizeof(double) - bump; 00593 /* reset n_ptr to account for the bump */ 00594 n_ptr = ptr_bytes / sizeof(double *); 00595 /* the requirement, in bytes */ 00596 units = n_ptr * sizeof(double *) + n_dbl * sizeof(double); 00597 /* allocate the memory */ 00598 block_head = (double **) mxCalloc(units, (size_t) 1); 00599 if (!block_head) return NULL; 00600 /* set up return values */ 00601 *data = (double *) (block_head + n_ptr); /* alignment IS ok */ 00602 return (void *) block_head; /* to a void* for return */ 00603 }