7089ffeccae5b09de5fac56b1972887ac9480660
[imager.git] / iolayer.c
1 #include "io.h"
2 #include "iolayer.h"
3 #include "log.h"
4 #include <stdlib.h>
5 #include <stdio.h>
6 #ifdef _MSC_VER
7 #include <io.h>
8 #endif
9
10 #define IOL_DEB(x)
11
12
13 char *io_type_names[] = { "FDSEEK", "FDNOSEEK", "BUFFER", "CBSEEK", "CBNOSEEK", "BUFCHAIN" };
14
15
16 /*
17 =head1 NAME
18
19 iolayer.c - encapsulates different source of data into a single framework.
20
21 =head1 SYNOPSIS
22
23   io_glue *ig = io_new_fd( fileno(stdin) );
24   method = io_reqmeth( IOL_NOSEEK | IOL_MMAP ); // not implemented yet
25   io_glue_commit_types(ig);                     // always assume IOL_SEEK for now
26   switch (method) {
27   case IOL_NOSEEK:
28     code that uses ig->readcb()
29     to read data goes here.
30     break;
31   case IOL_MMAP:
32     code that uses ig->readcb()
33     to read data goes here.
34     break;
35   }  
36
37   io_glue_DESTROY(ig);
38   // and much more
39
40 =head1 DESCRIPTION
41
42 iolayer.c implements the basic functions to create and destroy io_glue
43 objects for Imager.  The typical usage pattern for data sources is:
44
45    1. Create the source (io_new_fd)
46    2. Define how you want to get data from it (io_reqmeth)
47    3. read from it using the interface requested (ig->readdb, ig->mmapcb)
48    4. Close the source, which 
49       shouldn't really close the underlying source. (io_glue DESTROY)
50
51 =head1 FUNCTION REFERENCE
52
53 Some of these functions are internal.
54
55 =over
56
57 =cut
58 */
59
60
61
62
63 /*
64  * Callbacks for sources that cannot seek
65  */
66
67 /* fakeseek_read: read method for when emulating a seekable source
68 static
69 ssize_t
70 fakeseek_read(io_glue *ig, void *buf, size_t count) {
71   io_ex_fseek *exdata = ig->exdata; 
72   return 0;
73 }
74 */
75
76
77
78 /*
79  * Callbacks for sources that can seek 
80  */
81
82 /*
83 =item realseek_read(ig, buf, count)
84
85 Does the reading from a source that can be seeked on
86
87    ig    - io_glue object
88    buf   - buffer to return data in
89    count - number of bytes to read into buffer max
90
91 =cut
92 */
93
94 static
95 ssize_t 
96 realseek_read(io_glue *ig, void *buf, size_t count) {
97   io_ex_rseek *ier = ig->exdata;
98   int fd           = (int)ig->source.cb.p;
99   ssize_t       rc = 0;
100   size_t        bc = 0;
101   char       *cbuf = buf;
102
103   IOL_DEB( printf("realseek_read: fd = %d, ier->cpos = %ld, buf = %p, count = %d\n", fd, (long) ier->cpos, buf, count) );
104   /* Is this a good idea? Would it be better to handle differently? skip handling? */
105   while( count!=bc && (rc = ig->source.cb.readcb(fd,cbuf+bc,count-bc))>0 ) bc+=rc;
106   
107   ier->cpos += bc;
108   IOL_DEB( printf("realseek_read: rc = %d, bc = %d\n", rc, bc) );
109   return bc;
110 }
111
112
113 /*
114 =item realseek_write(ig, buf, count)
115
116 Does the writing to a 'source' that can be seeked on
117
118    ig    - io_glue object
119    buf   - buffer that contains data
120    count - number of bytes to write
121
122 =cut
123 */
124
125 static
126 ssize_t 
127 realseek_write(io_glue *ig, const void *buf, size_t count) {
128   io_ex_rseek *ier = ig->exdata;
129   int           fd = (int)ig->source.cb.p;
130   ssize_t       rc = 0;
131   size_t        bc = 0;
132   char       *cbuf = (char*)buf; 
133   
134   IOL_DEB( printf("realseek_write: fd = %d, ier->cpos = %ld, buf = %p, count = %d\n", fd, (long) ier->cpos, buf, count) );
135   /* Is this a good idea? Would it be better to handle differently? skip handling? */
136
137   while( count!=bc && (rc = ig->source.cb.writecb(fd,cbuf+bc,count-bc))>0 ) bc+=rc;
138
139   ier->cpos += bc;
140   IOL_DEB( printf("realseek_write: rc = %d, bc = %d\n", rc, bc) );
141   return bc;
142 }
143
144
145 /*
146 =item realseek_close(ig)
147
148 Closes a source that can be seeked on.  Not sure if this should be an actual close
149 or not.  Does nothing for now.  Should be fixed.
150
151    ig - data source
152
153 =cut
154 */
155
156 static
157 void
158 realseek_close(io_glue *ig) {
159   mm_log((1, "realseek_close(ig %p)\n", ig));
160   /* FIXME: Do stuff here */
161 }
162
163
164 /* realseek_seek(ig, offset, whence)
165
166 Implements seeking for a source that is seekable, the purpose of having this is to be able to
167 have an offset into a file that is different from what the underlying library thinks.
168
169    ig     - data source
170    offset - offset into stream
171    whence - whence argument a la lseek
172
173 =cut
174 */
175
176 static
177 off_t
178 realseek_seek(io_glue *ig, off_t offset, int whence) {
179   /*  io_ex_rseek *ier = ig->exdata; Needed later */
180   int fd           = (int)ig->source.cb.p;
181   int rc;
182   IOL_DEB( printf("realseek_seek(ig %p, offset %ld, whence %d)\n", ig, (long) offset, whence) );
183   rc = lseek(fd, offset, whence);
184
185   IOL_DEB( printf("realseek_seek: rc %ld\n", (long) rc) );
186   return rc;
187   /* FIXME: How about implementing this offset handling stuff? */
188 }
189
190
191
192
193
194
195
196 /*
197  * Callbacks for sources that are a chain of variable sized buffers
198  */
199
200
201
202 /* Helper functions for buffer chains */
203
204 static
205 io_blink*
206 io_blink_new(void) {
207   io_blink *ib;
208
209   mm_log((1, "io_blink_new()\n"));
210
211   ib = mymalloc(sizeof(io_blink));
212
213   ib->next = NULL;
214   ib->prev = NULL;
215   ib->len  = BBSIZ;
216
217   memset(&ib->buf, 0, ib->len);
218   return ib;
219 }
220
221
222
223 /*
224 =item io_bchain_advance(ieb)
225
226 Advances the buffer chain to the next link - extending if
227 necessary.  Also adjusts the cpos and tfill counters as needed.
228
229    ieb   - buffer chain object
230
231 =cut
232 */
233
234 static
235 void
236 io_bchain_advance(io_ex_bchain *ieb) {
237   if (ieb->cp->next == NULL) {
238     ieb->tail = io_blink_new();
239     ieb->tail->prev = ieb->cp;
240     ieb->cp->next   = ieb->tail;
241
242     ieb->tfill = 0; /* Only set this if we added a new slice */
243   }
244   ieb->cp    = ieb->cp->next;
245   ieb->cpos  = 0;
246 }
247
248
249
250 /*
251 =item io_bchain_destroy()
252
253 frees all resources used by a buffer chain.
254
255 =cut
256 */
257
258 void
259 io_destroy_bufchain(io_ex_bchain *ieb) {
260   io_blink *cp = ieb->head;
261   while(cp) {
262     io_blink *t = cp->next;
263     free(cp);
264     cp = t;
265   }
266 }
267
268
269
270
271 /*
272
273 static
274 void
275 bufchain_dump(io_ex_bchain *ieb) {
276   mm_log((1, "  buf_chain_dump(ieb %p)\n"));
277   mm_log((1, "  buf_chain_dump: ieb->offset = %d\n", ieb->offset));
278   mm_log((1, "  buf_chain_dump: ieb->length = %d\n", ieb->length));
279   mm_log((1, "  buf_chain_dump: ieb->head   = %p\n", ieb->head  ));
280   mm_log((1, "  buf_chain_dump: ieb->tail   = %p\n", ieb->tail  ));
281   mm_log((1, "  buf_chain_dump: ieb->tfill  = %d\n", ieb->tfill ));
282   mm_log((1, "  buf_chain_dump: ieb->cp     = %p\n", ieb->cp    ));
283   mm_log((1, "  buf_chain_dump: ieb->cpos   = %d\n", ieb->cpos  ));
284   mm_log((1, "  buf_chain_dump: ieb->gpos   = %d\n", ieb->gpos  ));
285 }
286 */
287
288 /*
289  * TRUE if lengths are NOT equal
290  */
291
292 /*
293 static
294 void
295 chainlencert( io_glue *ig ) {
296   int clen;
297   int cfl           = 0;
298   size_t csize      = 0;
299   size_t cpos       = 0;
300   io_ex_bchain *ieb = ig->exdata;
301   io_blink *cp      = ieb->head;
302   
303
304   if (ieb->gpos > ieb->length) mm_log((1, "BBAR : ieb->gpos = %d, ieb->length = %d\n", ieb->gpos, ieb->length));
305
306   while(cp) {
307     clen = (cp == ieb->tail) ? ieb->tfill : cp->len;
308     if (ieb->head == cp && cp->prev) mm_log((1, "Head of chain has a non null prev\n"));
309     if (ieb->tail == cp && cp->next) mm_log((1, "Tail of chain has a non null next\n"));
310     
311     if (ieb->head != cp && !cp->prev) mm_log((1, "Middle of chain has a null prev\n"));
312     if (ieb->tail != cp && !cp->next) mm_log((1, "Middle of chain has a null next\n"));
313     
314     if (cp->prev && cp->prev->next != cp) mm_log((1, "%p = cp->prev->next != cp\n", cp->prev->next));
315     if (cp->next && cp->next->prev != cp) mm_log((1, "%p cp->next->prev != cp\n", cp->next->prev));
316
317     if (cp == ieb->cp) {
318       cfl = 1;
319       cpos += ieb->cpos;
320     }
321
322     if (!cfl) cpos += clen;
323
324     csize += clen;
325     cp     = cp->next;
326   }
327   if (( csize != ieb->length )) mm_log((1, "BAR : csize = %d, ieb->length = %d\n", csize, ieb->length));
328   if (( cpos  != ieb->gpos   )) mm_log((1, "BAR : cpos  = %d, ieb->gpos   = %d\n", cpos,  ieb->gpos  ));
329 }
330
331
332 static
333 void
334 chaincert( io_glue *ig) {
335   size_t csize   = 0;
336   io_ex_bchain *ieb = ig->exdata;
337   io_blink *cp   = ieb->head;
338   
339   mm_log((1, "Chain verification.\n"));
340
341   mm_log((1, "  buf_chain_dump: ieb->offset = %d\n", ieb->offset));
342   mm_log((1, "  buf_chain_dump: ieb->length = %d\n", ieb->length));
343   mm_log((1, "  buf_chain_dump: ieb->head   = %p\n", ieb->head  ));
344   mm_log((1, "  buf_chain_dump: ieb->tail   = %p\n", ieb->tail  ));
345   mm_log((1, "  buf_chain_dump: ieb->tfill  = %d\n", ieb->tfill ));
346   mm_log((1, "  buf_chain_dump: ieb->cp     = %p\n", ieb->cp    ));
347   mm_log((1, "  buf_chain_dump: ieb->cpos   = %d\n", ieb->cpos  ));
348   mm_log((1, "  buf_chain_dump: ieb->gpos   = %d\n", ieb->gpos  ));
349
350   while(cp) {
351     int clen = cp == ieb->tail ? ieb->tfill : cp->len;
352     mm_log((1, "link: %p <- %p -> %p\n", cp->prev, cp, cp->next));
353     if (ieb->head == cp && cp->prev) mm_log((1, "Head of chain has a non null prev\n"));
354     if (ieb->tail == cp && cp->next) mm_log((1, "Tail of chain has a non null next\n"));
355     
356     if (ieb->head != cp && !cp->prev) mm_log((1, "Middle of chain has a null prev\n"));
357     if (ieb->tail != cp && !cp->next) mm_log((1, "Middle of chain has a null next\n"));
358     
359     if (cp->prev && cp->prev->next != cp) mm_log((1, "%p = cp->prev->next != cp\n", cp->prev->next));
360     if (cp->next && cp->next->prev != cp) mm_log((1, "%p cp->next->prev != cp\n", cp->next->prev));
361
362     csize += clen;
363     cp     = cp->next;
364   }
365
366   mm_log((1, "csize = %d %s ieb->length = %d\n", csize, csize == ieb->length ? "==" : "!=", ieb->length));
367 }
368 */
369
370
371
372
373
374
375
376
377
378
379
380 /*
381 =item bufchain_read(ig, buf, count)
382
383 Does the reading from a source that can be seeked on
384
385    ig    - io_glue object
386    buf   - buffer to return data in
387    count - number of bytes to read into buffer max
388
389 =cut
390 */
391
392 static
393 ssize_t 
394 bufchain_read(io_glue *ig, void *buf, size_t count) {
395   io_ex_bchain *ieb = ig->exdata;
396   size_t     scount = count;
397   char        *cbuf = buf;
398   size_t         sk;
399
400   mm_log((1, "bufchain_read(ig %p, buf %p, count %ld)\n", ig, buf, count));
401
402   while( scount ) {
403     int clen = (ieb->cp == ieb->tail) ? ieb->tfill : ieb->cp->len;
404     if (clen == ieb->cpos) {
405       if (ieb->cp == ieb->tail) break; /* EOF */
406       ieb->cp = ieb->cp->next;
407       ieb->cpos = 0;
408       clen = (ieb->cp == ieb->tail) ? ieb->tfill : ieb->cp->len;
409     }
410
411     sk = clen - ieb->cpos;
412     sk = sk > scount ? scount : sk;
413
414     memcpy(&cbuf[count-scount], &ieb->cp->buf[ieb->cpos], sk);
415     scount    -= sk;
416     ieb->cpos += sk;
417     ieb->gpos += sk;
418   }
419
420   mm_log((1, "bufchain_read: returning %d\n", count-scount));
421   return count-scount;
422 }
423
424
425
426
427
428 /*
429 =item bufchain_write(ig, buf, count)
430
431 Does the writing to a 'source' that can be seeked on
432
433    ig    - io_glue object
434    buf   - buffer that contains data
435    count - number of bytes to write
436
437 =cut
438 */
439
440 static
441 ssize_t
442 bufchain_write(io_glue *ig, const void *buf, size_t count) {
443   char *cbuf = (char *)buf;
444   io_ex_bchain *ieb = ig->exdata;
445   size_t         ocount = count;
446   size_t         sk;
447
448   mm_log((1, "bufchain_write: ig = %p, buf = %p, count = %d\n", ig, buf, count));
449
450   IOL_DEB( printf("bufchain_write: ig = %p, ieb->cpos = %ld, buf = %p, count = %d\n", ig, (long) ieb->cpos, buf, count) );
451   
452   while(count) {
453     mm_log((2, "bufchain_write: - looping - count = %d\n", count));
454     if (ieb->cp->len == ieb->cpos) {
455       mm_log((1, "bufchain_write: cp->len == ieb->cpos = %d - advancing chain\n", (long) ieb->cpos));
456       io_bchain_advance(ieb);
457     }
458
459     sk = ieb->cp->len - ieb->cpos;
460     sk = sk > count ? count : sk;
461     memcpy(&ieb->cp->buf[ieb->cpos], &cbuf[ocount-count], sk);
462
463     if (ieb->cp == ieb->tail) {
464       int extend = ieb->cpos + sk - ieb->tfill;
465       mm_log((2, "bufchain_write: extending tail by %d\n", extend));
466       if (extend > 0) {
467         ieb->length += extend;
468         ieb->tfill  += extend;
469       }
470     }
471
472     ieb->cpos += sk;
473     ieb->gpos += sk;
474     count     -= sk;
475   }
476   return ocount;
477 }
478
479 /*
480 =item bufchain_close(ig)
481
482 Closes a source that can be seeked on.  Not sure if this should be an actual close
483 or not.  Does nothing for now.  Should be fixed.
484
485    ig - data source
486
487 =cut
488 */
489
490 static
491 void
492 bufchain_close(io_glue *ig) {
493   mm_log((1, "bufchain_close(ig %p)\n",ig));
494   IOL_DEB( printf("bufchain_close(ig %p)\n", ig) );
495   /* FIXME: Commit a seek point here */
496   
497 }
498
499
500 /* bufchain_seek(ig, offset, whence)
501
502 Implements seeking for a source that is seekable, the purpose of having this is to be able to
503 have an offset into a file that is different from what the underlying library thinks.
504
505    ig     - data source
506    offset - offset into stream
507    whence - whence argument a la lseek
508
509 =cut
510 */
511
512 static
513 off_t
514 bufchain_seek(io_glue *ig, off_t offset, int whence) {
515   io_ex_bchain *ieb = ig->exdata;
516   io_blink *ib      = NULL;
517   int wrlen;
518
519   off_t cof = 0;
520   off_t scount = offset;
521   off_t sk;
522
523   mm_log((1, "bufchain_seek(ig %p, offset %ld, whence %d)\n", ig, offset, whence));
524
525   switch (whence) {
526   case SEEK_SET: /* SEEK_SET = 0, From the top */
527     ieb->cp   = ieb->head;
528     ieb->cpos = 0;
529     ieb->gpos = 0;
530
531     while( scount ) {
532       int clen = (ieb->cp == ieb->tail) ? ieb->tfill : ieb->cp->len;
533       if (clen == ieb->cpos) {
534         if (ieb->cp == ieb->tail) break; /* EOF */
535         ieb->cp = ieb->cp->next;
536         ieb->cpos = 0;
537         clen = (ieb->cp == ieb->tail) ? ieb->tfill : ieb->cp->len;
538       }
539       
540       sk = clen - ieb->cpos;
541       sk = sk > scount ? scount : sk;
542       
543       scount    -= sk;
544       ieb->cpos += sk;
545       ieb->gpos += sk;
546     }
547
548     wrlen = scount;
549
550     if (wrlen > 0) { 
551       /*
552        * extending file - get ieb into consistent state and then
553        * call write which will get it to the correct position 
554        */
555       char TB[BBSIZ];
556       memset(TB, 0, BBSIZ);
557       ieb->gpos = ieb->length;
558       ieb->cpos = ieb->tfill;
559
560       while(wrlen > 0) {
561         ssize_t rc, wl = min(wrlen, BBSIZ);
562         mm_log((1, "bufchain_seek: wrlen = %d, wl = %d\n", wrlen, wl));
563         rc = bufchain_write( ig, TB, wl );
564         if (rc != wl) m_fatal(0, "bufchain_seek: Unable to extend file\n");
565         wrlen -= rc;
566       }
567     }
568     
569     break;
570
571   case SEEK_CUR:
572     m_fatal(123, "SEEK_CUR IS NOT IMPLEMENTED\n");
573
574     /*
575       case SEEK_CUR: 
576       ib = ieb->cp;
577       if (cof < 0) {
578       cof += ib->cpos;
579       cpos = 0;
580       while(cof < 0 && ib->prev) {
581       ib = ib->prev;
582       cof += ib->len;
583       }
584     */
585     
586   case SEEK_END: /* SEEK_END = 2 */
587     if (cof>0) m_fatal(0, "bufchain_seek: SEEK_END + %d : Extending files via seek not supported!\n", cof);
588
589     ieb->cp   = ieb->tail;
590     ieb->cpos = ieb->tfill;
591     
592     if (cof<0) {
593       cof      += ieb->cpos;
594       ieb->cpos = 0;
595
596       while(cof<0 && ib->prev) {
597         ib   = ib->prev;
598         cof += ib->len;
599       }
600     
601       if (cof<0) m_fatal(0, "bufchain_seek: Tried to seek before start of file\n");
602       ieb->gpos = ieb->length+offset;
603       ieb->cpos = cof;
604     }
605     break;
606   default:
607     m_fatal(0, "bufchain_seek: Unhandled seek request: whence = %d\n", whence );
608   }
609
610   mm_log((2, "bufchain_seek: returning ieb->gpos = %d\n", ieb->gpos));
611   return ieb->gpos;
612 }
613
614
615
616
617
618
619 /*
620  * Methods for setting up data source
621  */
622
623 /*
624 =item io_obj_setp_buffer(io, p, len)
625
626 Sets an io_object for reading from a buffer source
627
628    io  - io object that describes a source
629    p   - pointer to buffer
630    len - length of buffer
631
632 =cut
633 */
634
635 void
636 io_obj_setp_buffer(io_obj *io, void *p, size_t len) {
637   io->buffer.type = BUFFER;
638   io->buffer.c    = (char*) p;
639   io->buffer.len  = len;
640 }
641
642
643 /*
644 =item io_obj_setp_cbuf(io, p)
645
646 Sets an io_object for reading/writing from a buffer source
647
648    io  - io object that describes a source
649    p   - pointer to buffer
650    len - length of buffer
651
652 =cut
653 */
654
655 void
656 io_obj_setp_bufchain(io_obj *io) {
657   io->type = BUFCHAIN;
658 }
659
660
661 /*
662 =item io_obj_setp_cb(io, p, readcb, writecb, seekcb)
663
664 Sets an io_object for reading from a source that uses callbacks
665
666    io      - io object that describes a source
667    p       - pointer to data for callbacks
668    readcb  - read callback to read from source
669    writecb - write callback to write to source
670    seekcb  - seek callback to seek on source
671
672 =cut
673 */
674
675 void
676 io_obj_setp_cb(io_obj *io, void *p, readl readcb, writel writecb, seekl seekcb) {
677   io->cb.type    = CBSEEK;
678   io->cb.p       = p;
679   io->cb.readcb  = readcb;
680   io->cb.writecb = writecb;
681   io->cb.seekcb  = seekcb;
682 }
683
684 /*
685 =item io_glue_commit_types(ig)
686
687 Creates buffers and initializes structures to read with the chosen interface.
688
689    ig - io_glue object
690
691 =cut
692 */
693
694 void
695 io_glue_commit_types(io_glue *ig) {
696   io_type      inn = ig->source.type;
697   
698   mm_log((1, "io_glue_commit_types(ig %p)\n", ig));
699   mm_log((1, "io_glue_commit_types: source type %d (%s)\n", inn, io_type_names[inn]));
700   
701   switch (inn) {
702   case BUFCHAIN:
703     {
704       io_ex_bchain *ieb = mymalloc(sizeof(io_ex_bchain));
705       
706       ieb->offset = 0;
707       ieb->length = 0;
708       ieb->cpos   = 0;
709       ieb->gpos   = 0;
710       ieb->tfill  = 0;
711
712       ieb->head   = io_blink_new();
713       ieb->cp     = ieb->head;
714       ieb->tail   = ieb->head;
715
716       ig->exdata  = ieb;
717       ig->readcb  = bufchain_read;
718       ig->writecb = bufchain_write;
719       ig->seekcb  = bufchain_seek;
720       ig->closecb = bufchain_close;
721     }
722     break;
723   case CBSEEK:
724   default:
725     {
726       io_ex_rseek *ier = mymalloc(sizeof(io_ex_rseek));
727       
728       ier->offset = 0;
729       ier->cpos   = 0;
730       
731       ig->exdata  = ier;
732       ig->readcb  = realseek_read;
733       ig->writecb = realseek_write;
734       ig->seekcb  = realseek_seek;
735       ig->closecb = realseek_close;
736     }
737   }
738 }
739
740 /*
741 =item io_glue_gettypes(ig, reqmeth)
742
743 Returns a set of compatible interfaces to read data with.
744
745   ig      - io_glue object
746   reqmeth - request mask
747
748 The request mask is a bit mask (of something that hasn't been implemented yet)
749 of interfaces that it would like to read data from the source which the ig
750 describes.
751
752 =cut
753 */
754
755 void
756 io_glue_gettypes(io_glue *ig, int reqmeth) {
757
758   ig = NULL;
759   reqmeth = 0;
760   
761   /* FIXME: Implement this function! */
762   /* if (ig->source.type = 
763      if (reqmeth & IO_BUFF) */ 
764
765 }
766
767
768 /*
769 =item io_new_bufchain()
770
771 returns a new io_glue object that has the 'empty' source and but can
772 be written to and read from later (like a pseudo file).
773
774 =cut
775 */
776
777 io_glue *
778 io_new_bufchain() {
779   io_glue *ig = mymalloc(sizeof(io_glue));
780   io_obj_setp_bufchain(&ig->source);
781   return ig;
782 }
783
784
785
786
787
788
789
790
791 /*
792 =item io_new_fd(fd)
793
794 returns a new io_glue object that has the source defined as reading
795 from specified filedescriptor.  Note that the the interface to recieving
796 data from the io_glue callbacks hasn't been done yet.
797
798    fd - file descriptor to read/write from
799
800 =cut
801 */
802
803 io_glue *
804 io_new_fd(int fd) {
805   io_glue *ig = mymalloc(sizeof(io_glue));
806   memset(ig, 0, sizeof(*ig));
807 #ifdef _MSC_VER
808   io_obj_setp_cb(&ig->source, (void*)fd, _read, _write, _lseek);
809 #else
810   io_obj_setp_cb(&ig->source, (void*)fd, read, write, lseek);
811 #endif
812   return ig;
813 }
814
815
816
817 /*
818 =item io_slurp(ig)
819
820 Takes the source that the io_glue is bound to and allocates space
821 for a return buffer and returns the entire content in a single buffer.
822 Note: This only works for io_glue objects that contain a bufchain.  It
823 is usefull for saving to scalars and such.
824
825    ig - io_glue object
826    c  - pointer to a pointer to where data should be copied to
827
828 =cut
829 */
830
831 size_t
832 io_slurp(io_glue *ig, unsigned char **c) {
833   ssize_t rc;
834   off_t orgoff;
835   io_ex_bchain *ieb;
836   unsigned char *cc;
837   io_type inn = ig->source.type;
838   
839   if ( inn != BUFCHAIN ) {
840     m_fatal(0, "io_slurp: called on a source that is not from a bufchain\n");
841   }
842
843   ieb = ig->exdata;
844   cc = *c = mymalloc( ieb->length );
845   
846   orgoff = ieb->gpos;
847   
848   bufchain_seek(ig, 0, SEEK_SET);
849   
850   rc = bufchain_read(ig, cc, ieb->length);
851
852   if (rc != ieb->length)
853     m_fatal(1, "io_slurp: bufchain_read returned an incomplete read: rc = %d, request was %d\n", rc, ieb->length);
854
855   return rc;
856 }
857
858
859 /*
860 =item io_glue_DESTROY(ig)
861
862 A destructor method for io_glue objects.  Should clean up all related buffers.
863 Might leave us with a dangling pointer issue on some buffers.
864
865    ig - io_glue object to destroy.
866
867 =cut
868 */
869
870 void
871 io_glue_DESTROY(io_glue *ig) {
872   io_type      inn = ig->source.type;
873   mm_log((1, "io_glue_DESTROY(ig %p)\n", ig));
874   
875   switch (inn) {
876   case BUFCHAIN:
877     {
878       io_ex_bchain *ieb = ig->exdata;
879       io_destroy_bufchain(ieb);
880     }
881     break;
882   case CBSEEK:
883   default:
884     {
885       io_ex_rseek *ier = ig->exdata;
886       myfree(ier);
887     }
888
889   }
890   myfree(ig);
891 }
892
893
894 /*
895 =back
896
897 =head1 AUTHOR
898
899 Arnar M. Hrafnkelsson <addi@umich.edu>
900
901 =head1 SEE ALSO
902
903 Imager(3)
904
905 =cut
906 */