2 lfs_wrap: Crappy wrapper code for supporting crappy ambiguous large file support.
4 copyright 2010 by the mpg123 project - free software under the terms of the LGPL 2.1
5 see COPYING and AUTHORS files in distribution or http://mpg123.org
7 initially written by Thomas Orgis, thanks to Guido Draheim for consulting
9 This file contains wrappers for the case that _FILE_OFFSET_BITS (or equivalent, theoretically, depends on mpg123.h) is defined and thus certain mpg123 API calls get renamed with a suffix (p.ex. _64).
10 The renamed calls expect large off_t arguments, and possibly return large off_t values... these wrappers here provide the same functionality with long integer arguments/values.
12 Prototypical idea: There is
13 off_t mpg123_seek_64(mpg123_handle*, off_t, int)
15 long mpg123_seek(mpg123_handle*, long, int)
17 This is rather simple business... wouldn't mpg123 offer replacing the I/O core with callbacks. Translating the callbacks between long and off_t world is the main reason why this file contains non-trivial code.
19 Note about file descriptors: We just assume that they are generally interchangeable between large and small file code... and that a large file descriptor will trigger errors when accessed with small file code where it may cause trouble (a really large file).
22 /* It mainly needs the official API ... */
23 /* ... but also some inside access (frame struct, readers). */
24 #include "mpg123lib_intern.h"
25 /* Include the system headers _after_ the implied config.h!
26 Otherwise _FILE_OFFSET_BITS is not in effect! */
34 Now, start off easy... translate simple API calls.
35 I need to deal with these here:
37 if(/^\s*MPG123_EXPORT\s+(\S+)\s+(mpg123_\S+)\((.*)\);\s*$/)
42 next unless ($type =~ /off_t/ or $args =~ /off_t/);
43 print "$name\n" unless grep {$_ eq $name}
44 ("mpg123_open", "mpg123_open_fd", "mpg123_open_handle", "mpg123_replace_reader", "mpg123_replace_reader_handle");
48 mpg123_framebyframe_decode
62 mpg123_decode_raw ... that's experimental.
64 Let's work on them in that order.
67 /* I see that I will need custom data storage. Main use is for the replaced I/O later, but the seek table for small file offsets needs extra storage, too. */
69 /* The wrapper handle for descriptor and handle I/O. */
71 /* The handle is used for nothing (0), or one of these two modes of operation: */
72 #define IO_FD 1 /* Wrapping over callbacks operation on integer file descriptor. */
73 #define IO_HANDLE 2 /* Wrapping over custom handle callbacks. */
77 /* Storage for small offset index table. */
79 /* I/O handle stuff */
80 int iotype
; /* IO_FD or IO_HANDLE */
83 int my_fd
; /* A descriptor that the wrapper code opened itself. */
84 /* The actual callbacks from the outside. */
85 ssize_t (*r_read
) (int, void *, size_t);
86 long (*r_lseek
)(int, long, int);
87 /* Data for IO_HANDLE. */
89 ssize_t (*r_h_read
)(void *, void *, size_t);
90 long (*r_h_lseek
)(void*, long, int);
91 void (*h_cleanup
)(void*);
95 /* Cleanup I/O part of the handle handle... but not deleting the wrapper handle itself.
96 That is stored in the frame and only deleted on mpg123_delete(). */
97 static void wrap_io_cleanup(void *handle
)
99 struct wrap_data
*ioh
= handle
;
100 if(ioh
->iotype
== IO_HANDLE
)
102 if(ioh
->h_cleanup
!= NULL
&& ioh
->handle
!= NULL
)
103 ioh
->h_cleanup(ioh
->handle
);
114 /* Really finish off the handle... freeing all memory. */
115 static void wrap_destroy(void *handle
)
117 struct wrap_data
*wh
= handle
;
118 wrap_io_cleanup(handle
);
119 if(wh
->indextable
!= NULL
)
120 free(wh
->indextable
);
125 /* More helper code... extract the special wrapper handle, possible allocate and initialize it. */
126 static struct wrap_data
* wrap_get(mpg123_handle
*mh
)
128 struct wrap_data
* whd
;
129 if(mh
== NULL
) return NULL
;
131 /* Access the private storage inside the mpg123 handle.
132 The real callback functions and handles are stored there. */
133 if(mh
->wrapperdata
== NULL
)
135 /* Create a new one. */
136 mh
->wrapperdata
= malloc(sizeof(struct wrap_data
));
137 if(mh
->wrapperdata
== NULL
)
139 mh
->err
= MPG123_OUT_OF_MEM
;
142 /* When we have wrapper data present, the callback for its proper cleanup is needed. */
143 mh
->wrapperclean
= wrap_destroy
;
145 whd
= mh
->wrapperdata
;
146 whd
->indextable
= NULL
;
153 whd
->r_h_read
= NULL
;
154 whd
->r_h_lseek
= NULL
;
155 whd
->h_cleanup
= NULL
;
157 else whd
= mh
->wrapperdata
;
162 /* After settling the data... start with some simple wrappers. */
164 #undef mpg123_decode_frame
165 /* int mpg123_decode_frame(mpg123_handle *mh, off_t *num, unsigned char **audio, size_t *bytes) */
166 int attribute_align_arg
mpg123_decode_frame(mpg123_handle
*mh
, long *num
, unsigned char **audio
, size_t *bytes
)
171 err
= MPG123_LARGENAME(mpg123_decode_frame
)(mh
, &largenum
, audio
, bytes
);
172 if(err
== MPG123_OK
&& num
!= NULL
)
177 mh
->err
= MPG123_LFS_OVERFLOW
;
184 #undef mpg123_framebyframe_decode
185 /* int mpg123_framebyframe_decode(mpg123_handle *mh, off_t *num, unsigned char **audio, size_t *bytes); */
186 int attribute_align_arg
mpg123_framebyframe_decode(mpg123_handle
*mh
, long *num
, unsigned char **audio
, size_t *bytes
)
191 err
= MPG123_LARGENAME(mpg123_framebyframe_decode
)(mh
, &largenum
, audio
, bytes
);
192 if(err
== MPG123_OK
&& num
!= NULL
)
197 mh
->err
= MPG123_LFS_OVERFLOW
;
204 #undef mpg123_framepos
205 /* off_t mpg123_framepos(mpg123_handle *mh); */
206 long attribute_align_arg
mpg123_framepos(mpg123_handle
*mh
)
211 largeval
= MPG123_LARGENAME(mpg123_framepos
)(mh
);
215 mh
->err
= MPG123_LFS_OVERFLOW
;
222 /* off_t mpg123_tell(mpg123_handle *mh); */
223 long attribute_align_arg
mpg123_tell(mpg123_handle
*mh
)
228 largeval
= MPG123_LARGENAME(mpg123_tell
)(mh
);
232 mh
->err
= MPG123_LFS_OVERFLOW
;
238 #undef mpg123_tellframe
239 /* off_t mpg123_tellframe(mpg123_handle *mh); */
240 long attribute_align_arg
mpg123_tellframe(mpg123_handle
*mh
)
245 largeval
= MPG123_LARGENAME(mpg123_tellframe
)(mh
);
249 mh
->err
= MPG123_LFS_OVERFLOW
;
255 #undef mpg123_tell_stream
256 /* off_t mpg123_tell_stream(mpg123_handle *mh); */
257 long attribute_align_arg
mpg123_tell_stream(mpg123_handle
*mh
)
262 largeval
= MPG123_LARGENAME(mpg123_tell_stream
)(mh
);
266 mh
->err
= MPG123_LFS_OVERFLOW
;
273 /* off_t mpg123_seek(mpg123_handle *mh, off_t sampleoff, int whence); */
274 long attribute_align_arg
mpg123_seek(mpg123_handle
*mh
, long sampleoff
, int whence
)
279 largeval
= MPG123_LARGENAME(mpg123_seek
)(mh
, sampleoff
, whence
);
283 mh
->err
= MPG123_LFS_OVERFLOW
;
289 #undef mpg123_feedseek
290 /* off_t mpg123_feedseek(mpg123_handle *mh, off_t sampleoff, int whence, off_t *input_offset); */
291 long attribute_align_arg
mpg123_feedseek(mpg123_handle
*mh
, long sampleoff
, int whence
, long *input_offset
)
297 largeval
= MPG123_LARGENAME(mpg123_feedseek
)(mh
, sampleoff
, whence
, &largeioff
);
298 /* Error/message codes are small... */
299 if(largeval
< 0) return (long)largeval
;
302 *input_offset
= largeioff
;
303 if(val
!= largeval
|| *input_offset
!= largeioff
)
305 mh
->err
= MPG123_LFS_OVERFLOW
;
311 #undef mpg123_seek_frame
312 /* off_t mpg123_seek_frame(mpg123_handle *mh, off_t frameoff, int whence); */
313 long attribute_align_arg
mpg123_seek_frame(mpg123_handle
*mh
, long frameoff
, int whence
)
318 largeval
= MPG123_LARGENAME(mpg123_seek_frame
)(mh
, frameoff
, whence
);
322 mh
->err
= MPG123_LFS_OVERFLOW
;
328 #undef mpg123_timeframe
329 /* off_t mpg123_timeframe(mpg123_handle *mh, double sec); */
330 long attribute_align_arg
mpg123_timeframe(mpg123_handle
*mh
, double sec
)
335 largeval
= MPG123_LARGENAME(mpg123_timeframe
)(mh
, sec
);
339 mh
->err
= MPG123_LFS_OVERFLOW
;
345 /* Now something less simple: Index retrieval and manipulation.
346 The index is an _array_ of off_t, which means that I need to construct a copy with translated long values. */
348 /* int mpg123_index(mpg123_handle *mh, off_t **offsets, off_t *step, size_t *fill) */
349 int attribute_align_arg
mpg123_index(mpg123_handle
*mh
, long **offsets
, long *step
, size_t *fill
)
357 struct wrap_data
*whd
;
360 if(whd
== NULL
) return MPG123_ERR
;
362 err
= MPG123_LARGENAME(mpg123_index
)(mh
, &largeoffsets
, &largestep
, &thefill
);
363 if(err
!= MPG123_OK
) return err
;
365 /* For a _very_ large file, even the step could overflow. */
366 smallstep
= largestep
;
367 if(smallstep
!= largestep
)
369 mh
->err
= MPG123_LFS_OVERFLOW
;
372 if(step
!= NULL
) *step
= smallstep
;
374 /* When there are no values stored, there is no table content to take care of.
375 Table pointer does not matter. Mission completed. */
376 if(thefill
== 0) return MPG123_OK
;
378 if(fill
!= NULL
) *fill
= thefill
;
380 /* Construct a copy of the index to hand over to the small-minded client. */
381 *offsets
= safe_realloc(whd
->indextable
, (*fill
)*sizeof(long));
384 mh
->err
= MPG123_OUT_OF_MEM
;
387 whd
->indextable
= *offsets
;
388 /* Elaborate conversion of each index value, with overflow check. */
389 for(i
=0; i
<*fill
; ++i
)
391 whd
->indextable
[i
] = largeoffsets
[i
];
392 if(whd
->indextable
[i
] != largeoffsets
[i
])
394 mh
->err
= MPG123_LFS_OVERFLOW
;
398 /* If we came that far... there should be a valid copy of the table now. */
402 /* The writing does basically the same than the above, just the opposite.
403 Oh, and the overflow checks are not needed -- off_t is bigger than long. */
404 #undef mpg123_set_index
405 /* int mpg123_set_index(mpg123_handle *mh, off_t *offsets, off_t step, size_t fill); */
406 int attribute_align_arg
mpg123_set_index(mpg123_handle
*mh
, long *offsets
, long step
, size_t fill
)
410 struct wrap_data
*whd
;
414 if(whd
== NULL
) return MPG123_ERR
;
416 /* Expensive temporary storage... for staying outside at the API layer. */
417 indextmp
= malloc(fill
*sizeof(off_t
));
420 mh
->err
= MPG123_OUT_OF_MEM
;
424 if(fill
> 0 && offsets
== NULL
)
426 mh
->err
= MPG123_BAD_INDEX_PAR
;
431 /* Fill the large-file copy of the provided index, then feed it to mpg123. */
432 for(i
=0; i
<fill
; ++i
)
433 indextmp
[i
] = offsets
[i
];
435 err
= MPG123_LARGENAME(mpg123_set_index
)(mh
, indextmp
, step
, fill
);
442 /* So... breathe... a couple of simple wrappers before the big mess. */
443 #undef mpg123_position
444 /* int mpg123_position( mpg123_handle *mh, off_t frame_offset, off_t buffered_bytes, off_t *current_frame, off_t *frames_left, double *current_seconds, double *seconds_left); */
445 int attribute_align_arg
mpg123_position(mpg123_handle
*mh
, long frame_offset
, long buffered_bytes
, long *current_frame
, long *frames_left
, double *current_seconds
, double *seconds_left
)
447 off_t curframe
, frameleft
;
448 long small_curframe
, small_frameleft
;
451 err
= MPG123_LARGENAME(mpg123_position
)(mh
, frame_offset
, buffered_bytes
, &curframe
, &frameleft
, current_seconds
, seconds_left
);
452 if(err
!= MPG123_OK
) return err
;
454 small_curframe
= curframe
;
455 small_frameleft
= frameleft
;
456 if(small_curframe
!= curframe
|| small_frameleft
!= frameleft
)
458 mh
->err
= MPG123_LFS_OVERFLOW
;
462 if(current_frame
!= NULL
) *current_frame
= small_curframe
;
464 if(frames_left
!= NULL
) *frames_left
= small_frameleft
;
470 #undef mpg123_framelength
471 /* off_t mpg123_framelength(mpg123_handle *mh); */
472 long attribute_align_arg
mpg123_framelength(mpg123_handle
*mh
)
477 largeval
= MPG123_LARGENAME(mpg123_framelength
)(mh
);
481 mh
->err
= MPG123_LFS_OVERFLOW
;
488 /* off_t mpg123_length(mpg123_handle *mh); */
489 long attribute_align_arg
mpg123_length(mpg123_handle
*mh
)
494 largeval
= MPG123_LARGENAME(mpg123_length
)(mh
);
498 mh
->err
= MPG123_LFS_OVERFLOW
;
504 /* The simplest wrapper of all... */
505 #undef mpg123_set_filesize
506 /* int mpg123_set_filesize(mpg123_handle *mh, off_t size); */
507 int attribute_align_arg
mpg123_set_filesize(mpg123_handle
*mh
, long size
)
509 return MPG123_LARGENAME(mpg123_set_filesize
)(mh
, size
);
513 /* =========================================
514 THE BOUNDARY OF SANITY
516 ========================================= */
520 The messy part: Replacement of I/O core (actally, this is only due to lseek()).
521 Both descriptor and handle replaced I/O are mapped to replaced handle I/O, the handle wrapping over the actual callbacks and the actual handle/descriptor.
522 You got multiple levels of handles and callbacks to think about. Have fun reading and comprehending.
525 /* Could go into compat.h ... Windows needs that flag. */
530 /* Read callback needs nothing special. */
531 ssize_t
wrap_read(void* handle
, void *buf
, size_t count
)
533 struct wrap_data
*ioh
= handle
;
536 case IO_FD
: return ioh
->r_read(ioh
->fd
, buf
, count
);
537 case IO_HANDLE
: return ioh
->r_h_read(ioh
->handle
, buf
, count
);
539 error("Serious breakage - bad IO type in LFS wrapper!");
543 /* Seek callback needs protection from too big offsets. */
544 off_t
wrap_lseek(void *handle
, off_t offset
, int whence
)
546 struct wrap_data
*ioh
= handle
;
547 long smalloff
= offset
;
548 if(smalloff
== offset
)
552 case IO_FD
: return ioh
->r_lseek(ioh
->fd
, smalloff
, whence
);
553 case IO_HANDLE
: return ioh
->r_h_lseek(ioh
->handle
, smalloff
, whence
);
555 error("Serious breakage - bad IO type in LFS wrapper!");
567 Now, let's replace the API dealing with replacement I/O.
568 Start with undefining the renames...
571 #undef mpg123_replace_reader
572 #undef mpg123_replace_reader_handle
574 #undef mpg123_open_fd
575 #undef mpg123_open_handle
578 /* Normal reader replacement needs fallback implementations. */
579 static ssize_t
fallback_read(int fd
, void *buf
, size_t count
)
581 return read(fd
, buf
, count
);
584 static long fallback_lseek(int fd
, long offset
, int whence
)
586 /* Since the offset is long int already, the returned value really should fit into a long... but whatever. */
589 newpos
= lseek(fd
, offset
, whence
);
590 newpos_long
= newpos
;
591 if(newpos_long
== newpos
)
600 /* Reader replacement prepares the hidden handle storage for next mpg123_open_fd() or plain mpg123_open(). */
601 int attribute_align_arg
mpg123_replace_reader(mpg123_handle
*mh
, ssize_t (*r_read
) (int, void *, size_t), long (*r_lseek
)(int, long, int) )
603 struct wrap_data
* ioh
;
605 if(mh
== NULL
) return MPG123_ERR
;
609 if(ioh
== NULL
) return MPG123_ERR
;
611 /* If both callbacks are NULL, switch totally to internal I/O, else just use fallback for at most half of them. */
612 if(r_read
== NULL
&& r_lseek
== NULL
)
614 /* Only the type is actually important to disable the code. */
623 ioh
->fd
= -1; /* On next mpg123_open_fd(), this gets a value. */
624 ioh
->r_read
= r_read
!= NULL
? r_read
: fallback_read
;
625 ioh
->r_lseek
= r_lseek
!= NULL
? r_lseek
: fallback_lseek
;
628 /* The real reader replacement will happen while opening. */
632 int attribute_align_arg
mpg123_replace_reader_handle(mpg123_handle
*mh
, ssize_t (*r_read
) (void*, void *, size_t), long (*r_lseek
)(void*, long, int), void (*cleanup
)(void*))
634 struct wrap_data
* ioh
;
636 if(mh
== NULL
) return MPG123_ERR
;
640 if(ioh
== NULL
) return MPG123_ERR
;
642 ioh
->iotype
= IO_HANDLE
;
644 ioh
->r_h_read
= r_read
;
645 ioh
->r_h_lseek
= r_lseek
;
646 ioh
->h_cleanup
= cleanup
;
648 /* The real reader replacement will happen while opening. */
653 The open routines always need to watch out for a prepared wrapper handle to use replaced normal I/O.
654 Two cases to consider:
655 1. Plain normal open using internal I/O.
656 2. Client called mpg123_replace_reader() before.
657 The second case needs hackery to activate the client I/O callbacks. For that, we create a custom I/O handle and use the guts of mpg123_open_fd() on it.
659 int attribute_align_arg
mpg123_open(mpg123_handle
*mh
, const char *path
)
661 struct wrap_data
* ioh
;
663 if(mh
== NULL
) return MPG123_ERR
;
665 ioh
= mh
->wrapperdata
;
666 /* Mimic the use of mpg123_replace_reader() functions by lower levels...
667 IO_HANDLE is not valid here, though. Only IO_FD. */
668 if(ioh
!= NULL
&& ioh
->iotype
== IO_FD
)
671 err
= MPG123_LARGENAME(mpg123_replace_reader_handle
)(mh
, wrap_read
, wrap_lseek
, wrap_io_cleanup
);
672 if(err
!= MPG123_OK
) return MPG123_ERR
;
674 /* The above call implied mpg123_close() already */
676 I really need to open the file here... to be able to use the replacer handle I/O ...
677 my_fd is used to indicate closing of the descriptor on cleanup.
679 ioh
->my_fd
= compat_open(path
, O_RDONLY
|O_BINARY
);
682 if(!(mh
->p
.flags
& MPG123_QUIET
)) error2("Cannot open file %s: %s", path
, strerror(errno
));
684 mh
->err
= MPG123_BAD_FILE
;
687 /* Store a copy of the descriptor where it is actually used. */
688 ioh
->fd
= ioh
->my_fd
;
689 /* Initiate I/O operating on my handle now. */
690 err
= open_stream_handle(mh
, ioh
);
693 wrap_io_cleanup(ioh
);
699 else return MPG123_LARGENAME(mpg123_open
)(mh
, path
);
703 This is in fact very similar to the above:
704 The open routines always need to watch out for a prepared wrapper handle to use replaced normal I/O.
705 Two cases to consider:
706 1. Plain normal open_fd using internal I/O.
707 2. Client called mpg123_replace_reader() before.
708 The second case needs hackery to activate the client I/O callbacks. For that, we create a custom I/O handle and use the guts of mpg123_open_fd() on it.
711 int attribute_align_arg
mpg123_open_fd(mpg123_handle
*mh
, int fd
)
713 struct wrap_data
* ioh
;
715 if(mh
== NULL
) return MPG123_ERR
;
718 ioh
= mh
->wrapperdata
;
719 if(ioh
!= NULL
&& ioh
->iotype
== IO_FD
)
722 err
= MPG123_LARGENAME(mpg123_replace_reader_handle
)(mh
, wrap_read
, wrap_lseek
, wrap_io_cleanup
);
723 if(err
!= MPG123_OK
) return MPG123_ERR
;
725 /* The above call implied mpg123_close() already */
727 /* Store the real file descriptor inside the handle. */
729 /* Initiate I/O operating on my handle now. */
730 err
= open_stream_handle(mh
, ioh
);
733 wrap_io_cleanup(ioh
);
739 else return MPG123_LARGENAME(mpg123_open_fd
)(mh
, fd
);
742 int attribute_align_arg
mpg123_open_handle(mpg123_handle
*mh
, void *handle
)
744 struct wrap_data
* ioh
;
746 if(mh
== NULL
) return MPG123_ERR
;
749 ioh
= mh
->wrapperdata
;
750 if(ioh
!= NULL
&& ioh
->iotype
== IO_HANDLE
&& ioh
->r_h_read
!= NULL
)
752 /* Wrap the custom handle into my handle. */
754 err
= MPG123_LARGENAME(mpg123_replace_reader_handle
)(mh
, wrap_read
, wrap_lseek
, wrap_io_cleanup
);
755 if(err
!= MPG123_OK
) return MPG123_ERR
;
757 ioh
->handle
= handle
;
758 /* No extra error handling, keep behaviour of the original open_handle. */
759 return open_stream_handle(mh
, ioh
);
763 /* This is an error ... you need to prepare the I/O before using it. */
764 mh
->err
= MPG123_BAD_CUSTOM_IO
;