changeset 30852:95725e6ad9c1 stable

restore part of the old octave_range class as octave_legacy_range This change allows old range objects to be loaded from data files and then converted to the new range type or other numeric types. * libinterp/octave-value/ov-legacy-range.h, libinterp/octave-value/ov-legacy-range.cc: New files restored from old versions of ov-range.h and ov-range.cc. Only provide enough support to load "range" objects from data files. * libinterp/octave-value/module.mk: Update. * ov.h, ov.cc: Update tests. (octave_value::is_legacy_object): New function. (octave_value::load_ascii, octave_value::load_binary, octave_value::load_hdf5): If loaded value is a legacy object, call maybe_mutate to allow conversion to a currently supported data type. (install_types): Register octave_legacy_range objects. (octave_value::make_range_rep_deprecated): Convert to Don't allow (const Range& r, bool force_range) (octave_value::make_range_rep_deprecated): Use Range constructor. Allow mutation to handle conversion to new range object or other numeric types. * ov-base.h (octave_base_value::is_legacy_object): New function * ov-range.cc: Rename ov_range<double> data type from "range" to "double_range". * Range.h (Range::Range): Always provide deprecated Range constructors. * ov-typeinfo.cc: Update test. * mk-conv-tst.sh: Update tests.
author John W. Eaton <jwe@octave.org>
date Mon, 21 Mar 2022 23:58:35 -0400
parents 82c1554c4a64
children fc3bd70cd1be
files libinterp/octave-value/module.mk libinterp/octave-value/ov-base.h libinterp/octave-value/ov-legacy-range.cc libinterp/octave-value/ov-legacy-range.h libinterp/octave-value/ov-range.cc libinterp/octave-value/ov-typeinfo.cc libinterp/octave-value/ov.cc libinterp/octave-value/ov.h liboctave/array/Range.h test/mk-conv-tst.sh
diffstat 10 files changed, 451 insertions(+), 17 deletions(-) [+]
line wrap: on
line diff
--- a/libinterp/octave-value/module.mk	Sat Mar 19 16:55:30 2022 +0100
+++ b/libinterp/octave-value/module.mk	Mon Mar 21 23:58:35 2022 -0400
@@ -53,6 +53,7 @@
   %reldir%/ov-flt-re-mat.h \
   %reldir%/ov-java.h \
   %reldir%/ov-lazy-idx.h \
+  %reldir%/ov-legacy-range.h \
   %reldir%/ov-magic-int.h \
   %reldir%/ov-mex-fcn.h \
   %reldir%/ov-null-mat.h \
@@ -119,6 +120,7 @@
   %reldir%/ov-flt-re-mat.cc \
   %reldir%/ov-java.cc \
   %reldir%/ov-lazy-idx.cc \
+  %reldir%/ov-legacy-range.cc \
   %reldir%/ov-magic-int.cc \
   %reldir%/ov-mex-fcn.cc \
   %reldir%/ov-null-mat.cc \
--- a/libinterp/octave-value/ov-base.h	Sat Mar 19 16:55:30 2022 +0100
+++ b/libinterp/octave-value/ov-base.h	Mon Mar 21 23:58:35 2022 -0400
@@ -386,6 +386,8 @@
 
   virtual bool is_storable (void) const { return true; }
 
+  virtual bool is_legacy_object (void) const { return false; }
+
   bool isempty (void) const { return (dims ().any_zero ()); }
 
   bool is_zero_by_zero (void) const { return dims().zero_by_zero (); }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libinterp/octave-value/ov-legacy-range.cc	Mon Mar 21 23:58:35 2022 -0400
@@ -0,0 +1,275 @@
+////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 1996-2022 The Octave Project Developers
+//
+// See the file COPYRIGHT.md in the top-level directory of this
+// distribution or <https://octave.org/copyright/>.
+//
+// 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 3 of the License, 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, see
+// <https://www.gnu.org/licenses/>.
+//
+////////////////////////////////////////////////////////////////////////
+
+#if defined (HAVE_CONFIG_H)
+#  include "config.h"
+#endif
+
+#include <istream>
+#include <ostream>
+#include <sstream>
+
+#include "lo-ieee.h"
+#include "lo-utils.h"
+
+#include "variables.h"
+#include "error.h"
+#include "ovl.h"
+#include "oct-hdf5.h"
+#include "ov-legacy-range.h"
+#include "ov-range.h"
+#include "ov-re-mat.h"
+#include "ov-scalar.h"
+#include "pr-output.h"
+
+#include "byte-swap.h"
+#include "ls-ascii-helper.h"
+#include "ls-hdf5.h"
+#include "ls-utils.h"
+
+#if defined (HAVE_PRAGMA_GCC_DIAGNOSTIC)
+#  pragma GCC diagnostic push
+#  pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
+DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (octave_legacy_range, "range", "double");
+
+octave_legacy_range::octave_legacy_range (void)
+  : octave_base_value (), range () { }
+
+octave_legacy_range::octave_legacy_range (const Range& r)
+  : octave_base_value (), range (r)
+{
+  if (range.numel () < 0 && range.numel () != -2)
+    error ("invalid range");
+}
+
+static octave_base_value *
+default_numeric_conversion_function (const octave_base_value& a)
+{
+  const octave_legacy_range& v = dynamic_cast<const octave_legacy_range&> (a);
+
+  return new octave_matrix (v.matrix_value ());
+}
+
+octave_base_value::type_conv_info
+octave_legacy_range::numeric_conversion_function (void) const
+{
+  return octave_base_value::type_conv_info (default_numeric_conversion_function,
+                                            octave_matrix::static_type_id ());
+}
+
+octave_base_value *
+octave_legacy_range::try_narrowing_conversion (void)
+{
+  octave_base_value *retval = nullptr;
+
+  switch (range.numel ())
+    {
+    case 1:
+      retval = new octave_scalar (range.base ());
+      break;
+
+    case 0:
+      retval = new octave_matrix (Matrix (1, 0));
+      break;
+
+    case -2:
+      retval = new octave_matrix (range.matrix_value ());
+      break;
+
+    default:
+      {
+        if (range.increment () == 0)
+          retval = new octave_matrix (range.matrix_value ());
+        else
+          retval = new octave_range
+            (octave::range<double> (range.base (), range.increment (),
+                                    range.limit (), range.numel ()));
+      }
+      break;
+    }
+
+  return retval;
+}
+
+// Skip white space and comments on stream IS.
+
+static void
+skip_comments (std::istream& is)
+{
+  char c = '\0';
+  while (is.get (c))
+    {
+      if (c == ' ' || c == '\t' || c == '\n')
+        ; // Skip whitespace on way to beginning of next line.
+      else
+        break;
+    }
+
+  octave::skip_until_newline (is, false);
+}
+
+bool
+octave_legacy_range::load_ascii (std::istream& is)
+{
+  // # base, limit, range comment added by save ().
+  skip_comments (is);
+
+  double base, limit, inc;
+  is >> base >> limit >> inc;
+
+  if (! is)
+    error ("load: failed to load range constant");
+
+  if (inc != 0)
+    range = Range (base, limit, inc);
+  else
+    range = Range (base, inc, static_cast<octave_idx_type> (limit));
+
+  return true;
+}
+
+bool
+octave_legacy_range::load_binary (std::istream& is, bool swap,
+                                  octave::mach_info::float_format /* fmt */)
+{
+  char tmp;
+  if (! is.read (reinterpret_cast<char *> (&tmp), 1))
+    return false;
+  double bas, lim, inc;
+  if (! is.read (reinterpret_cast<char *> (&bas), 8))
+    return false;
+  if (swap)
+    swap_bytes<8> (&bas);
+  if (! is.read (reinterpret_cast<char *> (&lim), 8))
+    return false;
+  if (swap)
+    swap_bytes<8> (&lim);
+  if (! is.read (reinterpret_cast<char *> (&inc), 8))
+    return false;
+  if (swap)
+    swap_bytes<8> (&inc);
+  if (inc != 0)
+    range = Range (bas, lim, inc);
+  else
+    range = Range (bas, inc, static_cast<octave_idx_type> (lim));
+
+  return true;
+}
+
+#if defined (HAVE_HDF5)
+
+// The following subroutines creates an HDF5 representation of the way
+// we will store Octave range types (triplets of floating-point numbers).
+// NUM_TYPE is the HDF5 numeric type to use for storage (e.g.
+// H5T_NATIVE_DOUBLE to save as 'double').  Note that any necessary
+// conversions are handled automatically by HDF5.
+
+static hid_t
+hdf5_make_range_type (hid_t num_type)
+{
+  hid_t type_id = H5Tcreate (H5T_COMPOUND, sizeof (double) * 3);
+
+  H5Tinsert (type_id, "base", 0 * sizeof (double), num_type);
+  H5Tinsert (type_id, "limit", 1 * sizeof (double), num_type);
+  H5Tinsert (type_id, "increment", 2 * sizeof (double), num_type);
+
+  return type_id;
+}
+
+#endif
+
+bool
+octave_legacy_range::load_hdf5 (octave_hdf5_id loc_id, const char *name)
+{
+  bool retval = false;
+
+#if defined (HAVE_HDF5)
+
+#if defined (HAVE_HDF5_18)
+  hid_t data_hid = H5Dopen (loc_id, name, octave_H5P_DEFAULT);
+#else
+  hid_t data_hid = H5Dopen (loc_id, name);
+#endif
+  hid_t type_hid = H5Dget_type (data_hid);
+
+  hid_t range_type = hdf5_make_range_type (H5T_NATIVE_DOUBLE);
+
+  if (! hdf5_types_compatible (type_hid, range_type))
+    {
+      H5Tclose (range_type);
+      H5Dclose (data_hid);
+      return false;
+    }
+
+  hid_t space_hid = H5Dget_space (data_hid);
+  hsize_t rank = H5Sget_simple_extent_ndims (space_hid);
+
+  if (rank != 0)
+    {
+      H5Tclose (range_type);
+      H5Sclose (space_hid);
+      H5Dclose (data_hid);
+      return false;
+    }
+
+  double rangevals[3];
+  if (H5Dread (data_hid, range_type, octave_H5S_ALL, octave_H5S_ALL,
+               octave_H5P_DEFAULT, rangevals)
+      >= 0)
+    {
+      retval = true;
+      octave_idx_type nel;
+      if (hdf5_get_scalar_attr (data_hid, H5T_NATIVE_IDX,
+                                "OCTAVE_RANGE_NELEM", &nel))
+        range = Range (rangevals[0], rangevals[2], nel);
+      else
+        {
+          if (rangevals[2] != 0)
+            range = Range (rangevals[0], rangevals[1], rangevals[2]);
+          else
+            range = Range (rangevals[0], rangevals[2],
+                           static_cast<octave_idx_type> (rangevals[1]));
+        }
+    }
+
+  H5Tclose (range_type);
+  H5Sclose (space_hid);
+  H5Dclose (data_hid);
+
+#else
+  octave_unused_parameter (loc_id);
+  octave_unused_parameter (name);
+
+  warn_load ("hdf5");
+#endif
+
+  return retval;
+}
+
+#if defined (HAVE_PRAGMA_GCC_DIAGNOSTIC)
+#  pragma GCC diagnostic pop
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libinterp/octave-value/ov-legacy-range.h	Mon Mar 21 23:58:35 2022 -0400
@@ -0,0 +1,110 @@
+////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 1996-2022 The Octave Project Developers
+//
+// See the file COPYRIGHT.md in the top-level directory of this
+// distribution or <https://octave.org/copyright/>.
+//
+// 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 3 of the License, 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, see
+// <https://www.gnu.org/licenses/>.
+//
+////////////////////////////////////////////////////////////////////////
+
+#if ! defined (octave_ov_legacy_range_h)
+#define octave_ov_legacy_range_h 1
+
+#include "octave-config.h"
+
+#include <cstdlib>
+
+#include <iosfwd>
+#include <string>
+
+#include "Range.h"
+
+#include "lo-mappers.h"
+#include "lo-utils.h"
+#include "mx-base.h"
+
+#include "error.h"
+#include "oct-stream.h"
+#include "ov-base.h"
+#include "ov-re-mat.h"
+#include "ov-typeinfo.h"
+
+class octave_value_list;
+
+// Legacy Range values.
+
+// Provide enough of the old octave_range class to allow Range objects
+// to be loaded from files.  After loading, they are converted to some
+// other type by a call to octave_value::maybe_mutate in
+// load_save_system::load_vars so there should no longer be any values
+// of this type used by the interpreter.  The action of maybe_mutate is
+// performed by octave_legacy_range::try_narrowing_conversion.
+
+class
+octave_legacy_range : public octave_base_value
+{
+public:
+
+  octave_legacy_range (void);
+
+  octave_legacy_range (const Range& r);
+
+  octave_legacy_range (const octave_legacy_range& r) = default;
+
+  // No assignment.
+
+  octave_legacy_range& operator = (const octave_legacy_range&) = delete;
+
+  ~octave_legacy_range (void) { }
+
+  octave_base_value * clone (void) const
+  {
+    return new octave_legacy_range (*this);
+  }
+
+  // A range is really just a special kind of real matrix object.  In
+  // the places where we need to call empty_clone, it makes more sense
+  // to create an empty matrix (0x0) instead of an empty range (1x0).
+  octave_base_value * empty_clone (void) const { return new octave_matrix (); }
+
+  type_conv_info numeric_conversion_function (void) const;
+
+  octave_base_value * try_narrowing_conversion (void);
+
+  bool is_defined (void) const { return true; }
+
+  bool is_legacy_object (void) const { return true; }
+
+  bool is_constant (void) const { return true; }
+
+  bool load_ascii (std::istream& is);
+
+  bool load_binary (std::istream& is, bool swap,
+                    octave::mach_info::float_format fmt);
+
+  bool load_hdf5 (octave_hdf5_id loc_id, const char *name);
+
+private:
+
+  Range range;
+
+  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
+};
+
+#endif
--- a/libinterp/octave-value/ov-range.cc	Sat Mar 19 16:55:30 2022 +0100
+++ b/libinterp/octave-value/ov-range.cc	Mon Mar 21 23:58:35 2022 -0400
@@ -143,7 +143,7 @@
 #endif
 
 DEFINE_TEMPLATE_OV_TYPEID_FUNCTIONS_AND_DATA (ov_range<double>,
-                                              "range", "double");
+                                              "double_range", "double");
 
 // For now, disable all but ov_range<double>.
 
--- a/libinterp/octave-value/ov-typeinfo.cc	Sat Mar 19 16:55:30 2022 +0100
+++ b/libinterp/octave-value/ov-typeinfo.cc	Mon Mar 21 23:58:35 2022 -0400
@@ -948,7 +948,7 @@
 
 %!test
 %! if (optimize_range ())
-%!   assert (typeinfo (1:2), "range")
+%!   assert (typeinfo (1:2), "double_range")
 %! else
 %!   assert (typeinfo (1:2), "matrix")
 %! endif
--- a/libinterp/octave-value/ov.cc	Sat Mar 19 16:55:30 2022 +0100
+++ b/libinterp/octave-value/ov.cc	Mon Mar 21 23:58:35 2022 -0400
@@ -48,6 +48,7 @@
 #include "ov-flt-re-mat.h"
 #include "ov-re-diag.h"
 #include "ov-flt-re-diag.h"
+#include "ov-legacy-range.h"
 #include "ov-perm.h"
 #include "ov-bool-sparse.h"
 #include "ov-cx-sparse.h"
@@ -1076,8 +1077,17 @@
 octave_base_value *
 octave_value::make_range_rep_deprecated (double base, double inc, double limit)
 {
+#if defined (HAVE_PRAGMA_GCC_DIAGNOSTIC)
+#  pragma GCC diagnostic push
+#  pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
   return dynamic_cast<octave_base_value *>
-    (new ov_range<double> (octave::range<double> (base, inc, limit)));
+    (new octave_legacy_range (Range (base, inc, limit)));
+
+#if defined (HAVE_PRAGMA_GCC_DIAGNOSTIC)
+#  pragma GCC diagnostic pop
+#endif
 }
 
 // Remove when public constructor that uses this function is removed.
@@ -1087,8 +1097,8 @@
   if (! force_range && ! r.ok ())
     error ("invalid range");
 
-  if (force_range || Voptimize_range)
-    return make_range_rep_deprecated (r.base (), r.increment (), r.limit ());
+  if ((force_range || Voptimize_range))
+    return dynamic_cast<octave_base_value *> (new octave_legacy_range (r));
   else
     return dynamic_cast<octave_base_value *> (new octave_matrix (r.matrix_value ()));
 }
@@ -2350,6 +2360,39 @@
   m_rep->print_info (os, prefix + ' ');
 }
 
+bool octave_value::load_ascii (std::istream& is)
+{
+  bool status = m_rep->load_ascii (is);
+
+  // Force conversion of legacy objects.
+  if (is_legacy_object ())
+    maybe_mutate ();
+
+  return status;
+}
+bool octave_value::load_binary (std::istream& is, bool swap,
+                                octave::mach_info::float_format fmt)
+{
+  bool status = m_rep->load_binary (is, swap, fmt);
+
+  // Force conversion of legacy objects.
+  if (is_legacy_object ())
+    maybe_mutate ();
+
+  return status;
+}
+
+bool octave_value::load_hdf5 (octave_hdf5_id loc_id, const char *name)
+{
+  bool status = m_rep->load_hdf5 (loc_id, name);
+
+  // Force conversion of legacy objects.
+  if (is_legacy_object ())
+    maybe_mutate ();
+
+  return status;
+}
+
 const void *
 octave_value::mex_get_data (mxClassID class_id, mxComplexity complexity) const
 {
@@ -3595,6 +3638,11 @@
   octave_diag_matrix::register_type (ti);
   octave_complex_matrix::register_type (ti);
   octave_complex_diag_matrix::register_type (ti);
+
+  // Legacy range type, preserved to allow loading "constant" ranges
+  // from data files.
+  octave_legacy_range::register_type (ti);
+
   ov_range<double>::register_type (ti);
 
   // For now, disable all but ov_range<double>.
@@ -4078,7 +4126,7 @@
 %!  r = base:limit;
 %!endfunction
 
-%!assert (typeinfo (__test_dr__ (true)), "range")
+%!assert (typeinfo (__test_dr__ (true)), "double_range")
 %!assert (typeinfo (__test_dr__ (false)), "matrix")
 */
 
--- a/libinterp/octave-value/ov.h	Sat Mar 19 16:55:30 2022 +0100
+++ b/libinterp/octave-value/ov.h	Mon Mar 21 23:58:35 2022 -0400
@@ -640,6 +640,9 @@
   bool is_undefined (void) const
   { return ! is_defined (); }
 
+  bool is_legacy_object (void) const
+  { return m_rep->is_legacy_object (); }
+
   bool isempty (void) const
   { return m_rep->isempty (); }
 
@@ -1483,21 +1486,19 @@
 
   bool save_ascii (std::ostream& os) { return m_rep->save_ascii (os); }
 
-  bool load_ascii (std::istream& is) { return m_rep->load_ascii (is); }
+  OCTINTERP_API bool load_ascii (std::istream& is);
 
   bool save_binary (std::ostream& os, bool save_as_floats)
   { return m_rep->save_binary (os, save_as_floats); }
 
-  bool load_binary (std::istream& is, bool swap,
-                    octave::mach_info::float_format fmt)
-  { return m_rep->load_binary (is, swap, fmt); }
+  OCTINTERP_API bool load_binary (std::istream& is, bool swap,
+                                  octave::mach_info::float_format fmt);
 
   bool save_hdf5 (octave_hdf5_id loc_id, const char *name,
                   bool save_as_floats)
   { return m_rep->save_hdf5 (loc_id, name, save_as_floats); }
 
-  bool load_hdf5 (octave_hdf5_id loc_id, const char *name)
-  { return m_rep->load_hdf5 (loc_id, name); }
+  OCTINTERP_API bool load_hdf5 (octave_hdf5_id loc_id, const char *name);
 
   OCTINTERP_API int
   write (octave::stream& os, int block_size,
--- a/liboctave/array/Range.h	Sat Mar 19 16:55:30 2022 +0100
+++ b/liboctave/array/Range.h	Mon Mar 21 23:58:35 2022 -0400
@@ -423,7 +423,6 @@
 {
 public:
 
-#if defined (OCTAVE_PROVIDE_DEPRECATED_SYMBOLS)
   OCTAVE_DEPRECATED (7, "use the 'octave::range<double>' class instead")
   Range (void)
     : m_base (0), m_limit (0), m_inc (0), m_numel (0)
@@ -439,7 +438,6 @@
     : m_base (r.base ()), m_limit (r.final_value ()), m_inc (r.increment ()),
       m_numel (r.numel ())
   { }
-#endif
 
   Range (const Range& r) = default;
 
@@ -447,7 +445,6 @@
 
   ~Range (void) = default;
 
-#if defined (OCTAVE_PROVIDE_DEPRECATED_SYMBOLS)
   OCTAVE_DEPRECATED (7, "use the 'octave::range<double>' class instead")
   Range (double b, double l)
     : m_base (b), m_limit (l), m_inc (1), m_numel (numel_internal ())
@@ -477,7 +474,6 @@
     if (! octave::math::isinf (m_limit))
       m_limit = limit_internal ();
   }
-#endif
 
   // The range has a finite number of elements.
   bool ok (void) const
--- a/test/mk-conv-tst.sh	Sat Mar 19 16:55:30 2022 +0100
+++ b/test/mk-conv-tst.sh	Mon Mar 21 23:58:35 2022 -0400
@@ -73,7 +73,7 @@
 %!
 %!test
 %! if (optimize_range ())
-%!   assert (typeinfo (r), "range")
+%!   assert (typeinfo (r), "double_range")
 %! else
 %!   assert (typeinfo (r), "matrix")
 %! endif