# HG changeset patch # User dbateman # Date 1179305388 0 # Node ID 5d02dfacfc9eb858d3f48f7f37b06dd6a90fa594 # Parent 0d69a50fc5a9f02d7896c55f2dccd9149c599717 [project @ 2007-05-16 08:49:47 by dbateman] diff -r 0d69a50fc5a9 -r 5d02dfacfc9e src/ChangeLog --- a/src/ChangeLog Tue May 15 20:17:27 2007 +0000 +++ b/src/ChangeLog Wed May 16 08:49:48 2007 +0000 @@ -1,3 +1,48 @@ +2007-05-16 David Bateman + + * load_pathc.cc (std::string octave_system_path (void)): New + function. + * load-path.h (std::string octave_system_path (void)): Declare it. + + * load-save.cc (static load_save_format get_file_format + (std::istream&, const std::string&)): Add filename argument, and + pass it to read_mat5_binary_header. Use new format throughout file. + (Fload): Don't allow gzip of matlab v7 files as the files + themselves include compression. + + * ls-mat5.cc (arrayclsstype:MAT_FILE_WORKSPACE_CLASS): New class + type. + (read_mat5_binary_element): Workspaces, don't have dimensions, so + don't read them. Implement read of matlab objects, but only use + them for inline functions. Implement reading of function and + workspace classes. + (read_mat5_binary_header): Add filename argument. Read sub-system + specific data block given as an offset in bytes 117 to 124. + (save_mat5_binary_element): Include saving of inline functions. + + * ls-mat5.h (read_mat5_binary_header): Include filename. + + * ov-fcn-handle.cc (octave_fcn_handle_save_ascii, + octave_fcn_handle::load_ascii, octave_fcn_handle::save_binary, + octave_fcn_handle::load_binary, octave_fcn_handle::save_hdf5, + octave_fcn_handle::load_hdf5): Save and reload the local symbol + table of the user function associated with anonymous function + handles. Save and load the absolute path and the exec_prefix for + normal function handles and use then to find equivalent functions + between different installations of Octave. Attempt to maintain + backward and forward compatibility. + (Ffunctions): Additional outputs, including the workspace of + anonymous functions, and more compatiable outputs. + + * ov-fcn-handle.h (user_function_value): Expose the user function + value of a function handle. + + * ov-fcn-inline.cc (Octave_map octave_fcn_inline::map_value + (void) const): Return a structure compatiable with matlab's class + implementation of inline functions. + + * ov-fcn-inline.h (map_value): Declare it. + 2007-05-14 Bob Weigel * DLD-FUNCTIONS/svd.cc: Doc fix. diff -r 0d69a50fc5a9 -r 5d02dfacfc9e src/load-path.cc --- a/src/load-path.cc Tue May 15 20:17:27 2007 +0000 +++ b/src/load-path.cc Wed May 16 08:49:48 2007 +0000 @@ -50,6 +50,12 @@ static std::string Vsystem_path; +std::string +octave_system_path (void) +{ + return Vsystem_path; +} + void load_path::dir_info::update (void) { diff -r 0d69a50fc5a9 -r 5d02dfacfc9e src/load-path.h --- a/src/load-path.h Tue May 15 20:17:27 2007 +0000 +++ b/src/load-path.h Wed May 16 08:49:48 2007 +0000 @@ -341,6 +341,8 @@ extern void execute_pkg_add (const std::string& dir); extern void execute_pkg_del (const std::string& dir); +extern std::string octave_system_path (void); + #endif /* diff -r 0d69a50fc5a9 -r 5d02dfacfc9e src/load-save.cc --- a/src/load-save.cc Tue May 15 20:17:27 2007 +0000 +++ b/src/load-save.cc Wed May 16 08:49:48 2007 +0000 @@ -348,7 +348,7 @@ #endif static load_save_format -get_file_format (std::istream& file) +get_file_format (std::istream& file, const std::string& filename) { load_save_format retval = LS_UNKNOWN; @@ -374,7 +374,7 @@ file.clear (); file.seekg (0, std::ios::beg); - err = read_mat5_binary_file_header (file, swap, true); + err = read_mat5_binary_file_header (file, swap, true, filename); if (! err) { @@ -415,7 +415,7 @@ if (file) { - retval = get_file_format (file); + retval = get_file_format (file, orig_fname); file.close (); #ifdef HAVE_ZLIB @@ -426,7 +426,7 @@ if (gzfile) { - retval = get_file_format (gzfile); + retval = get_file_format (gzfile, orig_fname); gzfile.close (); } } @@ -925,7 +925,7 @@ else if (format == LS_MAT5_BINARY || format == LS_MAT7_BINARY) { - if (read_mat5_binary_file_header (file, swap, false) < 0) + if (read_mat5_binary_file_header (file, swap, false, orig_fname) < 0) { if (file) file.close (); return retval; @@ -959,7 +959,7 @@ else if (format == LS_MAT5_BINARY || format == LS_MAT7_BINARY) { - if (read_mat5_binary_file_header (file, swap, false) < 0) + if (read_mat5_binary_file_header (file, swap, false, orig_fname) < 0) { if (file) file.close (); return retval; @@ -1400,6 +1400,10 @@ std::ios::openmode mode = std::ios::out; + // Matlab v7 files are always compressed + if (format == LS_MAT7_BINARY) + use_zlib = false; + if (format == LS_BINARY #ifdef HAVE_HDF5 || format == LS_HDF5 @@ -1667,6 +1671,10 @@ std::ios::openmode mode = std::ios::out; + // Matlab v7 files are always compressed + if (format == LS_MAT7_BINARY) + use_zlib = false; + if (format == LS_BINARY #ifdef HAVE_HDF5 || format == LS_HDF5 diff -r 0d69a50fc5a9 -r 5d02dfacfc9e src/ls-mat5.cc --- a/src/ls-mat5.cc Tue May 15 20:17:27 2007 +0000 +++ b/src/ls-mat5.cc Wed May 16 08:49:48 2007 +0000 @@ -48,15 +48,18 @@ #include "oct-time.h" #include "quit.h" #include "str-vec.h" +#include "file-stat.h" #include "Cell.h" #include "defun.h" #include "error.h" #include "gripes.h" #include "load-save.h" +#include "load-path.h" #include "oct-obj.h" #include "oct-map.h" #include "ov-cell.h" +#include "ov-fcn-inline.h" #include "pager.h" #include "pt-exp.h" #include "symtab.h" @@ -70,12 +73,19 @@ #include "ls-utils.h" #include "ls-mat5.h" +#include "parse.h" +#include "defaults.h" + #ifdef HAVE_ZLIB #include #endif #define PAD(l) (((l) > 0 && (l) <= 4) ? 4 : (((l)+7)/8)*8) + +// The subsystem data block +static octave_value subsys_ov; + // FIXME -- the following enum values should be the same as the // mxClassID values in mexproto.h, but it seems they have also changed // over time. What is the correct way to handle this and maintain @@ -100,7 +110,8 @@ MAT_FILE_UINT32_CLASS, // 32 bit unsigned integer MAT_FILE_INT64_CLASS, // 64 bit signed integer MAT_FILE_UINT64_CLASS, // 64 bit unsigned integer - MAT_FILE_FUNCTION_CLASS // Function handle + MAT_FILE_FUNCTION_CLASS, // Function handle + MAT_FILE_WORKSPACE_CLASS // Workspace (undocumented) }; // Read COUNT elements of data from IS in the format specified by TYPE, @@ -397,6 +408,8 @@ oct_mach_info::float_format flt_fmt = oct_mach_info::flt_fmt_unknown; int32_t type = 0; + std::string classname; + bool isclass = false; bool imag; bool logicalvar; enum arrayclasstype arrayclass; @@ -407,7 +420,7 @@ int32_t element_length; std::streampos pos; int16_t number; - number = *(int16_t *)"\x00\x01"; + number = *(reinterpret_cast("\x00\x01")); global = false; @@ -469,6 +482,7 @@ if (type != miMATRIX) { + pos = is.tellg (); error ("load: invalid element type = %d", type); goto early_read_error; } @@ -496,27 +510,35 @@ read_int (is, swap, nzmax); // max number of non-zero in sparse // dimensions array subelement - { - int32_t dim_len; + if (arrayclass != MAT_FILE_WORKSPACE_CLASS) + { + int32_t dim_len; - if (read_mat5_tag (is, swap, type, dim_len) || type != miINT32) - { - error ("load: invalid dimensions array subelement"); - goto early_read_error; - } + if (read_mat5_tag (is, swap, type, dim_len) || type != miINT32) + { + error ("load: invalid dimensions array subelement"); + goto early_read_error; + } - int ndims = dim_len / 4; - dims.resize (ndims); - for (int i = 0; i < ndims; i++) - { - int32_t n; - read_int (is, swap, n); - dims(i) = n; - } + int ndims = dim_len / 4; + dims.resize (ndims); + for (int i = 0; i < ndims; i++) + { + int32_t n; + read_int (is, swap, n); + dims(i) = n; + } - std::streampos tmp_pos = is.tellg (); - is.seekg (tmp_pos + static_cast (PAD (dim_len) - dim_len)); - } + std::streampos tmp_pos = is.tellg (); + is.seekg (tmp_pos + static_cast (PAD (dim_len) - dim_len)); + } + else + { + // Why did mathworks decide to not have dims for a workspace!!! + dims.resize(2); + dims(0) = 1; + dims(1) = 1; + } if (read_mat5_tag (is, swap, type, len) || type != miINT8) { @@ -571,10 +593,6 @@ } break; - case MAT_FILE_OBJECT_CLASS: - warning ("load: objects are not implemented"); - goto skip_ahead; - case MAT_FILE_SPARSE_CLASS: #if SIZEOF_INT != SIZEOF_OCTAVE_IDX_TYPE warning ("load: sparse objects are not implemented"); @@ -707,13 +725,310 @@ else tc = sm; } +#endif break; -#endif case MAT_FILE_FUNCTION_CLASS: - warning ("load: function handles are not implemented"); - goto skip_ahead; + { + octave_value tc2; + std::string nm + = read_mat5_binary_element (is, filename, swap, global, tc2); + + if (! is || error_state) + goto data_read_error; + + // Octave can handle both "/" and "\" as a directry seperator + // and so can ignore the seperator field of m0. I think the + // sentinel field is also save to ignore. + Octave_map m0 = tc2.map_value(); + Octave_map m1 = m0.contents("function_handle")(0).map_value(); + std::string ftype = m1.contents("type")(0).string_value(); + std::string fname = m1.contents("function")(0).string_value(); + std::string fpath = m1.contents("file")(0).string_value(); + + if (ftype == "simple" || ftype == "scopedfunction") + { + if (fpath.length() == 0) + // We have a builtin function + tc = make_fcn_handle (fname); + else + { + std::string mroot = + m0.contents("matlabroot")(0).string_value(); + + if ((fpath.length () >= mroot.length ()) && + fpath.substr(0, mroot.length()) == mroot && + OCTAVE_EXEC_PREFIX != mroot) + { + // If fpath starts with matlabroot, and matlabroot + // doesn't equal octave_config_info ("exec_prefix") + // then the function points to a version of Octave + // or Matlab other than the running version. In that + // case we replace with the same function in the + // running version of Octave? + + // First check if just replacing matlabroot is enough + std::string str = OCTAVE_EXEC_PREFIX + + fpath.substr (mroot.length ()); + file_stat fs (str); + + if (fs.exists ()) + { + symbol_record *sr = fbi_sym_tab->lookup (str, true); + + if (sr) + { + load_fcn_from_file (sr, false); + + tc = octave_value (new octave_fcn_handle + (sr->def (), fname)); + + // The next two lines are needed to force the + // definition of the function back to the one + // that is on the user path. + sr = fbi_sym_tab->lookup (fname, true); + + load_fcn_from_file (sr, false); + } + } + else + { + // Next just search for it anywhere in the + // system path + string_vector names(3); + names(0) = fname + ".oct"; + names(1) = fname + ".mex"; + names(2) = fname + ".m"; + + dir_path p (octave_system_path ()); + + str = octave_env::make_absolute + (p.find_first_of (names), octave_env::getcwd ()); + + symbol_record *sr = fbi_sym_tab->lookup (str, true); + + if (sr) + { + load_fcn_from_file (sr, false); + + tc = octave_value (new octave_fcn_handle + (sr->def (), fname)); + + // The next two lines are needed to force the + // definition of the function back to the one + // that is on the user path. + sr = fbi_sym_tab->lookup (fname, true); + + load_fcn_from_file (sr, false); + } + else + { + warning ("load: can't find the file %s", + fpath.c_str()); + goto skip_ahead; + } + } + } + else + { + symbol_record *sr = fbi_sym_tab->lookup (fpath, true); + + if (sr) + { + load_fcn_from_file (sr, false); + + tc = octave_value (new octave_fcn_handle (sr->def (), + fname)); + + sr = fbi_sym_tab->lookup (fname, true); + + load_fcn_from_file (sr, false); + } + else + { + warning ("load: can't find the file %s", + fpath.c_str()); + goto skip_ahead; + } + } + } + } + else if (ftype == "nested") + { + warning ("load: can't load nested function"); + goto skip_ahead; + } + else if (ftype == "anonymous") + { + Octave_map m2 = m1.contents("workspace")(0).map_value(); + uint32NDArray MCOS = m2.contents("MCOS")(0).uint32_array_value(); + octave_idx_type off = static_cast(double (MCOS (4))); + m2 = subsys_ov.map_value(); + m2 = m2.contents("MCOS")(0).map_value(); + tc2 = m2.contents("MCOS")(0).cell_value()(1 + off).cell_value()(1); + m2 = tc2.map_value(); + symbol_table *local_sym_tab = 0; + if (m2.length() > 0) + { + octave_value tmp; + local_sym_tab = new symbol_table (((m2.length() + 1) & ~1), + "LOCAL"); + + for (Octave_map::iterator p0 = m2.begin() ; + p0 != m2.end(); p0++) + { + std::string key = m2.key(p0); + octave_value val = m2.contents(p0)(0); + + symbol_record *sr = local_sym_tab->lookup (key, true); + + if (sr) + sr->define (val); + else + { + error ("load: failed to load anonymous function handle"); + goto skip_ahead; + } + } + } + + unwind_protect::begin_frame ("anon_mat5_load"); + unwind_protect_ptr (curr_sym_tab); + + if (local_sym_tab) + curr_sym_tab = local_sym_tab; + + int parse_status; + octave_value anon_fcn_handle = + eval_string (fname.substr (4), true, parse_status); + + if (parse_status == 0) + { + octave_fcn_handle *fh = + anon_fcn_handle.fcn_handle_value (); + if (fh) + tc = new octave_fcn_handle (fh->fcn_val(), "@"); + else + { + error ("load: failed to load anonymous function handle"); + goto skip_ahead; + } + } + else + { + error ("load: failed to load anonymous function handle"); + goto skip_ahead; + } + + unwind_protect::run_frame ("anon_mat5_load"); + + if (local_sym_tab) + delete local_sym_tab; + } + else + { + error ("load: invalid function handle type"); + goto skip_ahead; + } + } + break; + + case MAT_FILE_WORKSPACE_CLASS: + { + Octave_map m (dim_vector (1, 1)); + int n_fields = 2; + string_vector field (n_fields); + + for (int i = 0; i < n_fields; i++) + { + int32_t fn_type; + int32_t fn_len; + if (read_mat5_tag (is, swap, fn_type, fn_len) || fn_type != miINT8) + { + error ("load: invalid field name subelement"); + goto data_read_error; + } + + OCTAVE_LOCAL_BUFFER (char, elname, fn_len + 1); + + std::streampos tmp_pos = is.tellg (); + + if (fn_len) + { + if (! is.read (elname, fn_len)) + goto data_read_error; + + is.seekg (tmp_pos + + static_cast (PAD (fn_len))); + } + + elname[fn_len] = '\0'; + + field(i) = elname; + } + + std::vector elt (n_fields); + + for (octave_idx_type i = 0; i < n_fields; i++) + elt[i] = Cell (dims); + + octave_idx_type n = dims.numel (); + + // fields subelements + for (octave_idx_type j = 0; j < n; j++) + { + for (octave_idx_type i = 0; i < n_fields; i++) + { + if (field(i) == "MCOS") + { + octave_value fieldtc; + read_mat5_binary_element (is, filename, swap, global, + fieldtc); + if (! is || error_state) + goto data_read_error; + + elt[i](j) = fieldtc; + } + else + elt[i](j) = octave_value (); + } + } + + for (octave_idx_type i = 0; i < n_fields; i++) + m.assign (field (i), elt[i]); + tc = m; + } + break; + + case MAT_FILE_OBJECT_CLASS: + { + isclass = true; + + if (read_mat5_tag (is, swap, type, len) || type != miINT8) + { + error ("load: invalid class name"); + goto skip_ahead; + } + + { + OCTAVE_LOCAL_BUFFER (char, name, len+1); + + std::streampos tmp_pos = is.tellg (); + + if (len) + { + if (! is.read (name, len )) + goto data_read_error; + + is.seekg (tmp_pos + static_cast (PAD (len))); + } + + name[len] = '\0'; + classname = name; + } + } + // Fall-through case MAT_FILE_STRUCT_CLASS: { Octave_map m (dim_vector (1, 1)); @@ -784,7 +1099,24 @@ } } - tc = m; + if (isclass) + { + if (classname == "inline") + { + // inline is not an object in Octave but rather an + // overload of a function handle. Special case. + tc = + new octave_fcn_inline (m.contents("expr")(0).string_value(), + m.contents("args")(0).string_value()); + } + else + { + warning ("load: objects are not implemented"); + goto skip_ahead; + } + } + else + tc = m; } break; @@ -975,9 +1307,14 @@ } int -read_mat5_binary_file_header (std::istream& is, bool& swap, bool quiet) +read_mat5_binary_file_header (std::istream& is, bool& swap, bool quiet, + const std::string& filename) { int16_t version=0, magic=0; + uint64_t subsys_offset; + + is.seekg (116, std::ios::beg); + is.read (reinterpret_cast (&subsys_offset), 8); is.seekg (124, std::ios::beg); is.read (reinterpret_cast (&version), 2); @@ -1001,6 +1338,48 @@ warning ("load: found version %d binary MAT file, " "but only prepared for version 1", version); + if (swap) + swap_bytes<8> (&subsys_offset, 1); + + if (subsys_offset != 0x2020202020202020ULL && subsys_offset != 0ULL) + { + // Read the subsystem data block + is.seekg (subsys_offset, std::ios::beg); + + octave_value tc; + bool global; + read_mat5_binary_element (is, filename, swap, global, tc); + + if (!is || error_state) + return -1; + + if (tc.is_uint8_type ()) + { + const uint8NDArray itmp = tc.uint8_array_value(); + octave_idx_type ilen = itmp.nelem (); + + // Why should I have to initialize outbuf as just overwrite + std::string outbuf (ilen - 7, ' '); + + // FIXME -- find a way to avoid casting away const here + char *ctmp = const_cast (outbuf.c_str ()); + for (octave_idx_type j = 8; j < ilen; j++) + ctmp [j - 8] = itmp (j); + + std::istringstream fh_ws (outbuf); + + read_mat5_binary_element (fh_ws, filename, swap, global, subsys_ov); + + if (error_state) + return -1; + } + else + return -1; + + // Reposition to just after the header + is.seekg (128, std::ios::beg); + } + return 0; } @@ -1415,12 +1794,16 @@ ret += save_mat5_array_length (m.fortran_vec (), m.nelem (), save_as_floats); } - else if (tc.is_map ()) + else if (tc.is_map () || tc.is_inline_function ()) { int fieldcnt = 0; const Octave_map m = tc.map_value (); int nel = m.numel (); + if (tc.is_inline_function ()) + // length of "inline" is 6 + ret += 8 + PAD (6 > max_namelen ? max_namelen : 6); + for (Octave_map::const_iterator i = m.begin (); i != m.end (); i++) fieldcnt++; @@ -1559,6 +1942,8 @@ flags |= MAT_FILE_STRUCT_CLASS; else if (tc.is_cell ()) flags |= MAT_FILE_CELL_CLASS; + else if (tc.is_inline_function ()) + flags |= MAT_FILE_OBJECT_CLASS; else { gripe_wrong_type_arg ("save", tc, false); @@ -1746,12 +2131,28 @@ write_mat5_array (os, ::real (m_cmplx), save_as_floats); write_mat5_array (os, ::imag (m_cmplx), save_as_floats); } - else if (tc.is_map ()) + else if (tc.is_map () || tc.is_inline_function()) { + const Octave_map m = tc.map_value (); + if (tc.is_inline_function ()) + { + std::string classname = "inline"; + int namelen = classname.length (); + + if (namelen > max_namelen) + namelen = max_namelen; // only 31 or 63 char names permitted + + int paddedlength = PAD (namelen); + + write_mat5_tag (os, miINT8, namelen); + OCTAVE_LOCAL_BUFFER (char, paddedname, paddedlength); + memset (paddedname, 0, paddedlength); + strncpy (paddedname, classname.c_str (), namelen); + os.write (paddedname, paddedlength); + } + // an Octave structure */ // recursively write each element of the structure - const Octave_map m = tc.map_value (); - { char buf[64]; int32_t maxfieldnamelength = max_namelen + 1; diff -r 0d69a50fc5a9 -r 5d02dfacfc9e src/ls-mat5.h --- a/src/ls-mat5.h Tue May 15 20:17:27 2007 +0000 +++ b/src/ls-mat5.h Wed May 16 08:49:48 2007 +0000 @@ -48,7 +48,8 @@ extern int read_mat5_binary_file_header (std::istream& is, bool& swap, - bool quiet = false); + bool quiet = false, + const std::string& filename = std::string()); extern std::string read_mat5_binary_element (std::istream& is, const std::string& filename, bool swap, bool& global, octave_value& tc); diff -r 0d69a50fc5a9 -r 5d02dfacfc9e src/ov-fcn-handle.cc --- a/src/ov-fcn-handle.cc Tue May 15 20:17:27 2007 +0000 +++ b/src/ov-fcn-handle.cc Wed May 16 08:49:48 2007 +0000 @@ -46,9 +46,15 @@ #include "pt-assign.h" #include "variables.h" #include "parse.h" +#include "unwind-prot.h" +#include "defaults.h" +#include "file-stat.h" +#include "load-path.h" +#include "oct-env.h" #include "byte-swap.h" #include "ls-oct-ascii.h" +#include "ls-oct-binary.h" #include "ls-hdf5.h" #include "ls-utils.h" @@ -144,14 +150,159 @@ } bool -octave_fcn_handle::save_ascii (std::ostream& os, bool&) +octave_fcn_handle::set_fcn (const std::string &octaveroot, + const std::string& fpath) { - os << nm << "\n"; + bool success = true; + + if (octaveroot.length () != 0 && + fpath.length () >= octaveroot.length () && + fpath.substr (0, octaveroot.length ()) == octaveroot && + OCTAVE_EXEC_PREFIX != octaveroot) + { + // First check if just replacing matlabroot is enough + std::string str = OCTAVE_EXEC_PREFIX + + fpath.substr (octaveroot.length ()); + file_stat fs (str); + + if (fs.exists ()) + { + symbol_record *sr = fbi_sym_tab->lookup (str, true); + + if (sr) + { + load_fcn_from_file (sr, false); + + fcn = octave_value (new octave_fcn_handle (sr->def (), nm)); + + // The next two lines are needed to force the + // definition of the function back to the one + // that is on the user path. + sr = fbi_sym_tab->lookup (nm, true); + + load_fcn_from_file (sr, false); + + } + else + { + error ("function handle points to non-existent function"); + success = false; + } + } + else + { + // Next just search for it anywhere in the system path + string_vector names(3); + names(0) = nm + ".oct"; + names(1) = nm + ".mex"; + names(2) = nm + ".m"; + + dir_path p (octave_system_path ()); + + str = octave_env::make_absolute + (p.find_first_of (names), octave_env::getcwd ()); + + symbol_record *sr = fbi_sym_tab->lookup (str, true); + + if (sr) + { + load_fcn_from_file (sr, false); + fcn = octave_value (new octave_fcn_handle (sr->def (), nm)); + + // The next two lines are needed to force the + // definition of the function back to the one + // that is on the user path. + sr = fbi_sym_tab->lookup (nm, true); + + load_fcn_from_file (sr, false); + } + else + { + error ("function handle points to non-existent function"); + success = false; + } + } + } + else + { + if (fpath.length () > 0) + { + symbol_record *sr = fbi_sym_tab->lookup (fpath, true); + + if (sr) + { + load_fcn_from_file (sr, false); + + fcn = octave_value (new octave_fcn_handle (sr->def (), nm)); + + sr = fbi_sym_tab->lookup (nm, true); + + load_fcn_from_file (sr, false); + } + else + { + error ("function handle points to non-existent function"); + success = false; + } + } + else + { + fcn = lookup_function (nm); + if (! fcn.is_function ()) + { + error ("function handle points to non-existent function"); + success = false; + } + } + } + + return success; +} + +bool +octave_fcn_handle::save_ascii (std::ostream& os, bool& infnan_warned) +{ if (nm == "@") { + os << nm << "\n"; + print_raw (os, true); os << "\n"; + + if (fcn.is_undefined()) + return false; + + octave_user_function *f = fcn.user_function_value (); + + Array vars = f->sym_tab()->symbol_list(); + octave_idx_type varlen = vars.length(); + + // Exclude undefined values like __retval__ + for (octave_idx_type i = 0; i < vars.length(); i++) + { + if (! vars(i)->is_defined ()) + varlen--; + } + + if (varlen > 0) + { + os << "# length: " << varlen << "\n"; + + for (octave_idx_type i = 0; i < vars.length(); i++) + { + if (vars(i)->is_defined () && + ! save_ascii_data (os, vars(i)->def(), vars(i)->name(), + infnan_warned, false, 0)) + return os; + } + } + } + else + { + os << "# octaveroot: " << OCTAVE_EXEC_PREFIX << "\n"; + os << "# path: " << user_function_value ()-> fcn_file_name () << "\n"; + os << nm << "\n"; } return true; @@ -160,10 +311,28 @@ bool octave_fcn_handle::load_ascii (std::istream& is) { + bool success = true; + + std::streampos pos = is.tellg (); + std::string octaveroot = extract_keyword (is, "octaveroot", true); + if (octaveroot.length() == 0) + { + is.seekg (pos); + is.clear (); + } + pos = is.tellg (); + std::string fpath = extract_keyword (is, "path", true); + if (fpath.length() == 0) + { + is.seekg (pos); + is.clear (); + } + is >> nm; if (nm == "@") { + octave_idx_type len = 0; char c; std::ostringstream buf; @@ -187,56 +356,186 @@ } } - int parse_status; - octave_value anon_fcn_handle = eval_string (buf.str (), true, - parse_status); + pos = is.tellg (); + symbol_table *local_sym_tab = 0; - if (parse_status == 0) + if (extract_keyword (is, "length", len, true) && len >= 0) { - octave_fcn_handle *fh = anon_fcn_handle.fcn_handle_value (); - if (fh) - fcn = fh->fcn; - else - return false; + if (len > 0) + { + octave_idx_type nlen = len; + if (nlen % 2) + nlen++; + + local_sym_tab = new symbol_table (((nlen + 1) & ~1) , "LOCAL"); + + for (octave_idx_type i = 0; i < len; i++) + { + octave_value t2; + bool dummy; + + std::string name + = read_ascii_data (is, std::string (), dummy, t2, i); + + if (!is) + { + error ("load: failed to load anonymous function handle"); + break; + } + + symbol_record *sr = local_sym_tab->lookup (name, true); + + if (sr) + sr->define (t2); + else + { + error ("load: failed to load anonymous function handle"); + success = false; + break; + } + } + } } else - return false; + { + is.seekg (pos); + is.clear (); + } + + if (is && success) + { + unwind_protect::begin_frame ("anon_ascii_load"); + unwind_protect_ptr (curr_sym_tab); + + if (local_sym_tab) + curr_sym_tab = local_sym_tab; + + int parse_status; + octave_value anon_fcn_handle = + eval_string (buf.str (), true, parse_status); + + if (parse_status == 0) + { + octave_fcn_handle *fh = + anon_fcn_handle.fcn_handle_value (); + if (fh) + fcn = fh->fcn; + else + success = false; + } + else + success = false; + + unwind_protect::run_frame ("anon_ascii_load"); + } + else + success = false; + + if (local_sym_tab) + delete local_sym_tab; } else - { - fcn = lookup_function (nm); - if (! fcn.is_function ()) - { - error ("function handle points to non-existent function"); - return false; - } - } + success = set_fcn (octaveroot, fpath); - return true; + return success; } +/* + +%!test +%! a = 2; +%! f = @(x) a + x; +%! g = @(x) 2 * x; +%! h = @log2; +%! f2 = f; +%! g2 = g; +%! h2 = h; +%! nm = tmpnam(); +%! unwind_protect +%! save ("-text", nm, "f2", "g2", "h2"); +%! clear f2 g2 h2 +%! load (nm); +%! assert (f(2),f2(2)); +%! assert (g(2),g2(2)); +%! assert (g(3),g2(3)); +%! unlink (nm); +%! save ("-text", nm, "f2", "g2", "h2"); +%! unwind_protect_cleanup +%! unlink (nm); +%! end_unwind_protect + +*/ + bool -octave_fcn_handle::save_binary (std::ostream& os, bool&) +octave_fcn_handle::save_binary (std::ostream& os, bool& save_as_floats) { - int32_t tmp = nm.length (); - os.write (reinterpret_cast (&tmp), 4); - os.write (nm.c_str (), nm.length ()); if (nm == "@") { + std::ostringstream nmbuf; + + if (fcn.is_undefined()) + return false; + + octave_user_function *f = fcn.user_function_value (); + + Array vars = f->sym_tab()->symbol_list(); + octave_idx_type varlen = vars.length(); + + // Exclude undefined values like __retval__ + for (octave_idx_type i = 0; i < vars.length(); i++) + { + if (! vars(i)->is_defined ()) + varlen--; + } + + if (varlen > 0) + nmbuf << nm << " " << varlen; + else + nmbuf << nm; + + std::string buf_str = nmbuf.str(); + int32_t tmp = buf_str.length (); + os.write (reinterpret_cast (&tmp), 4); + os.write (buf_str.c_str (), buf_str.length ()); + std::ostringstream buf; print_raw (buf, true); std::string stmp = buf.str (); tmp = stmp.length (); os.write (reinterpret_cast (&tmp), 4); os.write (stmp.c_str (), stmp.length ()); + + if (varlen > 0) + { + for (octave_idx_type i = 0; i < vars.length(); i++) + { + if (vars(i)->is_defined () && + ! save_binary_data (os, vars(i)->def(), vars(i)->name(), + "", 0, save_as_floats)) + return os; + } + } + } + else + { + std::ostringstream nmbuf; + + nmbuf << nm << "\n" << OCTAVE_EXEC_PREFIX << "\n" + << user_function_value ()-> fcn_file_name () ; + + std::string buf_str = nmbuf.str (); + int32_t tmp = buf_str.length (); + os.write (reinterpret_cast (&tmp), 4); + os.write (buf_str.c_str (), buf_str.length ()); } return true; } bool octave_fcn_handle::load_binary (std::istream& is, bool swap, - oct_mach_info::float_format) + oct_mach_info::float_format fmt) { + bool success = true; int32_t tmp; if (! is.read (reinterpret_cast (&tmp), 4)) return false; @@ -250,8 +549,17 @@ if (! is) return false; - if (nm == "@") + if (nm.length() >= 12 && nm.substr (0, 12) == "@") { + octave_idx_type len = 0; + + if (nm.length() > 12) + { + std::istringstream nm_is (nm.substr(12)); + nm_is >> len; + nm = nm.substr(0,12); + } + if (! is.read (reinterpret_cast (&tmp), 4)) return false; if (swap) @@ -260,36 +568,126 @@ OCTAVE_LOCAL_BUFFER (char, ctmp2, tmp+1); is.read (ctmp2, tmp); - int parse_status; - octave_value anon_fcn_handle = eval_string (ctmp2, true, parse_status); - - if (parse_status == 0) + symbol_table *local_sym_tab = 0; + if (len > 0) { - octave_fcn_handle *fh = anon_fcn_handle.fcn_handle_value (); - if (fh) - fcn = fh->fcn; + octave_idx_type nlen = len; + if (nlen % 2) + nlen++; + + local_sym_tab = new symbol_table (nlen, "LOCAL"); + + for (octave_idx_type i = 0; i < len; i++) + { + octave_value t2; + bool dummy; + std::string doc; + + std::string name = + read_binary_data (is, swap, fmt, std::string (), + dummy, t2, doc); + + if (!is) + { + error ("load: failed to load anonymous function handle"); + break; + } + + symbol_record *sr = local_sym_tab->lookup (name, true); + + if (sr) + { + sr->define (t2); + sr->document (doc); + } + else + { + error ("load: failed to load anonymous function handle"); + success = false; + break; + } + } + } + + if (is && success) + { + unwind_protect::begin_frame ("anon_binary_load"); + unwind_protect_ptr (curr_sym_tab); + + if (local_sym_tab) + curr_sym_tab = local_sym_tab; + + int parse_status; + octave_value anon_fcn_handle = + eval_string (ctmp2, true, parse_status); + + if (parse_status == 0) + { + octave_fcn_handle *fh = anon_fcn_handle.fcn_handle_value (); + if (fh) + fcn = fh->fcn; + else + success = false; + } else - return false; + success = false; + + unwind_protect::run_frame ("anon_binary_load"); } - else - return false; + + if (local_sym_tab) + delete local_sym_tab; } else { - fcn = lookup_function (nm); - if (! fcn.is_function ()) + std::string octaveroot; + std::string fpath; + + if (nm.find_first_of ("\n") != NPOS) { - error ("function handle points to non-existent function"); - return false; + size_t pos1 = nm.find_first_of ("\n"); + size_t pos2 = nm.find_first_of ("\n", pos1 + 1); + octaveroot = nm.substr (pos1 + 1, pos2 - pos1 - 1); + fpath = nm.substr (pos2 + 1); + nm = nm.substr (0, pos1); } - } - return true; + + success = set_fcn (octaveroot, fpath); + } + + return success; } +/* + +%!test +%! a = 2; +%! f = @(x) a + x; +%! g = @(x) 2 * x; +%! h = @log2; +%! f2 = f; +%! g2 = g; +%! h2 = h; +%! nm = tmpnam(); +%! unwind_protect +%! save ("-binary", nm, "f2", "g2", "h2"); +%! clear f2 g2 h2 +%! load (nm); +%! assert (f(2),f2(2)); +%! assert (g(2),g2(2)); +%! assert (g(3),g2(3)); +%! unlink (nm); +%! save ("-binary", nm, "f2", "g2", "h2"); +%! unwind_protect_cleanup +%! unlink (nm); +%! end_unwind_protect + +*/ + #if defined (HAVE_HDF5) bool octave_fcn_handle::save_hdf5 (hid_t loc_id, const char *name, - bool /* save_as_floats */) + bool save_as_floats) { hid_t group_hid = -1; group_hid = H5Gcreate (loc_id, name, 0); @@ -355,6 +753,117 @@ } H5Dclose (data_hid); + + octave_user_function *f = fcn.user_function_value (); + Array vars = f->sym_tab()->symbol_list(); + octave_idx_type varlen = vars.length(); + + // Exclude undefined values like __retval__ + for (octave_idx_type i = 0; i < vars.length(); i++) + { + if (! vars(i)->is_defined ()) + varlen--; + } + + if (varlen > 0) + { + hid_t as_id = H5Screate (H5S_SCALAR); + + if (as_id >= 0) + { + hid_t a_id = H5Acreate (group_hid, "SYMBOL_TABLE", + H5T_NATIVE_IDX, as_id, H5P_DEFAULT); + + if (a_id >= 0) + { + retval = (H5Awrite (a_id, H5T_NATIVE_IDX, &varlen) >= 0); + + H5Aclose (a_id); + } + else + retval = false; + + H5Sclose (as_id); + } + else + retval = false; + + data_hid = H5Gcreate (group_hid, "symbol table", 0); + if (data_hid < 0) + { + H5Sclose (space_hid); + H5Tclose (type_hid); + H5Gclose (group_hid); + return false; + } + + for (octave_idx_type i = 0; i < vars.length(); i++) + { + if (vars(i)->is_defined () && + ! add_hdf5_data (data_hid, vars(i)->def(), vars(i)->name(), + "", false, save_as_floats)) + break; + } + H5Gclose (data_hid); + } + } + else + { + std::string octaveroot = OCTAVE_EXEC_PREFIX; + std::string fpath = user_function_value ()-> fcn_file_name (); + + H5Sclose (space_hid); + hdims[0] = 1; + hdims[1] = octaveroot.length (); + space_hid = H5Screate_simple (0 , hdims, 0); + if (space_hid < 0) + { + H5Tclose (type_hid); + H5Gclose (group_hid); + return false; + } + + H5Tclose (type_hid); + type_hid = H5Tcopy (H5T_C_S1); + H5Tset_size (type_hid, octaveroot.length () + 1); + + hid_t a_id = H5Acreate (group_hid, "OCTAVEROOT", + type_hid, space_hid, H5P_DEFAULT); + + if (a_id >= 0) + { + retval = (H5Awrite (a_id, type_hid, octaveroot.c_str ()) >= 0); + + H5Aclose (a_id); + } + else + retval = false; + + H5Sclose (space_hid); + hdims[0] = 1; + hdims[1] = fpath.length (); + space_hid = H5Screate_simple (0 , hdims, 0); + if (space_hid < 0) + { + H5Tclose (type_hid); + H5Gclose (group_hid); + return false; + } + + H5Tclose (type_hid); + type_hid = H5Tcopy (H5T_C_S1); + H5Tset_size (type_hid, fpath.length () + 1); + + a_id = H5Acreate (group_hid, "FILE", type_hid, space_hid, H5P_DEFAULT); + + if (a_id >= 0) + { + retval = (H5Awrite (a_id, type_hid, fpath.c_str ()) >= 0); + + H5Aclose (a_id); + } + else + retval = false; } H5Sclose (space_hid); @@ -366,11 +875,12 @@ bool octave_fcn_handle::load_hdf5 (hid_t loc_id, const char *name, - bool /* have_h5giterate_bug */) + bool have_h5giterate_bug) { hid_t group_hid, data_hid, space_hid, type_hid, type_class_hid, st_id; hsize_t rank; int slen; + bool success = true; group_hid = H5Gopen (loc_id, name); if (group_hid < 0 ) return false; @@ -490,34 +1000,220 @@ return false; } H5Dclose (data_hid); - H5Tclose (st_id); + + symbol_table *local_sym_tab = 0; + octave_idx_type len = 0; + + // we have to pull some shenanigans here to make sure + // HDF5 doesn't print out all sorts of error messages if we + // call H5Aopen for a non-existing attribute + + H5E_auto_t err_func; + void *err_func_data; - int parse_status; - octave_value anon_fcn_handle = eval_string (fcn_tmp, true, parse_status); + // turn off error reporting temporarily, but save the error + // reporting function: + H5Eget_auto (&err_func, &err_func_data); + H5Eset_auto (0, 0); + + hid_t attr_id = H5Aopen_name (group_hid, "SYMBOL_TABLE"); - if (parse_status == 0) + if (attr_id >= 0) + { + if (H5Aread (attr_id, H5T_NATIVE_IDX, &len) < 0) + success = false; + + H5Aclose (attr_id); + } + + // restore error reporting: + H5Eset_auto (err_func, err_func_data); + + if (len > 0 && success) { - octave_fcn_handle *fh = anon_fcn_handle.fcn_handle_value (); - if (fh) - fcn = fh->fcn; + octave_idx_type nlen = len; + if (nlen % 2) + nlen++; + + local_sym_tab = new symbol_table (nlen, "LOCAL"); + +#ifdef HAVE_H5GGET_NUM_OBJS + hsize_t num_obj = 0; + data_hid = H5Gopen (group_hid, "symbol table"); + H5Gget_num_objs (data_hid, &num_obj); + H5Gclose (data_hid); + + if (num_obj != static_cast(len)) + { + error ("load: failed to load anonymous function handle"); + success = false; + } +#endif + + if (! error_state) + { + hdf5_callback_data dsub; + int current_item = 0; + for (octave_idx_type i = 0; i < len; i++) + { + if (H5Giterate (group_hid, "symbol table", ¤t_item, + hdf5_read_next_data, &dsub) <= 0) + { + error ("load: failed to load anonymous function handle"); + success = false; + break; + } + + if (have_h5giterate_bug) + current_item++; // H5Giterate returns last index processed + + symbol_record *sr = local_sym_tab->lookup (dsub.name, true); + + if (sr) + sr->define (dsub.tc); + else + { + error ("load: failed to load anonymous function handle"); + success = false; + break; + } + } + } + } + + H5Tclose (st_id); + H5Gclose (group_hid); + + if (success) + { + unwind_protect::begin_frame ("anon_hdf5_load"); + unwind_protect_ptr (curr_sym_tab); + + if (local_sym_tab) + curr_sym_tab = local_sym_tab; + + int parse_status; + octave_value anon_fcn_handle = + eval_string (fcn_tmp, true, parse_status); + + if (parse_status == 0) + { + octave_fcn_handle *fh = anon_fcn_handle.fcn_handle_value (); + if (fh) + fcn = fh->fcn; + else + success = false; + } else - return false; + success = false; + + unwind_protect::run_frame ("anon_hdf5_load"); } - else - return false; + + if (local_sym_tab) + delete local_sym_tab; } else { - fcn = lookup_function (nm); - if (! fcn.is_function ()) + std::string octaveroot; + std::string fpath; + + // we have to pull some shenanigans here to make sure + // HDF5 doesn't print out all sorts of error messages if we + // call H5Aopen for a non-existing attribute + + H5E_auto_t err_func; + void *err_func_data; + + // turn off error reporting temporarily, but save the error + // reporting function: + H5Eget_auto (&err_func, &err_func_data); + H5Eset_auto (0, 0); + + hid_t attr_id = H5Aopen_name (group_hid, "OCTAVEROOT"); + if (attr_id >= 0) { - error ("function handle points to non-existent function"); - return false; + type_hid = H5Aget_type (attr_id); + type_class_hid = H5Tget_class (type_hid); + + if (type_class_hid != H5T_STRING) + success = false; + else + { + slen = H5Tget_size (type_hid); + st_id = H5Tcopy (H5T_C_S1); + H5Tset_size (st_id, slen); + OCTAVE_LOCAL_BUFFER (char, root_tmp, slen); + + if (H5Aread (attr_id, st_id, root_tmp) < 0) + success = false; + else + octaveroot = root_tmp; + } + + H5Aclose (attr_id); } + + attr_id = H5Aopen_name (group_hid, "FILE"); + if (attr_id >= 0) + { + type_hid = H5Aget_type (attr_id); + type_class_hid = H5Tget_class (type_hid); + + if (type_class_hid != H5T_STRING) + success = false; + else + { + slen = H5Tget_size (type_hid); + st_id = H5Tcopy (H5T_C_S1); + H5Tset_size (st_id, slen); + OCTAVE_LOCAL_BUFFER (char, path_tmp, slen); + + if (H5Aread (attr_id, st_id, path_tmp) < 0) + success = false; + else + fpath = path_tmp; + } + + H5Aclose (attr_id); + } + + // restore error reporting: + H5Eset_auto (err_func, err_func_data); + + success = (success ? set_fcn (octaveroot, fpath) : success); } - return true; + return success; } + +/* + +%!test +%! if (!isempty(findstr(octave_config_info ("DEFS"),"HAVE_HDF5"))) +%! a = 2; +%! f = @(x) a + x; +%! g = @(x) 2 * x; +%! h = @log2; +%! f2 = f; +%! g2 = g; +%! h2 = h; +%! nm = tmpnam(); +%! unwind_protect +%! save ("-hdf5", nm, "f2", "g2", "h2"); +%! clear f2 g2 h2 +%! load (nm); +%! assert (f(2),f2(2)); +%! assert (g(2),g2(2)); +%! assert (g(3),g2(3)); +%! unlink (nm); +%! save ("-hdf5", nm, "f2", "g2", "h2"); +%! unwind_protect_cleanup +%! unlink (nm); +%! end_unwind_protect +%! endif + +*/ #endif void @@ -657,23 +1353,70 @@ std::string fh_nm = fh->fcn_name (); - m.assign ("function", fh_nm); + if (fh_nm == "@") + { + std::ostringstream buf; + fh->print_raw (buf); + m.assign ("function", buf.str ()); + + m.assign ("type", "anonymous"); + } + else + { + m.assign ("function", fh_nm); - if (fcn->is_nested_function ()) - m.assign ("type", "subfunction"); - else - m.assign ("type", "simple"); + if (fcn->is_nested_function ()) + { + m.assign ("type", "subfunction"); + Cell parentage (dim_vector (1, 2)); + parentage.elem(0) = fh_nm; + parentage.elem(1) = fcn->parent_fcn_name (); + m.assign ("parentage", parentage); + } + else + m.assign ("type", "simple"); + } std::string nm = fcn->fcn_file_name (); if (nm.empty ()) { if (fh_nm == "@") - m.assign ("file", "none"); + { + m.assign ("file", ""); + + octave_user_function *fu = fh->user_function_value (); + Array vars = + fu->sym_tab ()->symbol_list (); + octave_idx_type varlen = vars.length (); + + // Exclude undefined values like __retval__ + for (int i = 0; i < vars.length (); i++) + { + if (! vars (i)->is_defined ()) + varlen--; + } + + if (varlen > 0) + { + Octave_map ws; + for (octave_idx_type i = 0; i < vars.length (); i++) + { + if (vars (i)->is_defined ()) + ws.assign (vars (i)->name (), + vars (i)->def ()); + } + + m.assign ("workspace", ws); + } + } else if (fcn->is_user_function ()) - m.assign ("file", "command-line function"); + { + octave_user_function *fu = fh->user_function_value (); + m.assign ("file", fu->fcn_file_name ()); + } else - m.assign ("file", "built-in function"); + m.assign ("file", ""); } else m.assign ("file", nm); diff -r 0d69a50fc5a9 -r 5d02dfacfc9e src/ov-fcn-handle.h --- a/src/ov-fcn-handle.h Tue May 15 20:17:27 2007 +0000 +++ b/src/ov-fcn-handle.h Wed May 16 08:49:48 2007 +0000 @@ -78,6 +78,9 @@ octave_function *function_value (bool = false) { return fcn.function_value (); } + octave_user_function *user_function_value (bool = false) + { return fcn.user_function_value (); } + octave_fcn_handle *fcn_handle_value (bool = false) { return this; } octave_value fcn_val (void) const { return fcn; } @@ -105,6 +108,8 @@ private: + bool set_fcn (const std::string &octaveroot, const std::string& fpath); + DECLARE_OCTAVE_ALLOCATOR DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA diff -r 0d69a50fc5a9 -r 5d02dfacfc9e src/ov-fcn-inline.cc --- a/src/ov-fcn-inline.cc Tue May 15 20:17:27 2007 +0000 +++ b/src/ov-fcn-inline.cc Wed May 16 08:49:48 2007 +0000 @@ -89,6 +89,26 @@ error ("inline: unable to define function"); } +// This function is supplied to allow a Matlab style class structure +// to be returned.. +Octave_map +octave_fcn_inline::map_value (void) const +{ + Octave_map m; + string_vector args = fcn_arg_names (); + m.assign ("version", octave_value (1.0)); + m.assign ("isEmpty", octave_value (0.0)); + m.assign ("expr", octave_value (fcn_text ())); + m.assign ("numArgs", octave_value (args.length ())); + m.assign ("args", octave_value (args)); + std::ostringstream buf; + for (int i = 0; i < args.length (); i++) + buf << args(i) << " = INLINE_INPUTS_{" << i + 1 << "}; "; + m.assign ("inputExpr", octave_value (buf.str ())); + + return m; +} + bool octave_fcn_inline::save_ascii (std::ostream& os, bool&) { diff -r 0d69a50fc5a9 -r 5d02dfacfc9e src/ov-fcn-inline.h --- a/src/ov-fcn-inline.h Tue May 15 20:17:27 2007 +0000 +++ b/src/ov-fcn-inline.h Wed May 16 08:49:48 2007 +0000 @@ -65,6 +65,8 @@ octave_value convert_to_str_internal (bool, bool, char) const; + Octave_map map_value (void) const; + bool save_ascii (std::ostream& os, bool& infnan_warned); bool load_ascii (std::istream& is);