improve i_io_peekn() / i_io_read_fill() interaction
authorTony Cook <tony@develop-help.com>
Sat, 17 Sep 2011 03:16:00 +0000 (13:16 +1000)
committerTony Cook <tony@develop-help.com>
Sat, 8 Oct 2011 03:39:46 +0000 (14:39 +1100)
a peekn() for a small amount no longer limits a following peekn() for
a larger amount

it also handles partly consumed input buffers

iolayer.c
t/t07iolayer.t

index 7d984a0a2efce0589f9c95e0b84c8475609af1fb..442b73a4545d1599e84db255d8fc83e3e256d8ab 100644 (file)
--- a/iolayer.c
+++ b/iolayer.c
@@ -1185,12 +1185,32 @@ i_io_read_fill(io_glue *ig, ssize_t needed) {
   ssize_t rc;
   int good = 0;
 
+  /* this condition may be unused, callers should also be checking it */
   if (ig->error || ig->buf_eof)
     return 0;
 
   if (needed > ig->buf_size)
     needed = ig->buf_size;
 
+  if (ig->read_ptr && ig->read_ptr < ig->read_end) {
+    size_t kept = ig->read_end - ig->read_ptr;
+
+    if (needed < kept) {
+      IOL_DEB(fprintf(IOL_DEBs, "i_io_read_fill(%u) -> 1 (already have enough)\n", (unsigned)needed));
+      return 1;
+    }
+
+    if (ig->read_ptr != ig->buffer)
+      memmove(ig->buffer, ig->read_ptr, kept);
+
+    good = 1; /* we have *something* available to read */
+    work = buf_start + kept;
+    needed -= kept;
+  }
+  else {
+    work = ig->buffer;
+  }
+
   while (work < buf_end && (rc = i_io_raw_read(ig, work, buf_end - work)) > 0) {
     work += rc;
     good = 1;
@@ -1305,7 +1325,7 @@ i_io_peekn(io_glue *ig, void *buf, size_t size) {
   if (!ig->buffer)
     i_io_setup_buffer(ig);
 
-  if (!ig->read_ptr || ig->read_ptr == ig->read_end) {
+  if (!ig->read_ptr || size > ig->read_end - ig->read_ptr) {
     if (ig->error) {
       IOL_DEB(fprintf(IOL_DEBs, "i_io_peekn() => -1 (error set)\n"));
       return -1;
index 917ca96cff22637f9807ca61728f61644d8bfac7..80032d61c535d172070627d0665ef9e281ab9992 100644 (file)
@@ -1,6 +1,6 @@
 #!perl -w
 use strict;
-use Test::More tests => 110;
+use Test::More tests => 115;
 # for SEEK_SET etc, Fcntl doesn't provide these in 5.005_03
 use IO::Seekable;
 
@@ -451,7 +451,7 @@ SKIP:
 
     # peek a bit
     undef $buf;
-    is($io->peekn(5120), substr($base, 4096, 4096),
+    is($io->peekn(5120), substr($base, 4096, 5120),
        "peekn() 5120, which should exceed the buffer, and only read the left overs");
   }
 
@@ -462,6 +462,41 @@ SKIP:
     is($io->read2(10), substr($base, 0, 10),
        "and that reading 10 gets the expected data");
   }
+
+  { # small peekn then large peekn
+    my $work = $base;
+    my $pos = 0;
+    my $ops = '';
+    my $reader = sub {
+      my ($size) = @_;
+
+      my $req_size = $size;
+      # do small reads, to trigger a possible bug
+      if ($size > 10) {
+       $size = 10;
+      }
+
+      if ($pos + $size > length $work) {
+       $size = length($work) - $pos;
+      }
+
+      my $result = substr($work, $pos, $size);
+      $pos += $size;
+      $ops .= "R$req_size>$size;";
+
+      print "# read $req_size>$size\n";
+
+      return $result;
+    };
+    my $io = Imager::io_new_cb(undef, $reader, undef, undef);
+    ok($io, "small reader io");
+    is($io->peekn(25), substr($base, 0, 25), "peek 25");
+    is($ops, "R8192>10;R8182>10;R8172>10;",
+       "check we got the raw calls expected");
+    is($io->peekn(65), substr($base, 0, 65), "peek 65");
+    is($ops, "R8192>10;R8182>10;R8172>10;R8162>10;R8152>10;R8142>10;R8132>10;",
+       "check we got the raw calls expected");
+  }
 }
 
 Imager->close_log;