/********************************************************************** * * common.c * * This file contains routines that are used or are intended for use * by the other source files of mexnc. * *********************************************************************/ # include # include # include # include # include # include "netcdf.h" # include "mex.h" # include "mexnc.h" /* * This variable is used in almost all routines, so I declare it * globally for this file. * */ static char error_message[1000]; /* * mexncCreateDoubleScalar: * * The netcdf-3 mexnc was originally written on an R13 platform. * Backporting to R11 created a conflict with the mxCreateDoubleScalar * routine, which was not available to the R11. Since the equivalent * code would be cumbersome, it was decided to just stub out all calls * to mxCreateDoubleScalar and handle all the differences in just one * place. * */ mxArray *mexncCreateDoubleScalar ( double value ) { mxArray *mx; #ifdef MEXNC_R11 /* * number of dimensions in a NetCDF file and their IDs. * */ int mx_count_coord[1]; /* * Pointer to data part of the matrix. * */ double *pr; mx_count_coord[0] = 1; mx = mxCreateNumericArray ( 1, mx_count_coord, mxDOUBLE_CLASS, mxREAL ); pr = mxGetPr ( mx ); pr[0] = value; #else mx = mxCreateDoubleScalar ( value ); #endif return ( mx ); } void Usage ( ) { mexPrintf ( "You must have at least one input argument. Try\n\n" ); mexPrintf ( " >> help mexcdf53\n\n" ); } /* * Mat2Size_t: * * Turns values in an mxArray into a size_t array. * */ size_t * Mat2Size_t ( const mxArray *mat ) { double * pr; size_t * pint; size_t * p; int len; int i; len = mxGetM(mat) * mxGetN(mat); pint = (size_t *) mxCalloc(len, sizeof(size_t)); p = pint; pr = mxGetPr(mat); for (i = 0; i < len; i++) { *p++ = (size_t) *pr++; } return (pint); } /* * Mat2Ptrdiff_t: * * Turns values in an mxArray into a ptrdiff_t array. * */ ptrdiff_t * Mat2Ptrdiff_t ( const mxArray *mat ) { double * pr; ptrdiff_t * pint; ptrdiff_t * p; int len; int i; len = mxGetM(mat) * mxGetN(mat); pint = (ptrdiff_t *) mxCalloc(len, sizeof(ptrdiff_t)); p = pint; pr = mxGetPr(mat); for (i = 0; i < len; i++) { *p++ = (size_t) *pr++; } return (pint); } /********************************************************************** * * CHECK_CHAR_ARGUMENT_TYPE * * Verifies that a parameter passed in from matlab is character. * * PARAMETERS: * mx: * matlab array whom we wish to verify * opname: * name of the mexnc operation whose argument is being verified. * idx: * Index of the calling parameter in question. * *********************************************************************/ void check_char_argument_type ( const mxArray *mx[], char *opname, int idx ) { char *error_fmt = { "argument in position %d must be character, operation \"%s\"\n" }; if ( mxIsChar(mx[idx]) == false ) { sprintf ( error_message, error_fmt, idx+1, opname ); mexErrMsgTxt ( error_message ); } } /********************************************************************** * * CHECK_CHAR_OR_ARGUMENT_TYPE * * Check as to whether an argument is character or numeric datatype. * PARAMETERS: * mx: * matlab array whom we wish to verify * opname: * name of the mexnc operation whose argument is being verified. * idx: * Index of the calling parameter in question. * *********************************************************************/ void check_char_or_numeric_argument_type ( const mxArray *mx[], char *opname, int idx ) { char *error_fmt = { "argument in position %d must be either character or numeric, operation \"%s\"\n" }; if ( !( mxIsChar(mx[idx]) || mxIsNumeric(mx[idx]) ) ) { sprintf ( error_message, error_fmt, idx+1, opname ); mexErrMsgTxt ( error_message ); } } /********************************************************************** * * CHECK_MODE_ARGUMENT_TYPE * * Verifies that the input matlab parameter is the proper datatype for * a "mode" argument. * * PARAMETERS: * mx: * matlab array whom we wish to verify * opname: * name of the mexnc operation whose argument is being verified. * idx: * Index of the calling parameter in question. * *********************************************************************/ void check_mode_argument_type ( const mxArray *mode_mx[], char *opname, int idx ) { if ( !((mxIsChar(mode_mx[idx]) == true) || (mxIsDouble(mode_mx[idx]) == true )) ) { sprintf ( error_message, "the mode argument in position %d of operation %s must be either matlab native double precision or a string corresponding to the mode", idx+1, opname ); mexErrMsgTxt ( error_message ); } } /********************************************************************** * * CHECK_NUMERIC_ARGUMENT_TYPE * * Check the argument as to whether it is an acceptable numeric * PARAMETERS: * mx: * matlab array whom we wish to verify * opname: * name of the mexnc operation whose argument is being verified. * idx: * Index of the calling parameter in question. * *********************************************************************/ void check_numeric_argument_type ( const mxArray *mx[], char *opname, int idx ) { char *error_fmt = { "argument in position %d must be numeric, operation \"%s\"\n" }; if ( mxIsNumeric(mx[idx]) == false ) { sprintf ( error_message, error_fmt, idx+1, opname ); mexErrMsgTxt ( error_message ); } } /*********************************************************************** * * INTERPRET_CHAR_PARAMETER * * For backwards compatibility, some matlab inputs are done with * character strings, such as using 'NC_CHAR' for a variable * declaration in DEF_VAR instead of nc_char. We have to be able to * handle that. This code is a clean implementation of the netcdf2 * routine "Parameter", which accomplished the same thing. * * Parameters: * mx: * matlab array that contains a character string * * Return Value: * If the parameter is identified, it is returned as an integer * value. Otherwise an exception is thrown. * **********************************************************************/ int interpret_char_parameter ( const mxArray *mx ) { /* * Define a datastructure to hold the enumerated constant and * the string equivalent. * */ typedef struct nc_constant_struct { int code; char *name; } nc_const; nc_const ncconst[] = { /* * Datatypes. * */ { NC_BYTE, "BYTE" }, { NC_CHAR, "CHAR" }, { NC_DOUBLE, "DOUBLE" }, { NC_FLOAT, "FLOAT" }, { NC_INT, "INT" }, { NC_LONG, "LONG" }, { NC_NAT, "NAT" }, { NC_SHORT, "SHORT" }, /* * Open and create modes. * */ { NC_CLOBBER, "CLOBBER" }, { NC_LOCK, "LOCK" }, { NC_NOCLOBBER, "NOCLOBBER" }, { NC_NOWRITE, "NOWRITE" }, { NC_SHARE, "SHARE" }, { NC_WRITE, "WRITE" }, { NC_64BIT_OFFSET, "64BIT_OFFSET" }, /* * Format modes. * */ { NC_FORMAT_CLASSIC, "FORMAT_CLASSIC" }, { NC_FORMAT_64BIT, "FORMAT_64BIT" }, /* * Error codes. * */ { NC_NOERR, "NOERR" }, { NC_EBADID, "EBADID" }, { NC_ENFILE, "ENFILE" }, { NC_EEXIST, "EEXIST" }, { NC_EINVAL, "EINVAL" }, { NC_EPERM, "EPERM" }, { NC_ENOTINDEFINE, "ENOTINDEFINE" }, { NC_EINDEFINE, "EINDEFINE" }, /* * -40 * */ { NC_EINVALCOORDS, "EINVALCOORDS" }, { NC_EMAXDIMS, "EMAXDIMS" }, { NC_ENAMEINUSE, "ENAMEINUSE" }, { NC_ENOTATT, "ENOTATT" }, { NC_EMAXATTS, "EMAXATTS" }, { NC_EBADTYPE, "EBADTYPE" }, { NC_EBADDIM, "EBADDIM" }, { NC_EUNLIMPOS, "EUNLIMPOS" }, { NC_EMAXVARS, "EMAXVARS" }, { NC_ENOTVAR, "ENOTVAR" }, /* * -50 * */ { NC_EGLOBAL, "EGLOBAL" }, { NC_ENOTNC, "ENOTNC" }, { NC_ESTS, "ESTS" }, { NC_EMAXNAME, "EMAXNAME" }, { NC_EUNLIMIT, "EUNLIMIT" }, { NC_ENORECVARS, "ENORECVARS" }, { NC_ECHAR, "ECHAR" }, { NC_EEDGE, "EEDGE" }, { NC_ESTRIDE, "ESTRIDE" }, { NC_EBADNAME, "EBADNAME" }, /* * -60 * */ { NC_ERANGE, "ERANGE" }, { NC_ENOMEM, "ENOMEM" }, { NC_EVARSIZE, "EVARSIZE" }, { NC_EDIMSIZE, "EDIMSIZE" }, { NC_FATAL, "FATAL" }, { NC_FILL, "FILL" }, { NC_GLOBAL, "GLOBAL" }, { NC_NOFILL, "NOFILL" }, { NC_UNLIMITED, "UNLIMITED" }, { NC_SIZEHINT_DEFAULT, "SIZEHINT_DEFAULT" }, { NC_VERBOSE, "VERBOSE" }, { 0, "NONE" } }; int parameter; /* * Character pointers for manipulating the parameter. * */ char *p, *q; /* * Loop index * */ int i; /* * flag as to whether or not we found the parameter in the * list of candidates. * */ int did_not_find_it; parameter = -1; p = unpackString ( mx ); /* * Make it upper case. * */ q = p; for (i = 0; i < strlen(p); i++) { *q = (char) toupper((int) *q); q++; } /* * Trim away any leading "NC_". * */ if (strncmp(p, "NC_", 3) == 0) { q = p + 3; } else { q = p; } /* * Loop thru the known parameter list. See if we can't identify * the parameter. * */ i = 0; did_not_find_it = 1; while (strcmp(ncconst[i].name, "NONE") != 0) { if (strcmp( q, ncconst[i].name ) == 0) { parameter = ncconst[i].code; did_not_find_it = 0; break; } else { i++; } } if ( did_not_find_it ) { sprintf ( error_message, "unable to identify parameter \"%s\"\n", p ); mexErrMsgTxt ( error_message ); } return (parameter); } /*********************************************************************** * * SET_OUTPUT_MATRIX_SIZE * * We need to set the dimensions of the matrix as the reverse of how * it is defined in the netcdf file. This makes the size of the matrix * seem transposed (upon return to matlab), but otherwise the data gets * layed out incorrectly due to the difference between row-major order * (C) and column-major order (matlab). * * PARAMETERS: * ncid: * NetCDF file ID * num_nc_dims: * Number of dimensions for the current variable. The variable * itself is not required here. * dimids: * Array of dimension IDs * nc_count_coords: * NetCDF enumerated type of a compound in question. * nc_op: * Pointer to the current mexnc operation structure. * mx_rank: * The size of the output matrix is set in this array. * * **********************************************************************/ void set_output_matrix_rank ( int ncid, int num_nc_dims, int *dimids, size_t *nc_count_coords, op *nc_op, int *mx_rank ) { /* * Size of a dimension. * */ size_t dimlen; /* * Loop index. * */ int j; /* * Return status from netcdf operation. * */ int status; /* * Determine the size of the output matrix. * * In the case of a singleton or 1D netcdf array, we then assume * that the output matrix is a 2D. * */ mx_rank[0] = 1; mx_rank[1] = 1; switch ( nc_op->opcode ) { /* * The output size is determined by the size of the netcdf * variable. * */ case GET_VAR_DOUBLE: case GET_VAR_FLOAT: case GET_VAR_INT: case GET_VAR_SHORT: case GET_VAR_SCHAR: case GET_VAR_UCHAR: case GET_VAR_TEXT: for ( j = 0; j < num_nc_dims; ++j ) { status = nc_inq_dimlen ( ncid, dimids[j], &dimlen ); if ( status != NC_NOERR ) { sprintf ( error_message, "nc_inq_dimlen failed, line %d, file %s\n", __LINE__, __FILE__ ); mexErrMsgTxt ( error_message ); } mx_rank[num_nc_dims - j - 1] = dimlen; } break; /* * We're already done in the case of a single-element grab. * */ case GET_VAR1_DOUBLE: case GET_VAR1_FLOAT: case GET_VAR1_INT: case GET_VAR1_SHORT: case GET_VAR1_SCHAR: case GET_VAR1_UCHAR: case GET_VAR1_TEXT: break; /* * The size is determined by the user! * */ case GET_VARA_DOUBLE: case GET_VARA_FLOAT: case GET_VARA_INT: case GET_VARA_SHORT: case GET_VARA_SCHAR: case GET_VARA_UCHAR: case GET_VARA_TEXT: case GET_VARS_DOUBLE: case GET_VARS_FLOAT: case GET_VARS_INT: case GET_VARS_SHORT: case GET_VARS_SCHAR: case GET_VARS_UCHAR: case GET_VARS_TEXT: for ( j = 0; j < num_nc_dims; ++j ) { mx_rank[num_nc_dims - j - 1] = nc_count_coords[j]; } break; default: sprintf ( error_message, "unhandled opcode %d, %s, line %d, file %s\n", nc_op->opcode, nc_op->opname, __LINE__, __FILE__ ); mexErrMsgTxt ( error_message ); } return; } /*********************************************************************** * * unpackDataType * * This routine unpacks an nc_type from a matlab matrix. This function could be called, say, from an invocation of * * status = mexnc ( 'PUT_ATT', ncid, varid, attname, xtype, attlength, attdata ); * * where "xtype" could be the numeric representation of the datatype, * such as NC_INT (this is the preferred way, please follow this * convention), or alternatively possibly "int" or "nc_int" instead. * This opens a can of worms due to the fact that NetCDF-4 allows for * user-defined datatypes that we cannot possibly know about * beforehand. So, only atomic types will be checked for string * versions. All other types MUST be the numeric representation. * * Input: * mx: a matlab array * Output: * The numeric representation of the intended datatype. * **********************************************************************/ nc_type unpackDataType ( const mxArray *mx ) { /* * Shortcut to matlab matrix data. * */ double *pr; /* * Original version of extracted string value. * */ char *typeString; /* * typeString as converted to upper case and possibly stripped * of the leading three characters. * */ char *p; /* * Length of the string. * */ int num_chars; /* * Loop index. * */ int j; if ( mxIsNumeric ( mx ) ) { /* * This part is easy. * */ pr = mxGetPr ( mx ); return ( (nc_type)pr[0] ); } /* * Ok, the g****** user must have given us a character string. * First, extract it and convert it to upper case. * */ typeString = mxArrayToString ( mx ); p = typeString; num_chars = strlen ( typeString ); for ( j = 0; j < num_chars; ++j ) { p[j] = toupper ( p[j] ); } /* * if the leading three characters are "NC_", then shave * it off. See how problematic this is getting??? * */ if ( strncmp ( typeString, "NC_", 3 ) == 0 ) { p = & p[2]; } /* * Now check it against the atomic types that we know about. * */ if ( strcmp ( p, "NAT" ) == 0 ) { return ( NC_NAT ); } else if ( strcmp ( p, "BYTE" ) == 0 ) { return ( NC_BYTE ); } else if ( strcmp ( p, "CHAR" ) == 0 ) { return ( NC_CHAR ); } else if ( strcmp ( p, "SHORT" ) == 0 ) { return ( NC_SHORT ); } else if ( strcmp ( p, "INT" ) == 0 ) { return ( NC_INT ); } else if ( strcmp ( p, "LONG" ) == 0 ) { return ( NC_LONG ); } else if ( strcmp ( p, "FLOAT" ) == 0 ) { return ( NC_FLOAT ); } else if ( strcmp ( p, "DOUBLE" ) == 0 ) { return ( NC_DOUBLE ); } else { sprintf ( error_message, "unable to identify parameter \"%s\"\n", p ); mexErrMsgTxt ( error_message ); } /* * We should never encounter this... * */ sprintf ( error_message, "unable to identify parameter \"%s\"\n", p ); mexErrMsgTxt ( error_message ); return ( NC_NAT ); } /*********************************************************************** * * unpackPtrdiff_t * * This routine unpacks ptrdiff_t data from a matlab matrix. * * Input: * mx: a matlab array * Output: * The function's return value is a pointer to the unpacked string. * **********************************************************************/ ptrdiff_t * unpackPtrdiff_t ( const mxArray *mx ) { /* * Shortcut to matlab matrix data. * */ double *pr; /* * C array that will hold the unpacked ptrdiff_t data. * */ ptrdiff_t *ptrdiff_tp; /* * Number of elements in the size_t array. * */ int buflen; /* * Loop index. * */ int i; buflen = mxGetM(mx) * mxGetN(mx); ptrdiff_tp = (ptrdiff_t *) mxCalloc(buflen, sizeof(ptrdiff_t)); pr = mxGetPr(mx); for (i = 0; i < buflen; i++) { ptrdiff_tp[i] = (ptrdiff_t) (pr[i]); } return (ptrdiff_tp); } /*********************************************************************** * * unpackSize_t * * This routine unpacks size_t data from a matlab matrix. * * Input: * mx: a matlab array * Output: * The function's return value is a pointer to the unpacked string. * **********************************************************************/ size_t * unpackSize_t ( const mxArray *mx ) { /* * Shortcut to matlab matrix data. * */ double *pr; /* * C array that will hold the unpacked size_t data. * */ size_t *size_tp; /* * Number of elements in the size_t array. * */ int buflen; /* * Loop index. * */ int i; buflen = mxGetM(mx) * mxGetN(mx); size_tp = (size_t *) mxCalloc(buflen, sizeof(size_t)); pr = mxGetPr(mx); for (i = 0; i < buflen; i++) { size_tp[i] = (size_t) (pr[i]); } return (size_tp); } /*********************************************************************** * * unpackString * * This routine unpacks a (char *) string from a matlab matrix and * checks the return status appropriately. * * Input: * mx: a matlab array * Output: * The function's return value is a pointer to the unpacked string. * **********************************************************************/ char * unpackString ( const mxArray *mx ) { /* * Defines the size and value of the extracted string. * */ char *theString; int buflen; /* * success or failure of mxGetString function call * */ int status; buflen = mxGetM(mx)*mxGetN(mx) + 1; theString = mxCalloc ( buflen, sizeof(char) ); status = mxGetString( mx, theString, buflen); if ( status == 1 ) { sprintf(error_message, "mxGetString failed, file %s, line %d\n", __FILE__, __LINE__); mexErrMsgTxt ( error_message ); } return (theString); } /*********************************************************************** * * UNPACK_CHAR_FILE_MODE: * * For historical (and bad) reasons, the users have been getting away * with using character strings for the mode being passed into nc_open. * For backwards compatibility purposes, we need to be able to handle * this. * * PARAMETERS: * string_mode: * Can only be a string for one of the earliest codes, such as * "nc_clobber", "nc_noclobber", "nc_write", or "nc_nowrite". * Can also be capitalized, or missing the leading "nc_". * * RETURN VALUE: * Integer mode corresponding to the string mode. * **********************************************************************/ int unpack_char_file_mode ( char *input_string_mode ) { /* * This is the integer mnemonic code that we will be passing into * nc_open. If the user hadn't been such a complete asshole, this * is what they would have given us in the first place. * */ int cmode; /* * Loop index. * */ int j; /* * Same as the input mode, but possibly trimmed of any leading "NC_" * */ char *trimmed_mode; /* * Convert it to lower case. * */ for ( j = 0; j < strlen(input_string_mode); ++j ) { input_string_mode[j] = toupper ( input_string_mode[j] ); } /* * Trim off any leading "NC_" * */ if ( strncmp ( input_string_mode, "NC_", 3 ) == 0 ) { trimmed_mode = &input_string_mode[3]; } else { trimmed_mode = input_string_mode; } /* * Compare to any of the earlier NC2 modes. For anything more advanced, * the user has to use numerical codes. * */ if ( strcmp(trimmed_mode,"CLOBBER") == 0 ) { cmode = NC_CLOBBER; } else if ( strcmp(trimmed_mode,"NOCLOBBER") == 0 ) { cmode = NC_NOCLOBBER; } else if ( strcmp(trimmed_mode,"WRITE") == 0 ) { cmode = NC_WRITE; } else if ( strcmp(trimmed_mode,"NOWRITE") == 0 ) { cmode = NC_NOWRITE; } else { sprintf ( error_message, "unknown parameter \"%s\", line %d file \"%s\"\n", input_string_mode, __LINE__, __FILE__ ); mexErrMsgTxt ( error_message ); } return ( cmode ); } /*********************************************************************** * * VARM_COORD_SANITY_CHECK * * Check that the lengths of the input "start", "count", "stride", and * "imap" matrices match the rank of the netcdf variable. * * Inputs: * prhs: * Array of matlab array structures. * ndims: * This is the rank of the netcdf variable being worked with. * * Outputs: * None. If the routine does not succeed, a matlab exception is * thrown. * **********************************************************************/ void varm_coord_sanity_check ( const mxArray *prhs[], int ndims ) { /* * Loop index. * */ int j; /* * number of rows, columns of "start", "count", "stride", and "imap" coord arrays. * */ int m, n; /* * length of "start", "count", "stride", and "imap" coord arrays * */ int vector_length; for ( j = 3; j < 7; ++j ) { /* * Check that the input coordinates are 1-D arrays. */ m = mxGetM ( prhs[j] ); n = mxGetN ( prhs[j] ); if ( ( m == 1 ) || ( n == 1 ) ) { ; } else { sprintf ( error_message, "input array %d must be a 1D vector, not %dx%d\n", j, m, n ); mexErrMsgTxt ( error_message ); } /* * Check that the vector length is ok. Must be the same as netcdf variable rank. */ vector_length = m*n; if ( vector_length != ndims ) { sprintf ( error_message, "input array %d must have a length equal to the netcdf variable rank, %d\n", j, ndims ); mexErrMsgTxt ( error_message ); } } }