Added io_buffer for reading from scalars. Also added test cases. Added
[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
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212 /*
213  * Callbacks for sources that are a fixed size buffer
214  */
215
216 /*
217 =item buffer_read(ig, buf, count)
218
219 Does the reading from a buffer source
220
221    ig    - io_glue object
222    buf   - buffer to return data in
223    count - number of bytes to read into buffer max
224
225 =cut
226 */
227
228 static
229 ssize_t 
230 buffer_read(io_glue *ig, void *buf, size_t count) {
231   io_ex_buffer *ieb = ig->exdata;
232   char        *cbuf = buf;
233
234   IOL_DEB( printf("buffer_read: fd = %d, ier->cpos = %ld, buf = %p, count = %d\n", fd, (long) ier->cpos, buf, count) );
235
236   if ( ieb->cpos+count > ig->source.buffer.len ) {
237     mm_log((1,"buffer_read: short read: cpos=%d, len=%d, count=%d\n", ieb->cpos, ig->source.buffer.len));
238     count = ig->source.buffer.len - ieb->cpos;
239   }
240   
241   memcpy(buf, ig->source.buffer.data+ieb->cpos, count);
242   ieb->cpos += count;
243   IOL_DEB( printf("buffer_read: rc = %d, count = %d\n", rc, count) );
244   return count;
245 }
246
247
248 /*
249 =item buffer_write(ig, buf, count)
250
251 Does nothing, returns -1
252
253    ig    - io_glue object
254    buf   - buffer that contains data
255    count - number of bytes to write
256
257 =cut
258 */
259
260 static
261 ssize_t 
262 buffer_write(io_glue *ig, const void *buf, size_t count) {
263   mm_log((1, "buffer_write called, this method should never be called.\n"));
264   return -1;
265 }
266
267
268 /*
269 =item buffer_close(ig)
270
271 Closes a source that can be seeked on.  Not sure if this should be an actual close
272 or not.  Does nothing for now.  Should be fixed.
273
274    ig - data source
275
276 =cut
277 */
278
279 static
280 void
281 buffer_close(io_glue *ig) {
282   mm_log((1, "buffer_close(ig %p)\n", ig));
283   /* FIXME: Do stuff here */
284 }
285
286
287 /* buffer_seek(ig, offset, whence)
288
289 Implements seeking for a buffer source.
290
291    ig     - data source
292    offset - offset into stream
293    whence - whence argument a la lseek
294
295 =cut
296 */
297
298 static
299 off_t
300 buffer_seek(io_glue *ig, off_t offset, int whence) {
301   io_ex_buffer *ieb = ig->exdata;
302   off_t reqpos = offset 
303     + (whence == SEEK_CUR)*ieb->cpos 
304     + (whence == SEEK_END)*ig->source.buffer.len;
305   
306   if (reqpos > ig->source.buffer.len) {
307     mm_log((1, "seeking out of readable range\n"));
308     return (off_t)-1;
309   }
310   
311   ieb->cpos = reqpos;
312   IOL_DEB( printf("buffer_seek(ig %p, offset %ld, whence %d)\n", ig, (long) offset, whence) );
313
314   return reqpos;
315   /* FIXME: How about implementing this offset handling stuff? */
316 }
317
318
319
320
321
322 /*
323  * Callbacks for sources that are a chain of variable sized buffers
324  */
325
326
327
328 /* Helper functions for buffer chains */
329
330 static
331 io_blink*
332 io_blink_new(void) {
333   io_blink *ib;
334
335   mm_log((1, "io_blink_new()\n"));
336
337   ib = mymalloc(sizeof(io_blink));
338
339   ib->next = NULL;
340   ib->prev = NULL;
341   ib->len  = BBSIZ;
342
343   memset(&ib->buf, 0, ib->len);
344   return ib;
345 }
346
347
348
349 /*
350 =item io_bchain_advance(ieb)
351
352 Advances the buffer chain to the next link - extending if
353 necessary.  Also adjusts the cpos and tfill counters as needed.
354
355    ieb   - buffer chain object
356
357 =cut
358 */
359
360 static
361 void
362 io_bchain_advance(io_ex_bchain *ieb) {
363   if (ieb->cp->next == NULL) {
364     ieb->tail = io_blink_new();
365     ieb->tail->prev = ieb->cp;
366     ieb->cp->next   = ieb->tail;
367
368     ieb->tfill = 0; /* Only set this if we added a new slice */
369   }
370   ieb->cp    = ieb->cp->next;
371   ieb->cpos  = 0;
372 }
373
374
375
376 /*
377 =item io_bchain_destroy()
378
379 frees all resources used by a buffer chain.
380
381 =cut
382 */
383
384 void
385 io_destroy_bufchain(io_ex_bchain *ieb) {
386   io_blink *cp;
387   mm_log((1, "io_destroy_bufchain(ieb %p)\n", ieb));
388   cp = ieb->head;
389   
390   while(cp) {
391     io_blink *t = cp->next;
392     myfree(cp);
393     cp = t;
394   }
395 }
396
397
398
399
400 /*
401
402 static
403 void
404 bufchain_dump(io_ex_bchain *ieb) {
405   mm_log((1, "  buf_chain_dump(ieb %p)\n"));
406   mm_log((1, "  buf_chain_dump: ieb->offset = %d\n", ieb->offset));
407   mm_log((1, "  buf_chain_dump: ieb->length = %d\n", ieb->length));
408   mm_log((1, "  buf_chain_dump: ieb->head   = %p\n", ieb->head  ));
409   mm_log((1, "  buf_chain_dump: ieb->tail   = %p\n", ieb->tail  ));
410   mm_log((1, "  buf_chain_dump: ieb->tfill  = %d\n", ieb->tfill ));
411   mm_log((1, "  buf_chain_dump: ieb->cp     = %p\n", ieb->cp    ));
412   mm_log((1, "  buf_chain_dump: ieb->cpos   = %d\n", ieb->cpos  ));
413   mm_log((1, "  buf_chain_dump: ieb->gpos   = %d\n", ieb->gpos  ));
414 }
415 */
416
417 /*
418  * TRUE if lengths are NOT equal
419  */
420
421 /*
422 static
423 void
424 chainlencert( io_glue *ig ) {
425   int clen;
426   int cfl           = 0;
427   size_t csize      = 0;
428   size_t cpos       = 0;
429   io_ex_bchain *ieb = ig->exdata;
430   io_blink *cp      = ieb->head;
431   
432
433   if (ieb->gpos > ieb->length) mm_log((1, "BBAR : ieb->gpos = %d, ieb->length = %d\n", ieb->gpos, ieb->length));
434
435   while(cp) {
436     clen = (cp == ieb->tail) ? ieb->tfill : cp->len;
437     if (ieb->head == cp && cp->prev) mm_log((1, "Head of chain has a non null prev\n"));
438     if (ieb->tail == cp && cp->next) mm_log((1, "Tail of chain has a non null next\n"));
439     
440     if (ieb->head != cp && !cp->prev) mm_log((1, "Middle of chain has a null prev\n"));
441     if (ieb->tail != cp && !cp->next) mm_log((1, "Middle of chain has a null next\n"));
442     
443     if (cp->prev && cp->prev->next != cp) mm_log((1, "%p = cp->prev->next != cp\n", cp->prev->next));
444     if (cp->next && cp->next->prev != cp) mm_log((1, "%p cp->next->prev != cp\n", cp->next->prev));
445
446     if (cp == ieb->cp) {
447       cfl = 1;
448       cpos += ieb->cpos;
449     }
450
451     if (!cfl) cpos += clen;
452
453     csize += clen;
454     cp     = cp->next;
455   }
456   if (( csize != ieb->length )) mm_log((1, "BAR : csize = %d, ieb->length = %d\n", csize, ieb->length));
457   if (( cpos  != ieb->gpos   )) mm_log((1, "BAR : cpos  = %d, ieb->gpos   = %d\n", cpos,  ieb->gpos  ));
458 }
459
460
461 static
462 void
463 chaincert( io_glue *ig) {
464   size_t csize   = 0;
465   io_ex_bchain *ieb = ig->exdata;
466   io_blink *cp   = ieb->head;
467   
468   mm_log((1, "Chain verification.\n"));
469
470   mm_log((1, "  buf_chain_dump: ieb->offset = %d\n", ieb->offset));
471   mm_log((1, "  buf_chain_dump: ieb->length = %d\n", ieb->length));
472   mm_log((1, "  buf_chain_dump: ieb->head   = %p\n", ieb->head  ));
473   mm_log((1, "  buf_chain_dump: ieb->tail   = %p\n", ieb->tail  ));
474   mm_log((1, "  buf_chain_dump: ieb->tfill  = %d\n", ieb->tfill ));
475   mm_log((1, "  buf_chain_dump: ieb->cp     = %p\n", ieb->cp    ));
476   mm_log((1, "  buf_chain_dump: ieb->cpos   = %d\n", ieb->cpos  ));
477   mm_log((1, "  buf_chain_dump: ieb->gpos   = %d\n", ieb->gpos  ));
478
479   while(cp) {
480     int clen = cp == ieb->tail ? ieb->tfill : cp->len;
481     mm_log((1, "link: %p <- %p -> %p\n", cp->prev, cp, cp->next));
482     if (ieb->head == cp && cp->prev) mm_log((1, "Head of chain has a non null prev\n"));
483     if (ieb->tail == cp && cp->next) mm_log((1, "Tail of chain has a non null next\n"));
484     
485     if (ieb->head != cp && !cp->prev) mm_log((1, "Middle of chain has a null prev\n"));
486     if (ieb->tail != cp && !cp->next) mm_log((1, "Middle of chain has a null next\n"));
487     
488     if (cp->prev && cp->prev->next != cp) mm_log((1, "%p = cp->prev->next != cp\n", cp->prev->next));
489     if (cp->next && cp->next->prev != cp) mm_log((1, "%p cp->next->prev != cp\n", cp->next->prev));
490
491     csize += clen;
492     cp     = cp->next;
493   }
494
495   mm_log((1, "csize = %d %s ieb->length = %d\n", csize, csize == ieb->length ? "==" : "!=", ieb->length));
496 }
497 */
498
499
500
501
502
503
504
505
506
507
508
509 /*
510 =item bufchain_read(ig, buf, count)
511
512 Does the reading from a source that can be seeked on
513
514    ig    - io_glue object
515    buf   - buffer to return data in
516    count - number of bytes to read into buffer max
517
518 =cut
519 */
520
521 static
522 ssize_t 
523 bufchain_read(io_glue *ig, void *buf, size_t count) {
524   io_ex_bchain *ieb = ig->exdata;
525   size_t     scount = count;
526   char        *cbuf = buf;
527   size_t         sk;
528
529   mm_log((1, "bufchain_read(ig %p, buf %p, count %ld)\n", ig, buf, count));
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     memcpy(&cbuf[count-scount], &ieb->cp->buf[ieb->cpos], sk);
544     scount    -= sk;
545     ieb->cpos += sk;
546     ieb->gpos += sk;
547   }
548
549   mm_log((1, "bufchain_read: returning %d\n", count-scount));
550   return count-scount;
551 }
552
553
554
555
556
557 /*
558 =item bufchain_write(ig, buf, count)
559
560 Does the writing to a 'source' that can be seeked on
561
562    ig    - io_glue object
563    buf   - buffer that contains data
564    count - number of bytes to write
565
566 =cut
567 */
568
569 static
570 ssize_t
571 bufchain_write(io_glue *ig, const void *buf, size_t count) {
572   char *cbuf = (char *)buf;
573   io_ex_bchain *ieb = ig->exdata;
574   size_t         ocount = count;
575   size_t         sk;
576
577   mm_log((1, "bufchain_write: ig = %p, buf = %p, count = %d\n", ig, buf, count));
578
579   IOL_DEB( printf("bufchain_write: ig = %p, ieb->cpos = %ld, buf = %p, count = %d\n", ig, (long) ieb->cpos, buf, count) );
580   
581   while(count) {
582     mm_log((2, "bufchain_write: - looping - count = %d\n", count));
583     if (ieb->cp->len == ieb->cpos) {
584       mm_log((1, "bufchain_write: cp->len == ieb->cpos = %d - advancing chain\n", (long) ieb->cpos));
585       io_bchain_advance(ieb);
586     }
587
588     sk = ieb->cp->len - ieb->cpos;
589     sk = sk > count ? count : sk;
590     memcpy(&ieb->cp->buf[ieb->cpos], &cbuf[ocount-count], sk);
591
592     if (ieb->cp == ieb->tail) {
593       int extend = ieb->cpos + sk - ieb->tfill;
594       mm_log((2, "bufchain_write: extending tail by %d\n", extend));
595       if (extend > 0) {
596         ieb->length += extend;
597         ieb->tfill  += extend;
598       }
599     }
600
601     ieb->cpos += sk;
602     ieb->gpos += sk;
603     count     -= sk;
604   }
605   return ocount;
606 }
607
608 /*
609 =item bufchain_close(ig)
610
611 Closes a source that can be seeked on.  Not sure if this should be an actual close
612 or not.  Does nothing for now.  Should be fixed.
613
614    ig - data source
615
616 =cut
617 */
618
619 static
620 void
621 bufchain_close(io_glue *ig) {
622   mm_log((1, "bufchain_close(ig %p)\n",ig));
623   IOL_DEB( printf("bufchain_close(ig %p)\n", ig) );
624   /* FIXME: Commit a seek point here */
625   
626 }
627
628
629 /* bufchain_seek(ig, offset, whence)
630
631 Implements seeking for a source that is seekable, the purpose of having this is to be able to
632 have an offset into a file that is different from what the underlying library thinks.
633
634    ig     - data source
635    offset - offset into stream
636    whence - whence argument a la lseek
637
638 =cut
639 */
640
641 static
642 off_t
643 bufchain_seek(io_glue *ig, off_t offset, int whence) {
644   io_ex_bchain *ieb = ig->exdata;
645   io_blink *ib      = NULL;
646   int wrlen;
647
648   off_t cof = 0;
649   off_t scount = offset;
650   off_t sk;
651
652   mm_log((1, "bufchain_seek(ig %p, offset %ld, whence %d)\n", ig, offset, whence));
653
654   switch (whence) {
655   case SEEK_SET: /* SEEK_SET = 0, From the top */
656     ieb->cp   = ieb->head;
657     ieb->cpos = 0;
658     ieb->gpos = 0;
659
660     while( scount ) {
661       int clen = (ieb->cp == ieb->tail) ? ieb->tfill : ieb->cp->len;
662       if (clen == ieb->cpos) {
663         if (ieb->cp == ieb->tail) break; /* EOF */
664         ieb->cp = ieb->cp->next;
665         ieb->cpos = 0;
666         clen = (ieb->cp == ieb->tail) ? ieb->tfill : ieb->cp->len;
667       }
668       
669       sk = clen - ieb->cpos;
670       sk = sk > scount ? scount : sk;
671       
672       scount    -= sk;
673       ieb->cpos += sk;
674       ieb->gpos += sk;
675     }
676
677     wrlen = scount;
678
679     if (wrlen > 0) { 
680       /*
681        * extending file - get ieb into consistent state and then
682        * call write which will get it to the correct position 
683        */
684       char TB[BBSIZ];
685       memset(TB, 0, BBSIZ);
686       ieb->gpos = ieb->length;
687       ieb->cpos = ieb->tfill;
688
689       while(wrlen > 0) {
690         ssize_t rc, wl = min(wrlen, BBSIZ);
691         mm_log((1, "bufchain_seek: wrlen = %d, wl = %d\n", wrlen, wl));
692         rc = bufchain_write( ig, TB, wl );
693         if (rc != wl) m_fatal(0, "bufchain_seek: Unable to extend file\n");
694         wrlen -= rc;
695       }
696     }
697     
698     break;
699
700   case SEEK_CUR:
701     m_fatal(123, "SEEK_CUR IS NOT IMPLEMENTED\n");
702
703     /*
704       case SEEK_CUR: 
705       ib = ieb->cp;
706       if (cof < 0) {
707       cof += ib->cpos;
708       cpos = 0;
709       while(cof < 0 && ib->prev) {
710       ib = ib->prev;
711       cof += ib->len;
712       }
713     */
714     
715   case SEEK_END: /* SEEK_END = 2 */
716     if (cof>0) m_fatal(0, "bufchain_seek: SEEK_END + %d : Extending files via seek not supported!\n", cof);
717
718     ieb->cp   = ieb->tail;
719     ieb->cpos = ieb->tfill;
720     
721     if (cof<0) {
722       cof      += ieb->cpos;
723       ieb->cpos = 0;
724
725       while(cof<0 && ib->prev) {
726         ib   = ib->prev;
727         cof += ib->len;
728       }
729     
730       if (cof<0) m_fatal(0, "bufchain_seek: Tried to seek before start of file\n");
731       ieb->gpos = ieb->length+offset;
732       ieb->cpos = cof;
733     }
734     break;
735   default:
736     m_fatal(0, "bufchain_seek: Unhandled seek request: whence = %d\n", whence );
737   }
738
739   mm_log((2, "bufchain_seek: returning ieb->gpos = %d\n", ieb->gpos));
740   return ieb->gpos;
741 }
742
743
744
745
746
747
748 /*
749  * Methods for setting up data source
750  */
751
752 /*
753 =item io_obj_setp_buffer(io, p, len)
754
755 Sets an io_object for reading from a buffer source
756
757    io  - io object that describes a source
758    p   - pointer to buffer
759    len - length of buffer
760
761 =cut
762 */
763
764 void
765 io_obj_setp_buffer(io_obj *io, char *p, size_t len, closebufp closecb, void *closedata) {
766   io->buffer.type      = BUFFER;
767   io->buffer.data      = p;
768   io->buffer.len       = len;
769   io->buffer.closecb   = closecb;
770   io->buffer.closedata = closedata;
771 }
772
773
774 /*
775 =item io_obj_setp_buchain(io)
776
777 Sets an io_object for reading/writing from a buffer source
778
779    io  - io object that describes a source
780    p   - pointer to buffer
781    len - length of buffer
782
783 =cut
784 */
785
786 void
787 io_obj_setp_bufchain(io_obj *io) {
788   io->type = BUFCHAIN;
789 }
790
791
792 /*
793 =item io_obj_setp_cb(io, p, readcb, writecb, seekcb)
794
795 Sets an io_object for reading from a source that uses callbacks
796
797    io      - io object that describes a source
798    p       - pointer to data for callbacks
799    readcb  - read callback to read from source
800    writecb - write callback to write to source
801    seekcb  - seek callback to seek on source
802
803 =cut
804 */
805
806 void
807 io_obj_setp_cb(io_obj *io, void *p, readl readcb, writel writecb, seekl seekcb) {
808   io->cb.type    = CBSEEK;
809   io->cb.p       = p;
810   io->cb.readcb  = readcb;
811   io->cb.writecb = writecb;
812   io->cb.seekcb  = seekcb;
813 }
814
815 /*
816 =item io_glue_commit_types(ig)
817
818 Creates buffers and initializes structures to read with the chosen interface.
819
820    ig - io_glue object
821
822 =cut
823 */
824
825 void
826 io_glue_commit_types(io_glue *ig) {
827   io_type      inn = ig->source.type;
828   
829   mm_log((1, "io_glue_commit_types(ig %p)\n", ig));
830   mm_log((1, "io_glue_commit_types: source type %d (%s)\n", inn, io_type_names[inn]));
831   
832   switch (inn) {
833   case BUFCHAIN:
834     {
835       io_ex_bchain *ieb = mymalloc(sizeof(io_ex_bchain));
836       
837       ieb->offset = 0;
838       ieb->length = 0;
839       ieb->cpos   = 0;
840       ieb->gpos   = 0;
841       ieb->tfill  = 0;
842
843       ieb->head   = io_blink_new();
844       ieb->cp     = ieb->head;
845       ieb->tail   = ieb->head;
846
847       ig->exdata  = ieb;
848       ig->readcb  = bufchain_read;
849       ig->writecb = bufchain_write;
850       ig->seekcb  = bufchain_seek;
851       ig->closecb = bufchain_close;
852     }
853     break;
854   case CBSEEK:
855     {
856       io_ex_rseek *ier = mymalloc(sizeof(io_ex_rseek));
857       
858       ier->offset = 0;
859       ier->cpos   = 0;
860       
861       ig->exdata  = ier;
862       ig->readcb  = realseek_read;
863       ig->writecb = realseek_write;
864       ig->seekcb  = realseek_seek;
865       ig->closecb = realseek_close;
866     }
867     break;
868   case BUFFER:
869     {
870       io_ex_buffer *ieb = mymalloc(sizeof(io_ex_buffer));
871       ieb->offset = 0;
872       ieb->cpos   = 0;
873
874       ig->exdata  = ieb;
875       ig->readcb  = buffer_read;
876       ig->writecb = buffer_write;
877       ig->seekcb  = buffer_seek;
878       ig->closecb = buffer_close;
879     }
880     break;
881   default:
882   }
883 }
884
885 /*
886 =item io_glue_gettypes(ig, reqmeth)
887
888 Returns a set of compatible interfaces to read data with.
889
890   ig      - io_glue object
891   reqmeth - request mask
892
893 The request mask is a bit mask (of something that hasn't been implemented yet)
894 of interfaces that it would like to read data from the source which the ig
895 describes.
896
897 =cut
898 */
899
900 void
901 io_glue_gettypes(io_glue *ig, int reqmeth) {
902
903   ig = NULL;
904   reqmeth = 0;
905   
906   /* FIXME: Implement this function! */
907   /* if (ig->source.type = 
908      if (reqmeth & IO_BUFF) */ 
909
910 }
911
912
913 /*
914 =item io_new_bufchain()
915
916 returns a new io_glue object that has the 'empty' source and but can
917 be written to and read from later (like a pseudo file).
918
919 =cut
920 */
921
922 io_glue *
923 io_new_bufchain() {
924   io_glue *ig;
925   mm_log((1, "io_new_bufchain()\n"));
926   ig = mymalloc(sizeof(io_glue));
927   io_obj_setp_bufchain(&ig->source);
928   return ig;
929 }
930
931
932
933
934
935 /*
936 =item io_new_buffer(data, len)
937
938 Returns a new io_glue object that has the source defined as reading
939 from specified buffer.  Note that the buffer is not copied.
940
941    data - buffer to read from
942    len - length of buffer
943
944 =cut
945 */
946
947 io_glue *
948 io_new_buffer(char *data, size_t len, closebufp closecb, void *closedata) {
949   io_glue *ig;
950   mm_log((1, "io_new_buffer(data %p, len %d, closecb %p, closedata %p)\n", data, len, closecb, closedata));
951   ig = mymalloc(sizeof(io_glue));
952   memset(ig, 0, sizeof(*ig));
953   io_obj_setp_buffer(&ig->source, data, len, closecb, closedata);
954   return ig;
955 }
956
957
958 /*
959 =item io_new_fd(fd)
960
961 returns a new io_glue object that has the source defined as reading
962 from specified filedescriptor.  Note that the the interface to recieving
963 data from the io_glue callbacks hasn't been done yet.
964
965    fd - file descriptor to read/write from
966
967 =cut
968 */
969
970 io_glue *
971 io_new_fd(int fd) {
972   io_glue *ig;
973   mm_log((1, "io_new_fd(fd %d)\n", fd));
974   ig = mymalloc(sizeof(io_glue));
975   memset(ig, 0, sizeof(*ig));
976 #ifdef _MSC_VER
977   io_obj_setp_cb(&ig->source, (void*)fd, _read, _write, _lseek);
978 #else
979   io_obj_setp_cb(&ig->source, (void*)fd, read, write, lseek);
980 #endif
981   mm_log((1, "(%p) <- io_new_fd\n", ig));
982   return ig;
983 }
984
985
986
987 /*
988 =item io_slurp(ig)
989
990 Takes the source that the io_glue is bound to and allocates space
991 for a return buffer and returns the entire content in a single buffer.
992 Note: This only works for io_glue objects that contain a bufchain.  It
993 is usefull for saving to scalars and such.
994
995    ig - io_glue object
996    c  - pointer to a pointer to where data should be copied to
997
998 =cut
999 */
1000
1001 size_t
1002 io_slurp(io_glue *ig, unsigned char **c) {
1003   ssize_t rc;
1004   off_t orgoff;
1005   io_ex_bchain *ieb;
1006   unsigned char *cc;
1007   io_type inn = ig->source.type;
1008   
1009   if ( inn != BUFCHAIN ) {
1010     m_fatal(0, "io_slurp: called on a source that is not from a bufchain\n");
1011   }
1012
1013   ieb = ig->exdata;
1014   cc = *c = mymalloc( ieb->length );
1015   
1016   orgoff = ieb->gpos;
1017   
1018   bufchain_seek(ig, 0, SEEK_SET);
1019   
1020   rc = bufchain_read(ig, cc, ieb->length);
1021
1022   if (rc != ieb->length)
1023     m_fatal(1, "io_slurp: bufchain_read returned an incomplete read: rc = %d, request was %d\n", rc, ieb->length);
1024
1025   return rc;
1026 }
1027
1028
1029 /*
1030 =item io_glue_DESTROY(ig)
1031
1032 A destructor method for io_glue objects.  Should clean up all related buffers.
1033 Might leave us with a dangling pointer issue on some buffers.
1034
1035    ig - io_glue object to destroy.
1036
1037 =cut
1038 */
1039
1040 void
1041 io_glue_DESTROY(io_glue *ig) {
1042   io_type      inn = ig->source.type;
1043   mm_log((1, "io_glue_DESTROY(ig %p)\n", ig));
1044   
1045   switch (inn) {
1046   case BUFCHAIN:
1047     {
1048       io_ex_bchain *ieb = ig->exdata;
1049       io_destroy_bufchain(ieb);
1050       myfree(ieb);
1051     }
1052     break;
1053   case CBSEEK:
1054   default:
1055     {
1056       io_ex_rseek *ier = ig->exdata;
1057       myfree(ier);
1058     }
1059     break;
1060   case BUFFER:
1061     {
1062       io_ex_buffer *ieb = ig->exdata;
1063       if (ig->source.buffer.closecb) {
1064         mm_log((1,"calling close callback %p for io_buffer\n", ig->source.buffer.closecb));
1065         ig->source.buffer.closecb(ig->source.buffer.closedata);
1066       }
1067       myfree(ieb);
1068     }
1069     break;
1070   }
1071   myfree(ig);
1072 }
1073
1074
1075 /*
1076 =back
1077
1078 =head1 AUTHOR
1079
1080 Arnar M. Hrafnkelsson <addi@umich.edu>
1081
1082 =head1 SEE ALSO
1083
1084 Imager(3)
1085
1086 =cut
1087 */