13 char *io_type_names[] = { "FDSEEK", "FDNOSEEK", "BUFFER", "CBSEEK", "CBNOSEEK", "BUFCHAIN" };
19 iolayer.c - encapsulates different source of data into a single framework.
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
28 code that uses ig->readcb()
29 to read data goes here.
32 code that uses ig->readcb()
33 to read data goes here.
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:
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)
51 =head1 FUNCTION REFERENCE
53 Some of these functions are internal.
64 * Callbacks for sources that cannot seek
67 /* fakeseek_read: read method for when emulating a seekable source
70 fakeseek_read(io_glue *ig, void *buf, size_t count) {
71 io_ex_fseek *exdata = ig->exdata;
79 * Callbacks for sources that can seek
83 =item realseek_read(ig, buf, count)
85 Does the reading from a source that can be seeked on
88 buf - buffer to return data in
89 count - number of bytes to read into buffer max
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;
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;
108 IOL_DEB( printf("realseek_read: rc = %d, bc = %d\n", rc, bc) );
114 =item realseek_write(ig, buf, count)
116 Does the writing to a 'source' that can be seeked on
119 buf - buffer that contains data
120 count - number of bytes to write
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;
132 char *cbuf = (char*)buf;
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? */
137 while( count!=bc && (rc = ig->source.cb.writecb(fd,cbuf+bc,count-bc))>0 ) bc+=rc;
140 IOL_DEB( printf("realseek_write: rc = %d, bc = %d\n", rc, bc) );
146 =item realseek_close(ig)
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.
158 realseek_close(io_glue *ig) {
159 mm_log((1, "realseek_close(ig %p)\n", ig));
160 /* FIXME: Do stuff here */
164 /* realseek_seek(ig, offset, whence)
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.
170 offset - offset into stream
171 whence - whence argument a la lseek
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;
182 IOL_DEB( printf("realseek_seek(ig %p, offset %ld, whence %d)\n", ig, (long) offset, whence) );
183 rc = lseek(fd, offset, whence);
185 IOL_DEB( printf("realseek_seek: rc %ld\n", (long) rc) );
187 /* FIXME: How about implementing this offset handling stuff? */
213 * Callbacks for sources that are a fixed size buffer
217 =item buffer_read(ig, buf, count)
219 Does the reading from a buffer source
222 buf - buffer to return data in
223 count - number of bytes to read into buffer max
230 buffer_read(io_glue *ig, void *buf, size_t count) {
231 io_ex_buffer *ieb = ig->exdata;
234 IOL_DEB( printf("buffer_read: fd = %d, ier->cpos = %ld, buf = %p, count = %d\n", fd, (long) ier->cpos, buf, count) );
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;
241 memcpy(buf, ig->source.buffer.data+ieb->cpos, count);
243 IOL_DEB( printf("buffer_read: rc = %d, count = %d\n", rc, count) );
249 =item buffer_write(ig, buf, count)
251 Does nothing, returns -1
254 buf - buffer that contains data
255 count - number of bytes to write
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"));
269 =item buffer_close(ig)
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.
281 buffer_close(io_glue *ig) {
282 mm_log((1, "buffer_close(ig %p)\n", ig));
283 /* FIXME: Do stuff here */
287 /* buffer_seek(ig, offset, whence)
289 Implements seeking for a buffer source.
292 offset - offset into stream
293 whence - whence argument a la lseek
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;
306 if (reqpos > ig->source.buffer.len) {
307 mm_log((1, "seeking out of readable range\n"));
312 IOL_DEB( printf("buffer_seek(ig %p, offset %ld, whence %d)\n", ig, (long) offset, whence) );
315 /* FIXME: How about implementing this offset handling stuff? */
323 * Callbacks for sources that are a chain of variable sized buffers
328 /* Helper functions for buffer chains */
335 mm_log((1, "io_blink_new()\n"));
337 ib = mymalloc(sizeof(io_blink));
343 memset(&ib->buf, 0, ib->len);
350 =item io_bchain_advance(ieb)
352 Advances the buffer chain to the next link - extending if
353 necessary. Also adjusts the cpos and tfill counters as needed.
355 ieb - buffer chain object
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;
368 ieb->tfill = 0; /* Only set this if we added a new slice */
370 ieb->cp = ieb->cp->next;
377 =item io_bchain_destroy()
379 frees all resources used by a buffer chain.
385 io_destroy_bufchain(io_ex_bchain *ieb) {
387 mm_log((1, "io_destroy_bufchain(ieb %p)\n", ieb));
391 io_blink *t = cp->next;
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 ));
418 * TRUE if lengths are NOT equal
424 chainlencert( io_glue *ig ) {
429 io_ex_bchain *ieb = ig->exdata;
430 io_blink *cp = ieb->head;
433 if (ieb->gpos > ieb->length) mm_log((1, "BBAR : ieb->gpos = %d, ieb->length = %d\n", ieb->gpos, ieb->length));
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"));
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"));
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));
451 if (!cfl) cpos += clen;
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 ));
463 chaincert( io_glue *ig) {
465 io_ex_bchain *ieb = ig->exdata;
466 io_blink *cp = ieb->head;
468 mm_log((1, "Chain verification.\n"));
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 ));
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"));
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"));
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));
495 mm_log((1, "csize = %d %s ieb->length = %d\n", csize, csize == ieb->length ? "==" : "!=", ieb->length));
510 =item bufchain_read(ig, buf, count)
512 Does the reading from a source that can be seeked on
515 buf - buffer to return data in
516 count - number of bytes to read into buffer max
523 bufchain_read(io_glue *ig, void *buf, size_t count) {
524 io_ex_bchain *ieb = ig->exdata;
525 size_t scount = count;
529 mm_log((1, "bufchain_read(ig %p, buf %p, count %ld)\n", ig, buf, count));
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;
537 clen = (ieb->cp == ieb->tail) ? ieb->tfill : ieb->cp->len;
540 sk = clen - ieb->cpos;
541 sk = sk > scount ? scount : sk;
543 memcpy(&cbuf[count-scount], &ieb->cp->buf[ieb->cpos], sk);
549 mm_log((1, "bufchain_read: returning %d\n", count-scount));
558 =item bufchain_write(ig, buf, count)
560 Does the writing to a 'source' that can be seeked on
563 buf - buffer that contains data
564 count - number of bytes to write
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;
577 mm_log((1, "bufchain_write: ig = %p, buf = %p, count = %d\n", ig, buf, count));
579 IOL_DEB( printf("bufchain_write: ig = %p, ieb->cpos = %ld, buf = %p, count = %d\n", ig, (long) ieb->cpos, buf, 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);
588 sk = ieb->cp->len - ieb->cpos;
589 sk = sk > count ? count : sk;
590 memcpy(&ieb->cp->buf[ieb->cpos], &cbuf[ocount-count], sk);
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));
596 ieb->length += extend;
597 ieb->tfill += extend;
609 =item bufchain_close(ig)
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.
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 */
629 /* bufchain_seek(ig, offset, whence)
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.
635 offset - offset into stream
636 whence - whence argument a la lseek
643 bufchain_seek(io_glue *ig, off_t offset, int whence) {
644 io_ex_bchain *ieb = ig->exdata;
649 off_t scount = offset;
652 mm_log((1, "bufchain_seek(ig %p, offset %ld, whence %d)\n", ig, offset, whence));
655 case SEEK_SET: /* SEEK_SET = 0, From the top */
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;
666 clen = (ieb->cp == ieb->tail) ? ieb->tfill : ieb->cp->len;
669 sk = clen - ieb->cpos;
670 sk = sk > scount ? scount : sk;
681 * extending file - get ieb into consistent state and then
682 * call write which will get it to the correct position
685 memset(TB, 0, BBSIZ);
686 ieb->gpos = ieb->length;
687 ieb->cpos = ieb->tfill;
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");
701 m_fatal(123, "SEEK_CUR IS NOT IMPLEMENTED\n");
709 while(cof < 0 && ib->prev) {
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);
719 ieb->cpos = ieb->tfill;
725 while(cof<0 && ib->prev) {
730 if (cof<0) m_fatal(0, "bufchain_seek: Tried to seek before start of file\n");
731 ieb->gpos = ieb->length+offset;
736 m_fatal(0, "bufchain_seek: Unhandled seek request: whence = %d\n", whence );
739 mm_log((2, "bufchain_seek: returning ieb->gpos = %d\n", ieb->gpos));
749 * Methods for setting up data source
753 =item io_obj_setp_buffer(io, p, len)
755 Sets an io_object for reading from a buffer source
757 io - io object that describes a source
758 p - pointer to buffer
759 len - length of buffer
765 io_obj_setp_buffer(io_obj *io, char *p, size_t len, closebufp closecb, void *closedata) {
766 io->buffer.type = BUFFER;
768 io->buffer.len = len;
769 io->buffer.closecb = closecb;
770 io->buffer.closedata = closedata;
775 =item io_obj_setp_buchain(io)
777 Sets an io_object for reading/writing from a buffer source
779 io - io object that describes a source
780 p - pointer to buffer
781 len - length of buffer
787 io_obj_setp_bufchain(io_obj *io) {
793 =item io_obj_setp_cb(io, p, readcb, writecb, seekcb)
795 Sets an io_object for reading from a source that uses callbacks
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
807 io_obj_setp_cb(io_obj *io, void *p, readl readcb, writel writecb, seekl seekcb) {
808 io->cb.type = CBSEEK;
810 io->cb.readcb = readcb;
811 io->cb.writecb = writecb;
812 io->cb.seekcb = seekcb;
816 =item io_glue_commit_types(ig)
818 Creates buffers and initializes structures to read with the chosen interface.
826 io_glue_commit_types(io_glue *ig) {
827 io_type inn = ig->source.type;
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]));
835 io_ex_bchain *ieb = mymalloc(sizeof(io_ex_bchain));
843 ieb->head = io_blink_new();
845 ieb->tail = ieb->head;
848 ig->readcb = bufchain_read;
849 ig->writecb = bufchain_write;
850 ig->seekcb = bufchain_seek;
851 ig->closecb = bufchain_close;
856 io_ex_rseek *ier = mymalloc(sizeof(io_ex_rseek));
862 ig->readcb = realseek_read;
863 ig->writecb = realseek_write;
864 ig->seekcb = realseek_seek;
865 ig->closecb = realseek_close;
870 io_ex_buffer *ieb = mymalloc(sizeof(io_ex_buffer));
875 ig->readcb = buffer_read;
876 ig->writecb = buffer_write;
877 ig->seekcb = buffer_seek;
878 ig->closecb = buffer_close;
885 =item io_glue_gettypes(ig, reqmeth)
887 Returns a set of compatible interfaces to read data with.
890 reqmeth - request mask
892 The request mask is a bit mask (of something that hasn't been implemented yet)
893 of interfaces that it would like to read data from the source which the ig
900 io_glue_gettypes(io_glue *ig, int reqmeth) {
905 /* FIXME: Implement this function! */
906 /* if (ig->source.type =
907 if (reqmeth & IO_BUFF) */
913 =item io_new_bufchain()
915 returns a new io_glue object that has the 'empty' source and but can
916 be written to and read from later (like a pseudo file).
924 mm_log((1, "io_new_bufchain()\n"));
925 ig = mymalloc(sizeof(io_glue));
926 io_obj_setp_bufchain(&ig->source);
935 =item io_new_buffer(data, len)
937 Returns a new io_glue object that has the source defined as reading
938 from specified buffer. Note that the buffer is not copied.
940 data - buffer to read from
941 len - length of buffer
947 io_new_buffer(char *data, size_t len, closebufp closecb, void *closedata) {
949 mm_log((1, "io_new_buffer(data %p, len %d, closecb %p, closedata %p)\n", data, len, closecb, closedata));
950 ig = mymalloc(sizeof(io_glue));
951 memset(ig, 0, sizeof(*ig));
952 io_obj_setp_buffer(&ig->source, data, len, closecb, closedata);
960 returns a new io_glue object that has the source defined as reading
961 from specified filedescriptor. Note that the the interface to recieving
962 data from the io_glue callbacks hasn't been done yet.
964 fd - file descriptor to read/write from
972 mm_log((1, "io_new_fd(fd %d)\n", fd));
973 ig = mymalloc(sizeof(io_glue));
974 memset(ig, 0, sizeof(*ig));
976 io_obj_setp_cb(&ig->source, (void*)fd, _read, _write, _lseek);
978 io_obj_setp_cb(&ig->source, (void*)fd, read, write, lseek);
980 mm_log((1, "(%p) <- io_new_fd\n", ig));
989 Takes the source that the io_glue is bound to and allocates space
990 for a return buffer and returns the entire content in a single buffer.
991 Note: This only works for io_glue objects that contain a bufchain. It
992 is usefull for saving to scalars and such.
995 c - pointer to a pointer to where data should be copied to
1001 io_slurp(io_glue *ig, unsigned char **c) {
1006 io_type inn = ig->source.type;
1008 if ( inn != BUFCHAIN ) {
1009 m_fatal(0, "io_slurp: called on a source that is not from a bufchain\n");
1013 cc = *c = mymalloc( ieb->length );
1017 bufchain_seek(ig, 0, SEEK_SET);
1019 rc = bufchain_read(ig, cc, ieb->length);
1021 if (rc != ieb->length)
1022 m_fatal(1, "io_slurp: bufchain_read returned an incomplete read: rc = %d, request was %d\n", rc, ieb->length);
1029 =item io_glue_DESTROY(ig)
1031 A destructor method for io_glue objects. Should clean up all related buffers.
1032 Might leave us with a dangling pointer issue on some buffers.
1034 ig - io_glue object to destroy.
1040 io_glue_DESTROY(io_glue *ig) {
1041 io_type inn = ig->source.type;
1042 mm_log((1, "io_glue_DESTROY(ig %p)\n", ig));
1047 io_ex_bchain *ieb = ig->exdata;
1048 io_destroy_bufchain(ieb);
1055 io_ex_rseek *ier = ig->exdata;
1061 io_ex_buffer *ieb = ig->exdata;
1062 if (ig->source.buffer.closecb) {
1063 mm_log((1,"calling close callback %p for io_buffer\n", ig->source.buffer.closecb));
1064 ig->source.buffer.closecb(ig->source.buffer.closedata);
1079 Arnar M. Hrafnkelsson <addi@umich.edu>