changeset 18512:fdd27f68b011 stable

handle fread skip parameter correctly (bug #41648) * oct-stream.cc (octave_stream::read): Decide whether to skip based on current number of elements read, not total. Correctly handle case of reading partial blocks when there is a repeat cound. Skip to EOF if the full skip is beyond EOF. * io.tst: New test.
author Rik <rik@octave.org> and John W. Eaton <jwe@octave.org>
date Fri, 21 Feb 2014 15:28:02 -0500
parents bcc88f8f071d
children 39fbe4aba560 0bdecd41b2dd
files libinterp/corefcn/oct-stream.cc test/io.tst
diffstat 2 files changed, 55 insertions(+), 5 deletions(-) [+]
line wrap: on
line diff
--- a/libinterp/corefcn/oct-stream.cc	Thu Jan 16 16:26:56 2014 -0500
+++ b/libinterp/corefcn/oct-stream.cc	Fri Feb 21 15:28:02 2014 -0500
@@ -3260,6 +3260,14 @@
               while (is && ! is.eof ()
                      && (read_to_eof || count < elts_to_read))
                 {
+                  if (! read_to_eof)
+                    {
+                      octave_idx_type remaining_elts = elts_to_read - count;
+
+                      if (remaining_elts < input_buf_elts)
+                        input_buf_size = remaining_elts * input_elt_size;
+                    }
+
                   char *input_buf = new char [input_buf_size];
 
                   is.read (input_buf, input_buf_size);
@@ -3268,15 +3276,35 @@
 
                   char_count += gcount;
 
-                  count += gcount / input_elt_size;
+                  size_t nel = gcount / input_elt_size;
+
+                  count += nel;
 
                   input_buf_list.push_back (input_buf);
 
-                  if (is && skip != 0 && count == block_size)
+                  if (is && skip != 0 && nel == block_size)
                     {
-                      int seek_status = seek (skip, SEEK_CUR);
-
-                      if (seek_status < 0)
+                      // Seek to skip.  If skip would move past EOF,
+                      // position at EOF.
+
+                      off_t orig_pos = tell ();
+
+                      seek (0, SEEK_END);
+
+                      off_t eof_pos = tell ();
+
+                      // Is it possible for this to fail to return us to
+                      // the original position?
+                      seek (orig_pos, SEEK_SET);
+
+                      size_t remaining = eof_pos - orig_pos;
+
+                      if (remaining < skip)
+                        seek (0, SEEK_END);
+                      else
+                        seek (skip, SEEK_CUR);
+
+                      if (! is)
                         break;
                     }
                 }
--- a/test/io.tst	Thu Jan 16 16:26:56 2014 -0500
+++ b/test/io.tst	Fri Feb 21 15:28:02 2014 -0500
@@ -547,3 +547,25 @@
 %! assert (data, [25185, 26213; 25699, 0]);
 %! assert (count, 3);
 %! fclose (id);
+
+%!test
+%! id = tmpfile ();
+%! fwrite (id, char (0:15));
+%! frewind (id);
+%! [data, count] = fread (id, inf, "2*uint8", 2);
+%! assert (data, [0; 1; 4; 5; 8; 9; 12; 13]);
+%! assert (count, 8);
+%! frewind (id);
+%! [data, count] = fread (id, 3, "2*uint8", 3);
+%! assert (data, [0; 1; 5]);
+%! assert (count, 3);
+%! [data, count] = fread (id, 3, "2*uint8", 3);
+%! assert (data, [6; 7; 11]);
+%! assert (count, 3);
+%! [data, count] = fread (id, 3, "2*uint8", 3);
+%! assert (data, [12; 13]);
+%! assert (count, 2);
+%! [data, count] = fread (id, 3, "2*uint8", 3);
+%! assert (data, []);
+%! assert (count, 0);
+%! fclose (id);