Mercurial > hg > octave-max
diff src/mex.cc @ 5864:e884ab4f29ee
[project @ 2006-06-22 00:57:27 by jwe]
author | jwe |
---|---|
date | Thu, 22 Jun 2006 00:57:28 +0000 |
parents | |
children | 4f1112bfafce |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/src/mex.cc @@ -0,0 +1,1307 @@ +/* + +Copyright (C) 2001, 2006 Paul Kienzle + +This file is part of Octave. + +Octave is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2, or (at your option) any +later version. + +Octave is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with Octave; see the file COPYING. If not, write to the Free +Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301, USA. + +*/ + +// This code was originally distributed as part of Octave Forge under +// the follwoing terms: +// +// Author: Paul Kienzle +// I grant this code to the public domain. +// 2001-03-22 +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +// PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +// USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +// SUCH DAMAGE. + +#include <cfloat> +#include <csetjmp> +#include <cstdlib> + +#include <iomanip> +#include <set> +#include <string> + +typedef void *Pix; +typedef std::set<Pix> MemSet; + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "oct.h" +#include "pager.h" +#include "f77-fcn.h" +#include "unwind-prot.h" +#include "lo-mappers.h" +#include "lo-ieee.h" +#include "parse.h" +#include "toplev.h" +#include "variables.h" +#include "oct-map.h" +#include "str-vec.h" + +// mex file context +// +// Class mex keeps track of all memory allocated and frees anything +// not explicitly marked persistent when the it is destroyed. It also +// maintains the setjump/longjump buffer required for non-local exit +// from the mex file, and any other state local to this instance of +// the mex function invocation. +class mxArray; + +// Prototypes for external funcitons. Must declare mxArray as a class +// before including this file. +#include "mexproto.h" + +class mex +{ +public: + + mex (void) { } + + ~mex (void) + { + if (! memlist.empty ()) + error("mex: no cleanup performed"); + } + + // free all unmarked pointers obtained from malloc and calloc + static void cleanup (void *context); + + // allocate a pointer, and mark it to be freed on exit + Pix malloc (int n); + + // allocate a pointer to be freed on exit, and initialize to 0 + Pix calloc (int n, int t); + + // reallocate a pointer obtained from malloc or calloc + Pix realloc (Pix ptr, int n); + + // free a pointer obtained from malloc or calloc + void free (Pix ptr); + + // mark a pointer so that it will not be freed on exit + void persistent (Pix ptr) { unmark (ptr); } + + // make a new array value and initialize it with zeros; it will be + // freed on exit unless marked as persistent + mxArray *make_value (int nr, int nc, int cmplx); + + // make a new array value and initialize from an octave value; it will be + // freed on exit unless marked as persistent + mxArray *make_value (const octave_value&); + + // make a new structure value and initialize with empty matrices + // FIXME does this leak memory? Is it persistent? + mxArray *make_value (int nr, int nc, const string_vector& keys); + + // free an array and its contents + void free_value (mxArray *ptr); + + // mark an array and its contents so it will not be freed on exit + void persistent (mxArray *ptr); + + // 1 if error should be returned to MEX file, 0 if abort + int trap_feval_error; + + // longjmp return point if mexErrMsgTxt or error + jmp_buf jump; + + // trigger a long jump back to the mex calling function + void abort (void) { longjmp (jump, 1); } + +private: + + // list of memory resources that need to be freed upon exit + MemSet memlist; + + // mark a pointer to be freed on exit + void mark (Pix p); + + // unmark a pointer to be freed on exit, either because it was + // made persistent, or because it was already freed + void unmark (Pix p); +}; + +// Current context +mex *__mex = 0; + +// free all unmarked pointers obtained from malloc and calloc +void +mex::cleanup (Pix ptr) +{ + mex *context = static_cast<mex *> (ptr); + + for (MemSet::iterator p = context->memlist.begin (); + p != context->memlist.end (); p++) + ::free (*p); + + context->memlist.clear (); +} + +// mark a pointer to be freed on exit +void +mex::mark (Pix p) +{ +#ifdef DEBUG + if (memlist.find (p) != memlist.end ()) + warning ("%s: double registration ignored", mexFunctionName ()); +#endif + + memlist.insert (p); +} + +// unmark a pointer to be freed on exit, either because it was +// made persistent, or because it was already freed +void +mex::unmark (Pix p) +{ +#ifdef DEBUG + if (memlist.find (p) != memlist.end ()) + warning ("%s: value not marked", mexFunctionName ()); +#endif + + memlist.erase (p); +} + +// allocate a pointer, and mark it to be freed on exit +Pix +mex::malloc (int n) +{ + if (n == 0) + return 0; +#if 0 + // FIXME -- how do you allocate and free aligned, non-typed + // memory in C++? + Pix ptr = Pix (new double[(n+sizeof(double)-1)/sizeof(double)]); +#else + // FIXME -- can we mix C++ and C-style heap management? + Pix ptr = ::malloc (n); + + if (! ptr) + { + // FIXME -- could use "octave_new_handler();" instead + error ("%s: out of memory", mexFunctionName ()); + abort (); + } +#endif + + mark (ptr); + + return ptr; +} + +// allocate a pointer to be freed on exit, and initialize to 0 +Pix +mex::calloc (int n, int t) +{ + Pix v = malloc (n*t); + + memset (v, 0, n*t); + + return v; +} + +// reallocate a pointer obtained from malloc or calloc +Pix +mex::realloc (Pix ptr, int n) +{ +#if 0 + error ("%s: cannot reallocate using C++ new/delete operations", + mexFunctionName ()); + abort (); +#else + Pix v = 0; + if (n == 0) + free (ptr); + else if (! ptr) + v = malloc (n); + else + { + v = ::realloc (ptr, n); + MemSet::iterator p = memlist.find (ptr); + if (v && p != memlist.end ()) + { + memlist.erase (p); + memlist.insert (v); + } + } +#endif + return v; +} + +// free a pointer obtained from malloc or calloc +void +mex::free (Pix ptr) +{ + unmark (ptr); +#if 0 + delete [] ptr; +#else + ::free (ptr); +#endif +} + +// mxArray data type +// +// Class mxArray is not much more than a struct for keeping together +// dimensions and data. It doesn't even ensure consistency between +// the dimensions and the data. Unfortunately you can't do better +// than this without restricting the operations available in Matlab +// for directly manipulating its mxArray type. + +typedef unsigned short mxChar; +const int mxMAXNAM=64; + +class mxArray +{ +public: + + mxArray(void) + { + nr = nc = -1; + pr = pi = NULL; + keys = NULL; + pmap = NULL; + isstr = false; + aname[0] = '\0'; + } + + ~mxArray (void) + { + if (pmap) + { + // FIXME why don't string_vectors work? + for (int i = 0; i < pmap->length (); i++) + delete [] keys[i]; + + delete [] keys; + } + } + + octave_value as_octave_value (void) const; + + int rows (void) const { return nr; } + int columns (void) const { return nc; } + void rows (int r) { nr = r; } + void columns (int c) { nc = c; } + int dims (void) const { return 2; } + + double *imag (void) const { return pi; } + double *real (void) const { return pr; } + void imag (double *p) { pi = p; } + void real (double *p) { pr = p; } + + bool is_empty (void) const { return nr==0 || nc==0; } + bool is_numeric (void) const { return ! isstr && (pr || nr == 0 || nc == 0); } + bool is_complex (void) const { return pi; } + bool is_sparse (void) const { return false; } + bool is_struct (void) const { return pmap; } + + bool is_string (void) const { return isstr; } + void is_string (bool set) { isstr = set; } + + const char *name (void) const { return aname; } + void name (const char *nm) + { + strncpy (aname, nm, mxMAXNAM); + aname[mxMAXNAM]='\0'; + } + + // Structure support functions. Matlab uses a fixed field order + // (the order in which the fields were added?), but Octave uses an + // unordered hash for structs. We can emulate a fixed field order + // using pmap->keys(), which returns a string_vector of key names, + // but these keys will not be in the same order as the keys given in + // mxCreateStruct*. Within the creating function, we can populate + // the key name vector in the order given, so the only problem will + // be those functions which assume the key order is maintained + // between calls from Matlab. Unfortunately, these might exist and + // I can't detect them :-( + + // Return the map value + Octave_map *map (void) const { return pmap; } + + // New structure with the given presumed field order (CreateStruct call) + void map (Octave_map *p, const string_vector& mapkeys) + { + pmap = p; + keys = mapkeys.c_str_vec (); + } + + // New structure with unknown field order (passed in from Octave) + void map (Octave_map *p) + { + pmap = p; + if (p) + keys = p->keys().c_str_vec (); + } + + // Get field given field name + mxArray *field (const std::string& key_arg, const int index) const + { + if (pmap && pmap->contains (key_arg)) + return __mex->make_value (pmap->contents(key_arg)(index)); + else + return 0; + } + + // Set field given field name + void field (const std::string& key_arg, const int index, mxArray *value) + { + if (pmap) + pmap->assign (octave_value (index+1), + key_arg, Cell (value->as_octave_value ())); + + if (error_state) + __mex->abort (); + } + + // Return number of fields in structure + int num_keys(void) const { return pmap ? pmap->length () : 0; } + + // Return field name from field number + const std::string key (const int key_num) const + { + if (key_num >= 0 && key_num < pmap->length ()) + return keys[key_num]; + else + return 0; + } + // Return field number from field name + int key (const std::string &key_name) const + { + for (int i = 0; i < pmap->length (); i++) + if (key_name == std::string (keys[i])) + return i; + + return -1; + } + + // Get field using field number + mxArray *field (const int key_num, const int index) const + { + if (key_num >= 0 && key_num < pmap->length ()) + return field (keys[key_num], index); + else + return 0; + } + + // Set field using field number + void field (const int key_num, const int index , mxArray *value) + { + if (key_num >= 0 && key_num < pmap->length ()) + field (keys[key_num], index, value); + } + +private: + int nr; + int nc; + double *pr; + double *pi; + // FIXME -- need to have a typeid here instead of complex logic on + // isstr, pmap, pr, pi, etc. + Octave_map *pmap; + // string_vector keys; + char **keys; + bool isstr; + char aname[mxMAXNAM+1]; +}; + +octave_value +mxArray::as_octave_value (void) const +{ + octave_value ret; + + if (isstr) + { + charMatrix chm (nr, nc); + char *pchm = chm.fortran_vec (); + for (int i=0; i < nr*nc; i++) + pchm[i] = NINT (pr[i]); + ret = octave_value (chm, true); + } + else if (pmap) + { + ret = octave_value (*pmap); + } + else if (pi) + { + ComplexMatrix cm (nr, nc); + Complex *pcm = cm.fortran_vec (); + for (int i=0; i < nr*nc; i++) + pcm[i] = Complex (pr[i], pi[i]); + ret = cm; + } + else if (pr) + { + Matrix m (nr, nc); + double *pm = m.fortran_vec (); + memcpy (pm, pr, nr*nc*sizeof(double)); + ret = m; + } + else + ret = Matrix (0, 0); + + return ret; +} + + +// mex/mxArray interface + +// Make a new array value and initialize from an octave value; it will +// be freed on exit unless marked as persistent. + +mxArray *mex::make_value(const octave_value &ov) +{ + int nr = -1; + int nc = -1; + double *pr = 0; + double *pi = 0; + Octave_map *pmap = 0; + + if (ov.is_numeric_type () || ov.is_string ()) + { + nr = ov.rows (); + nc = ov.columns (); + } + if (ov.is_map ()) + { + pmap = new Octave_map (ov.map_value ()); + nr = ov.rows (); + nc = ov.columns (); + } + else if (nr > 0 && nc > 0) + { + if (ov.is_string ()) + { + // FIXME - must use 16 bit unicode to represent strings. + const Matrix m (ov.matrix_value (1)); + pr = static_cast<double *> (malloc(nr*nc*sizeof(double))); + memcpy (pr, m.data (), nr*nc*sizeof(double)); + } + else if (ov.is_complex_type ()) + { + // FIXME -- may want to consider lazy copying of the + // matrix, but this will only help if the matrix is being + // passed on to octave via callMATLAB later. + const ComplexMatrix cm (ov.complex_matrix_value ()); + const Complex *pz = cm.data (); + pr = static_cast<double *> (malloc (nr*nc*sizeof(double))); + pi = static_cast<double *> (malloc (nr*nc*sizeof(double))); + for (int i = 0; i < nr*nc; i++) + { + pr[i] = real (pz[i]); + pi[i] = imag (pz[i]); + } + } + else + { + const Matrix m (ov.matrix_value ()); + pr = static_cast<double *> (malloc (nr*nc*sizeof(double))); + memcpy (pr, m.data (), nr*nc*sizeof(double)); + } + } + + mxArray *value = static_cast<mxArray *> (malloc (sizeof(mxArray))); + + value->is_string (ov.is_string ()); + value->real (pr); + value->imag (pi); + value->map (pmap); + value->rows (nr); + value->columns (nc); + value->name (""); + + return value; +} + +// Make a new array value and initialize it with zeros; it will be +// freed on exit unless marked as persistent. + +mxArray * +mex::make_value (int nr, int nc, int cmplx) +{ + + mxArray *value = static_cast<mxArray *> (malloc (sizeof(mxArray))); + double *p = static_cast<double *> (calloc (nr*nc, sizeof(double))); + + value->real (p); + if (cmplx) + value->imag (static_cast<double *> (calloc (nr*nc, sizeof(double)))); + else + value->imag (static_cast<double *> (Pix (0))); + value->rows (nr); + value->columns (nc); + value->is_string (false); + value->map (0); + value->name (""); + + return value; +} + +// Make a new structure value and initialize with empty matrices +// FIXME does this leak memory? Is it persistent? + +mxArray * +mex::make_value (int nr, int nc, const string_vector& keys) +{ + if (keys.length () == 0) + return 0; + + Cell empty (nr, nc); + Octave_map *pmap = new Octave_map (keys[0], empty); + for (int i=1; i < keys.length (); i++) + pmap->assign (keys[i], empty); + + mxArray *value = static_cast<mxArray *> (malloc (sizeof(mxArray))); + + value->rows (nr); + value->columns (nc); + value->map (pmap, keys); + + return value; +} + +// free an array and its contents + +void +mex::free_value (mxArray *ptr) +{ + free (ptr->real ()); + free (ptr->imag ()); + free (ptr); +} + +// mark an array and its contents so it will not be freed on exit + +void +mex::persistent (mxArray *ptr) +{ + persistent (Pix (ptr->real ())); + persistent (Pix (ptr->imag ())); + persistent (Pix (ptr)); +} + + +// Octave interface to mex files + +#if 0 +// Don't bother trapping stop/exit +// To trap for STOP in fortran code, this needs to be registered with atexit +static void mex_exit() +{ + if (__mex) + { + error ("%s: program aborted", mexFunctionName ()); + __mex->abort (); + } +} +#endif + +typedef void (*cmex_fptr) (int nlhs, mxArray **plhs, int nrhs, mxArray **prhs); +typedef F77_RET_T (*fmex_fptr) (int& nlhs, mxArray **plhs, int& nrhs, mxArray **prhs); + +enum callstyle { use_fortran, use_C }; + +octave_value_list +call_mex (callstyle cs, void *f, const octave_value_list& args, int nargout) +{ +#if 0 + // Don't bother trapping stop/exit + // FIXME -- should really push "mex_exit" onto the octave + // atexit stack before we start and pop it when we are through, but + // the stack handle isn't exported from toplev.cc, so we can't. mex_exit + // would have to be declared as DEFUN(mex_exit,,,"") of course. + static bool unregistered = true; + if (unregistered) + { + atexit (mex_exit); + unregistered = false; + } +#endif + + // Use nargout+1 since even for zero specified args, still want to + // be able to return an ans. + + int nargin = args.length (); + OCTAVE_LOCAL_BUFFER(mxArray*, argin, nargin); + for (int i = 0; i < nargin; i++) + argin[i] = 0; + + int nout = nargout == 0 ? 1 : nargout; + OCTAVE_LOCAL_BUFFER(mxArray*, argout, nout); + for (int i = 0; i < nout; i++) + argout[i] = 0; + + mex context; + unwind_protect::add (mex::cleanup, Pix (&context)); + + for (int i = 0; i < nargin; i++) + argin[i] = context.make_value (args(i)); + + // Save old mex pointer. + unwind_protect_ptr (__mex); + + if (setjmp (context.jump) == 0) + { + __mex = &context; + + if (cs == use_fortran) + { + fmex_fptr fcn = FCN_PTR_CAST (fmex_fptr, f); + + int tmp_nargout = nargout; + int tmp_nargin = nargin; + + fcn (tmp_nargout, argout, tmp_nargin, argin); + } + else + { + cmex_fptr fcn = FCN_PTR_CAST (cmex_fptr, f); + + fcn (nargout, argout, nargin, argin); + } + } + + // Restore old mex pointer. + unwind_protect::run (); + + // Convert returned array entries back into octave values. + + octave_value_list retval; + + if (! error_state) + { + if (nargout == 0 && argout[0]) + retval(0) = argout[0]->as_octave_value (); + else + { + retval.resize (nargout); + + for (int i = 0; i < nargout; i++) + if (argout[i]) + retval(i) = argout[i]->as_octave_value (); + } + } + + // Clean up mex resources. + unwind_protect::run (); + + return retval; +} + +octave_value_list +Fortran_mex (void *f, const octave_value_list& args, int nargout) +{ + return call_mex (use_fortran, f, args, nargout); +} + +octave_value_list +C_mex (void *f, const octave_value_list& args, int nargout) +{ + return call_mex (use_C, f, args, nargout); +} + +// C interface to mex functions: + +extern "C" { + +const char * +mexFunctionName (void) +{ + static char *retval = 0; + + delete [] retval; + + octave_function *fcn = octave_call_stack::current (); + + if (fcn) + { + std::string nm = fcn->name (); + retval = strsave (nm.c_str ()); + } + else + retval = strsave ("unknown"); + + return retval; +} + +void +mexErrMsgTxt (const char *s) +{ + if (s && strlen (s) > 0) + error("%s: %s", mexFunctionName (), s); + else + // Just set the error state; don't print msg. + error (""); + + __mex->abort(); +} + +void mexWarnMsgTxt (const char *s) { warning("%s", s); } + +void +mexPrintf (const char *fmt, ...) +{ + va_list args; + va_start (args, fmt); + octave_vformat (octave_stdout, fmt, args); + va_end (args); +} + +// Floating point representation. + +int mxIsNaN (const double v) { return lo_ieee_isnan (v) != 0; } +int mxIsFinite (const double v) { return lo_ieee_finite (v) != 0; } +int mxIsInf (const double v) { return lo_ieee_isinf (v) != 0; } + +double mxGetEps (void) { return DBL_EPSILON; } +double mxGetInf (void) { return lo_ieee_inf_value (); } +double mxGetNaN (void) { return lo_ieee_nan_value (); } + +int +mexEvalString (const char *s) +{ + int parse_status; + octave_value_list ret; + ret = eval_string (s, false, parse_status, 0); + if (parse_status || error_state) + { + error_state = 0; + return 1; + } + else + return 0; +} + +int +mexCallMATLAB (int nargout, mxArray *argout[], + int nargin, mxArray *argin[], + const char *fname) +{ + octave_value_list args; + + // FIXME -- do we need unwind protect to clean up args? Off hand, I + // would say that this problem is endemic to Octave and we will + // continue to have memory leaks after Ctrl-C until proper exception + // handling is implemented. longjmp() only clears the stack, so any + // class which allocates data on the heap is going to leak. + + args.resize (nargin); + + for (int i = 0; i < nargin; i++) + args(i) = argin[i]->as_octave_value (); + + octave_value_list retval = feval (fname, args, nargout); + + if (error_state && __mex->trap_feval_error == 0) + { + // FIXME -- is this the correct way to clean up? abort() is + // going to trigger a long jump, so the normal class destructors + // will not be called. Hopefully this will reduce things to a + // tiny leak. Maybe create a new octave memory tracer type + // which prints a friendly message every time it is + // created/copied/deleted to check this. + + args.resize (0); + retval.resize (0); + __mex->abort (); + } + + int num_to_copy = retval.length (); + + if (nargout < retval.length ()) + num_to_copy = nargout; + + for (int i = 0; i < num_to_copy; i++) + { + // FIXME -- it would be nice to avoid copying the value here, + // but there is no way to steal memory from a matrix, never mind + // that matrix memory is allocated by new[] and mxArray memory + // is allocated by malloc(). + argout[i] = __mex->make_value (retval (i)); + } + + while (num_to_copy < nargout) + argout[num_to_copy++] = 0; + + if (error_state) + { + error_state = 0; + return 1; + } + else + return 0; +} + +void mexSetTrapFlag (int flag) { __mex->trap_feval_error = flag; } + +Pix mxMalloc (int n) { return __mex->malloc(n); } +Pix mxCalloc (int n, int size) { return __mex->calloc (n, size); } +Pix mxRealloc (Pix ptr, int n) { return __mex->realloc (ptr, n); } +void mxFree (Pix ptr) { __mex->free (ptr); } +void mexMakeMemoryPersistent (Pix ptr) { __mex->persistent (ptr); } + +mxArray * +mxCreateDoubleMatrix (int nr, int nc, int iscomplex) +{ + return __mex->make_value(nr, nc, iscomplex); +} + +mxArray * +mxCreateDoubleScalar (double val) +{ + mxArray *ptr = mxCreateDoubleMatrix (1, 1, 0); + *mxGetPr (ptr) = val; + return ptr; +} + +mxArray * +mxCreateLogicalScalar (int val) +{ + mxArray *ptr = mxCreateDoubleMatrix (1, 1, 0); + *mxGetPr (ptr) = val; + return ptr; +} + +void mxDestroyArray (mxArray *v) { __mex->free (v); } + +mxArray * +mxDuplicateArray (const mxArray *ptr) +{ + return __mex->make_value (ptr->as_octave_value ()); +} + +void mexMakeArrayPersistent (mxArray *ptr) { __mex->persistent (ptr); } + +int mxIsChar (const mxArray *ptr) { return ptr->is_string (); } +int mxIsSparse (const mxArray *ptr) { return ptr->is_sparse (); } +int mxIsFull(const mxArray *ptr) { return !ptr->is_sparse (); } +int mxIsNumeric (const mxArray *ptr) { return ptr->is_numeric (); } +int mxIsComplex (const mxArray *ptr) { return ptr->is_complex (); } +int mxIsDouble (const mxArray *) { return true; } +int mxIsEmpty (const mxArray *ptr) { return ptr->is_empty (); } + +int +mxIsLogicalScalar (const mxArray *ptr) +{ + return (ptr->is_numeric () + && ptr->rows () == 1 && ptr->columns () == 1 + && *ptr->real ()); +} + +double *mxGetPr (const mxArray *ptr) { return ptr->real (); } +double *mxGetPi (const mxArray *ptr) { return ptr->imag (); } +int mxGetM (const mxArray *ptr) { return ptr->rows (); } +int mxGetN (const mxArray *ptr) { return ptr->columns (); } +int mxGetNumberOfDimensions (const mxArray *ptr) { return ptr->dims (); } +int mxGetNumberOfElements (const mxArray *ptr) { return ptr->rows () * ptr->columns (); } +void mxSetM (mxArray *ptr, int M) { ptr->rows (M); } +void mxSetN (mxArray *ptr, int N) { ptr->columns (N); } +void mxSetPr (mxArray *ptr, double *pr) { ptr->real (pr); } +void mxSetPi (mxArray *ptr, double *pi) { ptr->imag (pi); } + +double +mxGetScalar (const mxArray *ptr) +{ + double *pr = ptr->real (); + if (! pr) + mexErrMsgTxt ("calling mxGetScalar on an empty matrix"); + return pr[0]; +} + +int +mxGetString (const mxArray *ptr, char *buf, int buflen) +{ + if (ptr->is_string ()) + { + int nr = ptr->rows (); + int nc = ptr->columns (); + int n = nr*nc < buflen ? nr*nc : buflen; + const double *pr = ptr->real (); + for (int i = 0; i < n; i++) + buf[i] = NINT (pr[i]); + if (n < buflen) + buf[n] = '\0'; + return n >= buflen; + } + else + return 1; +} + +char * +mxArrayToString (const mxArray *ptr) +{ + int nr = ptr->rows (); + int nc = ptr->columns (); + int n = nr*nc*sizeof(mxChar)+1; + char *buf = static_cast<char *> (mxMalloc (n)); + if (buf) + mxGetString (ptr, buf, n); + + return buf; +} + +mxArray * +mxCreateString (const char *str) +{ + int n = strlen (str); + mxArray *m = __mex->make_value (1, n, 0); + if (! m) + return m; + m->is_string (true); + + double *pr = m->real (); + for (int i = 0; i < n; i++) + pr[i] = str[i]; + + return m; +} + +mxArray * +mxCreateCharMatrixFromStrings (int n, const char **str) +{ + // Find length of the individual strings. + Array<int> len (n); + + for (int i = 0; i < n; i++) + len(i) = strlen (str[i]); + + // Find maximum length. + int maxlen = 0; + for (int i = 0; i < n; i++) + if (len(i) > maxlen) + maxlen = len(i); + + // Need a place to copy them. + mxArray *m = __mex->make_value (n, maxlen, 0); + if (! m) + return m; + m->is_string (true); + + // Do the copy (being sure not to exceed the length of any of the + // strings). + double *pr = m->real (); + for (int j = 0; j < maxlen; j++) + for (int i = 0; i < n; i++) + if (j < len(i)) + *pr++ = str[i][j]; + else + *pr++ = '\0'; + + return m; +} + +int +mexPutVariable (const char *space, const char *name, mxArray *ptr) +{ + if (! ptr) + return 1; + + if (! name) + return 1; + + if (name[0] == '\0') + name = ptr->name (); + + if (! name || name[0] == '\0') + return 1; + + if (! strcmp (space, "global")) + set_global_value (name, ptr->as_octave_value ()); + else if (! strcmp (space, "caller")) + { + // FIXME -- this belongs in variables.cc. + symbol_record *sr = curr_sym_tab->lookup (name, true); + if (sr) + sr->define (ptr->as_octave_value ()); + else + panic_impossible (); + } + else if (! strcmp (space, "base")) + mexErrMsgTxt ("mexPutVariable: 'base' symbol table not implemented"); + else + mexErrMsgTxt ("mexPutVariable: symbol table does not exist"); + return 0; +} + +mxArray * +mexGetArray (const char *name, const char *space) +{ + mxArray *retval = 0; + + // FIXME -- this should be in variable.cc, but the correct + // functionality is not exported. Particularly, get_global_value() + // generates an error if the symbol is undefined. + + symbol_record *sr = 0; + + if (! strcmp (space, "global")) + sr = global_sym_tab->lookup (name); + else if (! strcmp (space, "caller")) + sr = curr_sym_tab->lookup (name); + else if (! strcmp (space, "base")) + mexErrMsgTxt ("mexGetArray: 'base' symbol table not implemented"); + else + mexErrMsgTxt ("mexGetArray: symbol table does not exist"); + + if (sr) + { + octave_value sr_def = sr->def (); + + if (sr_def.is_defined ()) + { + retval = __mex->make_value (sr_def); + retval->name (name); + } + } + + return retval; +} + +mxArray * +mexGetArrayPtr (const char *name, const char *space) +{ + return mexGetArray (name, space); +} + +const char *mxGetName (const mxArray *ptr) { return ptr->name (); } + +void mxSetName (mxArray *ptr, const char*nm) { ptr->name (nm); } + +mxArray * +mxCreateStructMatrix (int nr, int nc, int num_keys, const char **keys) +{ + const string_vector ordered_keys (keys, num_keys); + mxArray *m = __mex->make_value (nr, nc, ordered_keys); + return m; +} + +mxArray * +mxGetField (const mxArray *ptr, int index, const char *key) +{ + return ptr->field (key, index); +} + +void +mxSetField (mxArray *ptr, int index, const char *key, mxArray *val) +{ + ptr->field (key, index, val); +} + +int mxGetNumberOfFields (const mxArray *ptr) { return ptr->num_keys (); } +int mxIsStruct (const mxArray *ptr) { return ptr->is_struct (); } + +const char * +mxGetFieldNameByNumber (const mxArray *ptr, int key_num) +{ + return ptr->key(key_num).c_str (); +} + +int +mxGetFieldNumber (const mxArray *ptr, const char *key) +{ + return ptr->key (key); +} +mxArray * +mxGetFieldByNumber (const mxArray *ptr, int index, int key_num) +{ + return ptr->field (key_num, index); +} +void +mxSetFieldByNumber (mxArray *ptr, int index, int key_num, mxArray *val) +{ + return ptr->field (key_num,index,val); +} + +} // extern "C" + +// Fortran interface to mex functions +// +// Where possible, these call the equivalent C function since that API +// is fixed. It costs and extra function call, but is easier to +// maintain. + +extern "C" { + +void F77_FUNC (mexerrmsgtxt, MEXERRMSGTXT) (const char *s, long slen) +{ + if (slen > 1 || (slen == 1 && s[0] != ' ') ) + error ("%s: %.*s", mexFunctionName (), slen, s); + else + // Just set the error state; don't print msg. + error (""); + + __mex->abort(); +} + +void F77_FUNC (mexprintf, MEXPRINTF) (const char *s, long slen) +{ + mexPrintf ("%.*s\n", slen, s); +} + +double F77_FUNC (mexgeteps, MEXGETEPS) (void) { return mxGetEps (); } +double F77_FUNC (mexgetinf, MEXGETINF) (void) { return mxGetInf (); } +double F77_FUNC (mexgetnan, MEXGETNAN) (void) { return mxGetNaN (); } +int F77_FUNC (mexisfinite, MEXISFINITE) (double v) { return mxIsFinite (v); } +int F77_FUNC (mexisinf, MEXISINF) (double v) { return mxIsInf (v); } +int F77_FUNC (mexisnan, MEXISNAN) (double v) { return mxIsNaN (v); } + +// Array access: + +Pix F77_FUNC (mxcreatefull, MXCREATEFULL) + (const int& nr, const int& nc, const int& iscomplex) +{ + return mxCreateDoubleMatrix (nr, nc, iscomplex); +} + +void F77_FUNC (mxfreematrix, MXFREEMATRIX) (mxArray* &p) +{ + mxDestroyArray (p); +} + +Pix F77_FUNC (mxcalloc, MXCALLOC) (const int& n, const int& size) +{ + return mxCalloc (n, size); +} + +void F77_FUNC (mxfree, MXFREE) (const Pix &p) { mxFree (p); } + +int F77_FUNC (mxgetm, MXGETM) (const mxArray* &p) { return mxGetM (p); } +int F77_FUNC (mxgetn, MXGETN) (const mxArray* &p) { return mxGetN (p); } + +Pix F77_FUNC (mxgetpi, MXGETPI) (const mxArray* &p) { return mxGetPi (p); } +Pix F77_FUNC (mxgetpr, MXGETPR) (const mxArray* &p) { return mxGetPr (p); } + +void F77_FUNC (mxsetm, MXSETM) (mxArray* &p, const int& m) { mxSetM (p, m); } +void F77_FUNC (mxsetn, MXSETN) (mxArray* &p, const int& n) { mxSetN (p, n); } + +void F77_FUNC (mxsetpi, MXSETPI) (mxArray* &p, double *pi) { mxSetPi (p, pi); } +void F77_FUNC (mxsetpr, MXSETPR) (mxArray* &p, double *pr) { mxSetPr (p, pr); } + +int F77_FUNC (mxiscomplex, MXISCOMPLEX) (const mxArray* &p) +{ + return mxIsComplex (p); +} + +int F77_FUNC (mxisdouble, MXISDOUBLE) (const mxArray* &p) +{ + return mxIsDouble (p); +} + +int F77_FUNC (mxisnumeric, MXISNUMERIC) (const mxArray* &p) +{ + return mxIsNumeric(p); +} + +int F77_FUNC (mxisfull, MXISFULL) (const mxArray* &p) +{ + return 1 - mxIsSparse (p); +} + +int F77_FUNC (mxissparse, MXISSPARSE) (const mxArray* &p) +{ + return mxIsSparse (p); +} + +int F77_FUNC (mxisstring, MXISSTRING) (const mxArray* &p) +{ + return mxIsChar (p); +} + +int F77_FUNC (mxgetstring, MXGETSTRING) + (const mxArray* &ptr, char *str, const int& len) +{ + return mxGetString (ptr, str, len); +} + +int F77_FUNC (mexcallmatlab, MEXCALLMATLAB) + (const int& nargout, mxArray **argout, + const int& nargin, mxArray **argin, + const char *fname, + long fnamelen) +{ + char str[mxMAXNAM+1]; + strncpy (str, fname, (fnamelen < mxMAXNAM ? fnamelen : mxMAXNAM)); + str[fnamelen] = '\0'; + return mexCallMATLAB (nargout, argout, nargin, argin, str); +} + +// Fake pointer support: + +void F77_FUNC (mxcopyreal8toptr, MXCOPYREAL8TOPTR) + (const double *d, const int& prref, const int& len) +{ + double *pr = (double *) prref; + for (int i = 0; i < len; i++) + pr[i] = d[i]; +} + +void F77_FUNC (mxcopyptrtoreal8, MXCOPYPTRTOREAL8) + (const int& prref, double *d, const int& len) +{ + double *pr = (double *) prref; + for (int i = 0; i < len; i++) + d[i] = pr[i]; +} + +void F77_FUNC (mxcopycomplex16toptr, MXCOPYCOMPLEX16TOPTR) + (const double *d, int& prref, int& piref, const int& len) +{ + double *pr = (double *) prref; + double *pi = (double *) piref; + for (int i = 0; i < len; i++) + { + pr[i] = d[2*i]; + pi[i] = d[2*i+1]; + } +} + +void F77_FUNC (mxcopyptrtocomplex16, MXCOPYPTRTOCOMPLEX16) + (const int& prref, const int& piref, double *d, const int& len) +{ + double *pr = (double *) prref; + double *pi = (double *) piref; + for (int i = 0; i < len; i++) + { + d[2*i] = pr[i]; + d[2*i+1] = pi[i]; + } +} + +} // extern "C" + +/* +;;; Local Variables: *** +;;; mode: C++ *** +;;; End: *** +*/