]> git.imager.perl.org - imager.git/commitdiff
remove buffering from the callback IO object implementation
authorTony Cook <tony@develop-help.com>
Sat, 10 Sep 2011 04:00:10 +0000 (14:00 +1000)
committerTony Cook <tony@develop-help.com>
Mon, 12 Sep 2011 12:38:25 +0000 (22:38 +1000)
   - the callback IO object did its own buffering, controlled by the
     maxbuffer parameter supplied to the read() and write() methods.
     This buffering has been removed, to avoid redundancy with the
     common io_glue buffering.

   - the callback IO object new tests the result of calling the close
     callback, which should return true for success.

Changes
Imager.xs
iolayer.c
t/t07iolayer.t

diff --git a/Changes b/Changes
index 68f62ba30bd16fab34cf53fe4b347746f7b86420..1cfe41c1620d64dfe5fd8b9f78967743897dba75 100644 (file)
--- a/Changes
+++ b/Changes
@@ -15,6 +15,14 @@ Imager release history.  Older releases can be found in Changes.old
    - TIFF now uses wrapper functions of the correct types to avoid casts
      https://rt.cpan.org/Ticket/Display.html?id=69912
 
+   - the callback IO object did its own buffering, controlled by the
+     maxbuffer parameter supplied to the read() and write() methods.
+     This buffering has been removed, to avoid redundancy with the
+     common io_glue buffering.
+
+   - the callback IO object new tests the result of calling the close
+     callback, which should return true for success.
+
 To do:
 
  - make sure i_io_close() is called and the result is checked
@@ -25,6 +33,14 @@ To do:
 
  - coverage/functional tests for the buffering code
 
+ - document the new Imager::IO methods
+
+ - document the io_glue C callbacks properly
+
+ - document the Imager::io_new_cb() callback functions properly.
+
+ - fix buffered seeks
+
 Imager 0.86
 ===========
 
index 1b63817763275242d2ab682d3f6222059f7237f1..1a8672c88204513e75de7cae7a1c88536b986abe 100644 (file)
--- a/Imager.xs
+++ b/Imager.xs
@@ -143,73 +143,11 @@ struct cbdata {
   SV *readcb;
   SV *seekcb;
   SV *closecb;
-
-  /* we need to remember whether the buffer contains write data or 
-     read data
-   */
-  int reading;
-  int writing;
-
-  /* how far we've read into the buffer (not used for writing) */
-  int where;
-
-  /* the amount of space used/data available in the buffer */
-  int used;
-
-  /* the maximum amount to fill the buffer before flushing
-     If any write is larger than this then the buffer is flushed and 
-     the full write is performed.  The write is _not_ split into 
-     maxwrite sized calls
-   */
-  int maxlength;
-
-  char buffer[CBDATA_BUFSIZE];
 };
 
-/* 
-
-call_writer(cbd, buf, size)
-
-Low-level function to call the perl writer callback.
-
-*/
-
-static ssize_t call_writer(struct cbdata *cbd, void const *buf, size_t size) {
-  dTHX;
-  int count;
-  int success;
-  SV *sv;
-  dSP;
-
-  if (!SvOK(cbd->writecb))
-    return -1;
-
-  ENTER;
-  SAVETMPS;
-  EXTEND(SP, 1);
-  PUSHMARK(SP);
-  PUSHs(sv_2mortal(newSVpv((char *)buf, size)));
-  PUTBACK;
-
-  count = perl_call_sv(cbd->writecb, G_SCALAR);
-
-  SPAGAIN;
-  if (count != 1)
-    croak("Result of perl_call_sv(..., G_SCALAR) != 1");
-
-  sv = POPs;
-  success = SvTRUE(sv);
-
-
-  PUTBACK;
-  FREETMPS;
-  LEAVE;
-
-  return success ? size : -1;
-}
-
-static ssize_t call_reader(struct cbdata *cbd, void *buf, size_t size, 
-                           size_t maxread) {
+static ssize_t
+call_reader(struct cbdata *cbd, void *buf, size_t size, 
+            size_t maxread) {
   dTHX;
   int count;
   int result;
@@ -257,21 +195,8 @@ static ssize_t call_reader(struct cbdata *cbd, void *buf, size_t size,
   return result;
 }
 
-static ssize_t write_flush(struct cbdata *cbd) {
-  dTHX;
-  ssize_t result;
-
-  if (cbd->used) {
-    result = call_writer(cbd, cbd->buffer, cbd->used);
-    cbd->used = 0;
-    return result;
-  }
-  else {
-    return 1; /* success of some sort */
-  }
-}
-
-static off_t io_seeker(void *p, off_t offset, int whence) {
+static off_t
+io_seeker(void *p, off_t offset, int whence) {
   dTHX;
   struct cbdata *cbd = p;
   int count;
@@ -281,17 +206,6 @@ static off_t io_seeker(void *p, off_t offset, int whence) {
   if (!SvOK(cbd->seekcb))
     return -1;
 
-  if (cbd->writing) {
-    if (cbd->used && write_flush(cbd) <= 0)
-      return -1;
-    cbd->writing = 0;
-  }
-  if (whence == SEEK_CUR && cbd->reading && cbd->where != cbd->used) {
-    offset -= cbd->where - cbd->used;
-  }
-  cbd->reading = 0;
-  cbd->where = cbd->used = 0;
-  
   ENTER;
   SAVETMPS;
   EXTEND(SP, 2);
@@ -316,95 +230,57 @@ static off_t io_seeker(void *p, off_t offset, int whence) {
   return result;
 }
 
-static ssize_t io_writer(void *p, void const *data, size_t size) {
+static ssize_t
+io_writer(void *p, void const *data, size_t size) {
   dTHX;
   struct cbdata *cbd = p;
+  I32 count;
+  SV *sv;
+  dSP;
+  bool success;
 
-  /* printf("io_writer(%p, %p, %u)\n", p, data, size); */
-  if (!cbd->writing) {
-    if (cbd->reading && cbd->where < cbd->used) {
-      /* we read past the place where the caller expected us to be
-         so adjust our position a bit */
-      if (io_seeker(p, cbd->where - cbd->used, SEEK_CUR) < 0) {
-        return -1;
-      }
-      cbd->reading = 0;
-    }
-    cbd->where = cbd->used = 0;
-  }
-  cbd->writing = 1;
-  if (cbd->used && cbd->used + size > cbd->maxlength) {
-    int write_res = write_flush(cbd);
-    if (write_res <= 0) {
-      return write_res;
-    }
-    cbd->used = 0;
-  }
-  if (cbd->used+size <= cbd->maxlength) {
-    memcpy(cbd->buffer + cbd->used, data, size);
-    cbd->used += size;
-    return size;
-  }
-  /* it doesn't fit - just pass it up */
-  return call_writer(cbd, data, size);
+  if (!SvOK(cbd->writecb))
+    return -1;
+
+  ENTER;
+  SAVETMPS;
+  EXTEND(SP, 1);
+  PUSHMARK(SP);
+  PUSHs(sv_2mortal(newSVpv((char *)data, size)));
+  PUTBACK;
+
+  count = perl_call_sv(cbd->writecb, G_SCALAR);
+
+  SPAGAIN;
+  if (count != 1)
+    croak("Result of perl_call_sv(..., G_SCALAR) != 1");
+
+  sv = POPs;
+  success = SvTRUE(sv);
+
+
+  PUTBACK;
+  FREETMPS;
+  LEAVE;
+
+  return success ? size : -1;
 }
 
 static ssize_t 
 io_reader(void *p, void *data, size_t size) {
   dTHX;
   struct cbdata *cbd = p;
-  ssize_t total;
+  ssize_t total = 0;
   char *out = data; /* so we can do pointer arithmetic */
 
-  /* printf("io_reader(%p, %p, %d)\n", p, data, size); */
-  if (cbd->writing) {
-    if (write_flush(cbd) <= 0)
-      return 0;
-    cbd->writing = 0;
-  }
-
-  cbd->reading = 1;
-  if (size <= cbd->used - cbd->where) {
-    /* simplest case */
-    memcpy(data, cbd->buffer+cbd->where, size);
-    cbd->where += size;
-    return size;
-  }
-  total = 0;
-  memcpy(out, cbd->buffer + cbd->where, cbd->used - cbd->where);
-  total += cbd->used - cbd->where;
-  size  -= cbd->used - cbd->where;
-  out   += cbd->used - cbd->where;
-  if (size < sizeof(cbd->buffer)) {
-    int did_read = 0;
-    int copy_size;
-    while (size
-          && (did_read = call_reader(cbd, cbd->buffer, size, 
-                                   sizeof(cbd->buffer))) > 0) {
-      cbd->where = 0;
-      cbd->used  = did_read;
-
-      copy_size = i_min(size, cbd->used);
-      memcpy(out, cbd->buffer, copy_size);
-      cbd->where += copy_size;
-      out   += copy_size;
-      total += copy_size;
-      size  -= copy_size;
-    }
-    if (did_read < 0)
-      return -1;
-  }
-  else {
-    /* just read the rest - too big for our buffer*/
-    int did_read;
-    while (size > 0 && (did_read = call_reader(cbd, out, size, size)) > 0) {
-      size  -= did_read;
-      total += did_read;
-      out   += did_read;
-    }
-    if (did_read < 0)
-      return -1;
+  int did_read;
+  while (size > 0 && (did_read = call_reader(cbd, out, size, size)) > 0) {
+    size  -= did_read;
+    total += did_read;
+    out   += did_read;
   }
+  if (total == 0 && did_read < 0)
+    return -1;
 
   return total;
 }
@@ -412,30 +288,31 @@ io_reader(void *p, void *data, size_t size) {
 static int io_closer(void *p) {
   dTHX;
   struct cbdata *cbd = p;
-
-  if (cbd->writing && cbd->used > 0) {
-    if (write_flush(cbd) < 0)
-      return -1;
-    cbd->writing = 0;
-  }
+  int success = 1;
 
   if (SvOK(cbd->closecb)) {
     dSP;
+    I32 count;
+    SV *sv;
 
     ENTER;
     SAVETMPS;
     PUSHMARK(SP);
     PUTBACK;
 
-    perl_call_sv(cbd->closecb, G_VOID);
+    count = perl_call_sv(cbd->closecb, G_SCALAR);
 
     SPAGAIN;
+    
+    sv = POPs;
+    success = SvTRUE(sv);
+
     PUTBACK;
     FREETMPS;
     LEAVE;
   }
 
-  return 0;
+  return success ? 0 : -1;
 }
 
 static void io_destroyer(void *p) {
@@ -1079,10 +956,6 @@ io_new_cb(writecb, readcb, seekcb, closecb, maxwrite = CBDATA_BUFSIZE)
         cbd->seekcb = seekcb;
         SvREFCNT_inc(closecb);
         cbd->closecb = closecb;
-        cbd->reading = cbd->writing = cbd->where = cbd->used = 0;
-        if (maxwrite > CBDATA_BUFSIZE)
-          maxwrite = CBDATA_BUFSIZE;
-        cbd->maxlength = maxwrite;
         RETVAL = io_new_cb(cbd, io_reader, io_writer, io_seeker, io_closer, 
                            io_destroyer);
       OUTPUT:
index 9e360dc0b1e5a61c0efe707b2a09fa9de9905529..12c4419f7ffe7f74109b68b7061eb74b24620ef3 100644 (file)
--- a/iolayer.c
+++ b/iolayer.c
@@ -203,8 +203,8 @@ realseek_read(io_glue *igo, void *buf, size_t count) {
   size_t        bc = 0;
   char       *cbuf = buf;
 
-  IOL_DEB( fprintf(IOL_DEBs, "realseek_read:  buf = %p, count = %d\n", 
-                 buf, count) );
+  IOL_DEB( fprintf(IOL_DEBs, "realseek_read:  buf = %p, count = %u\n", 
+                  buf, (unsigned)count) );
   /* Is this a good idea? Would it be better to handle differently?
      skip handling? */
   while( count!=bc && (rc = ig->readcb(p,cbuf+bc,count-bc))>0 ) {
@@ -212,7 +212,7 @@ realseek_read(io_glue *igo, void *buf, size_t count) {
   }
   
   ier->cpos += bc;
-  IOL_DEB( fprintf(IOL_DEBs, "realseek_read: rc = %d, bc = %d\n", rc, bc) );
+  IOL_DEB( fprintf(IOL_DEBs, "realseek_read: rc = %d, bc = %u\n", (int)rc, (unsigned)bc) );
   return rc < 0 ? rc : bc;
 }
 
@@ -240,7 +240,7 @@ realseek_write(io_glue *igo, const void *buf, size_t count) {
   char       *cbuf = (char*)buf; 
   
   IOL_DEB( fprintf(IOL_DEBs, "realseek_write: ig = %p, ier->cpos = %ld, buf = %p, "
-                  "count = %d\n", ig, (long) ier->cpos, buf, count) );
+                  "count = %u\n", ig, (long) ier->cpos, buf, (unsigned)count) );
 
   /* Is this a good idea? Would it be better to handle differently? 
      skip handling? */
@@ -249,7 +249,7 @@ realseek_write(io_glue *igo, const void *buf, size_t count) {
   }
 
   ier->cpos += bc;
-  IOL_DEB( fprintf(IOL_DEBs, "realseek_write: rc = %d, bc = %d\n", rc, bc) );
+  IOL_DEB( fprintf(IOL_DEBs, "realseek_write: rc = %d, bc = %u\n", (int)rc, (unsigned)bc) );
   return rc < 0 ? rc : bc;
 }
 
@@ -339,7 +339,7 @@ buffer_read(io_glue *igo, void *buf, size_t count) {
   io_buffer *ig = (io_buffer *)igo;
   io_ex_buffer *ieb = igo->exdata;
 
-  IOL_DEB( fprintf(IOL_DEBs, "buffer_read: ieb->cpos = %ld, buf = %p, count = %d\n", (long) ieb->cpos, buf, count) );
+  IOL_DEB( fprintf(IOL_DEBs, "buffer_read: ieb->cpos = %ld, buf = %p, count = %u\n", (long) ieb->cpos, buf, (unsigned)count) );
 
   if ( ieb->cpos+count > ig->len ) {
     mm_log((1,"buffer_read: short read: cpos=%ld, len=%ld, count=%ld\n", (long)ieb->cpos, (long)ig->len, (long)count));
@@ -1500,7 +1500,6 @@ i_io_flush(io_glue *ig) {
 
 int
 i_io_close(io_glue *ig) {
-  int flush_res = 0;
   int result = 0;
 
   IOL_DEB(fprintf(IOL_DEBs, "i_io_close(%p)\n", ig));
@@ -1607,10 +1606,10 @@ i_io_dump(io_glue *ig, int flags) {
   fprintf(IOL_DEBs, "  exdata: %p\n", ig->exdata);
   if (flags & I_IO_DUMP_CALLBACKS) {
     fprintf(IOL_DEBs, "  readcb: %p\n", ig->readcb);
-    fprintf(IOL_DEBs, "  writecb: %d\n", ig->writecb);
-    fprintf(IOL_DEBs, "  seekcb: %d\n", ig->seekcb);
-    fprintf(IOL_DEBs, "  closecb: %d\n", ig->closecb);
-    fprintf(IOL_DEBs, "  sizecb: %d\n", ig->sizecb);
+    fprintf(IOL_DEBs, "  writecb: %p\n", ig->writecb);
+    fprintf(IOL_DEBs, "  seekcb: %p\n", ig->seekcb);
+    fprintf(IOL_DEBs, "  closecb: %p\n", ig->closecb);
+    fprintf(IOL_DEBs, "  sizecb: %p\n", ig->sizecb);
   }
   if (flags & I_IO_DUMP_BUFFER) {
     fprintf(IOL_DEBs, "  buffer: %p\n", ig->buffer);
index da792d68b239b0b889b15645742cad43b16f0df9..2aa0d6d203d187c7ca88fbf506801876ca2f5db4 100644 (file)
@@ -1,6 +1,6 @@
 #!perl -w
 use strict;
-use Test::More tests => 74;
+use Test::More tests => 76;
 # for SEEK_SET etc, Fcntl doesn't provide these in 5.005_03
 use IO::Seekable;
 
@@ -273,6 +273,20 @@ SKIP:
   is($io->getc, ord "P", "check we got back to the start");
 }
 
+{ # test closecb result is propagated
+  my $success_cb = sub { 1 };
+  my $failure_cb = sub { 0 };
+
+  {
+    my $io = Imager::io_new_cb(undef, $success_cb, undef, $success_cb);
+    is($io->close(), 0, "test successful close");
+  }
+  {
+    my $io = Imager::io_new_cb(undef, $success_cb, undef, $failure_cb);
+    is($io->close(), -1, "test failed close");
+  }
+}
+
 Imager->close_log;
 
 unless ($ENV{IMAGER_KEEP_FILES}) {