changeset 23323:31be59137160 stable

Don't error out if RANGE specification for dlmread contains large values. * dlmread.cc (idx_max): Redefine static const variable that holds the maximum row or column idx for a range specification to be max octave_idx_type - 1. * dlmread.cc (idx_max_dbl): New static const variable that holds the maximum possible row or column idx for a range specification as a double value. * dlmread.cc (parse_range_spec): Use new idx_max constant. Use std::min (x, idx_max_dbl) to eliminate overflow when casting double to octave_idx_type. * dlmread.cc (Fdlmread): Use new idx_max constant. After parsing range specification, return an empty matrix immediately if range is empty. When skipping lines at beginning of file, look at both the number of lines to skip and the output of getline; The EOF may be reached before the number of lines to skip.
author Rik <rik@octave.org>
date Tue, 21 Mar 2017 23:15:53 -0700
parents fc344de3afc9
children 336f2e8331fa
files libinterp/corefcn/dlmread.cc
diffstat 1 files changed, 27 insertions(+), 15 deletions(-) [+]
line wrap: on
line diff
--- a/libinterp/corefcn/dlmread.cc	Tue Mar 21 08:32:44 2017 -0400
+++ b/libinterp/corefcn/dlmread.cc	Tue Mar 21 23:15:53 2017 -0700
@@ -42,7 +42,9 @@
 #include "utils.h"
 
 static const octave_idx_type idx_max =
-  std::numeric_limits<octave_idx_type>::max ();
+  std::numeric_limits<octave_idx_type>::max () - 1;
+
+static const double idx_max_dbl = double (idx_max);
 
 static bool
 read_cell_spec (std::istream& is, octave_idx_type& row, octave_idx_type& col)
@@ -122,8 +124,8 @@
                         stat = false;
                     }
 
-                  rup = idx_max - 1;
-                  cup = idx_max - 1;
+                  rup = idx_max;
+                  cup = idx_max;
                 }
               else
                 {
@@ -143,12 +145,12 @@
     }
   else if (range_spec.is_real_matrix () && range_spec.numel () == 4)
     {
-      ColumnVector range(range_spec.vector_value ());
-      // double --> unsigned int
-      rlo = static_cast<octave_idx_type> (range(0));
-      clo = static_cast<octave_idx_type> (range(1));
-      rup = static_cast<octave_idx_type> (range(2));
-      cup = static_cast<octave_idx_type> (range(3));
+      ColumnVector range (range_spec.vector_value ());
+      // double --> unsigned int avoiding any overflow
+      rlo = static_cast<octave_idx_type> (std::min (range(0), idx_max_dbl));
+      clo = static_cast<octave_idx_type> (std::min (range(1), idx_max_dbl));
+      rup = static_cast<octave_idx_type> (std::min (range(2), idx_max_dbl));
+      cup = static_cast<octave_idx_type> (std::min (range(3), idx_max_dbl));
     }
   else
     stat = false;
@@ -249,8 +251,8 @@
   // Take a subset if a range was given.
   octave_idx_type r0 = 0;
   octave_idx_type c0 = 0;
-  octave_idx_type r1 = idx_max-1;
-  octave_idx_type c1 = idx_max-1;
+  octave_idx_type r1 = idx_max;
+  octave_idx_type c1 = idx_max;
   if (nargin > 2)
     {
       if (nargin == 3)
@@ -266,6 +268,10 @@
 
       if (r0 < 0 || c0 < 0)
         error ("dlmread: left & top must be positive");
+
+      // Short-circuit and return if range is empty 
+      if (r1 < r0 || c1 < c0)
+        return ovl (Matrix (0,0));
     }
 
   octave_idx_type i = 0;
@@ -283,10 +289,15 @@
 
   std::string line;
 
-  // Skip the r0 leading lines as these might be a header.
-  for (octave_idx_type m = 0; m < r0; m++)
-    getline (*input, line);
-  r1 -= r0;
+  // Skip the r0 leading lines
+  octave_idx_type rcnt = r0;
+  while (rcnt > 0 && getline (*input, line))
+    rcnt--;
+
+  if (rcnt > 0)
+    return ovl (Matrix (0,0));  // Not enough lines in file to satisfy RANGE
+  else
+    r1 -= r0;
 
   std::istringstream tmp_stream;
 
@@ -509,6 +520,7 @@
 %!   assert (dlmread (file, ",", "A2.."), [4 + 4i, 5, 6; 7, 8, 9; 10, 11, 12]);
 %!   assert (dlmread (file, ",", 10, 0), []);
 %!   assert (dlmread (file, ",", 0, 10), []);
+%!   assert (dlmread (file, ",", [0, 4, 0, Inf]), []);
 %! unwind_protect_cleanup
 %!   unlink (file);
 %! end_unwind_protect