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