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