123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367 |
- /********************************************************************
- * *
- * THIS FILE IS PART OF THE libopusfile SOFTWARE CODEC SOURCE CODE. *
- * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
- * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
- * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
- * *
- * THE libopusfile SOURCE CODE IS (C) COPYRIGHT 1994-2012 *
- * by the Xiph.Org Foundation and contributors http://www.xiph.org/ *
- * *
- ********************************************************************
- function: stdio-based convenience library for opening/seeking/decoding
- last mod: $Id: vorbisfile.c 17573 2010-10-27 14:53:59Z xiphmont $
- ********************************************************************/
- #ifdef HAVE_CONFIG_H
- #include "config.h"
- #endif
- #include "internal.h"
- #include <sys/types.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <errno.h>
- #include <string.h>
- #if defined(_WIN32)
- # include <io.h>
- #endif
- typedef struct OpusMemStream OpusMemStream;
- #define OP_MEM_SIZE_MAX (~(size_t)0>>1)
- #define OP_MEM_DIFF_MAX ((ptrdiff_t)OP_MEM_SIZE_MAX)
- /*The context information needed to read from a block of memory as if it were a
- file.*/
- struct OpusMemStream{
- /*The block of memory to read from.*/
- const unsigned char *data;
- /*The total size of the block.
- This must be at most OP_MEM_SIZE_MAX to prevent signed overflow while
- seeking.*/
- ptrdiff_t size;
- /*The current file position.
- This is allowed to be set arbitrarily greater than size (i.e., past the end
- of the block, though we will not read data past the end of the block), but
- is not allowed to be negative (i.e., before the beginning of the block).*/
- ptrdiff_t pos;
- };
- static int op_fread(void *_stream,unsigned char *_ptr,int _buf_size){
- FILE *stream;
- size_t ret;
- /*Check for empty read.*/
- if(_buf_size<=0)return 0;
- stream=(FILE *)_stream;
- ret=fread(_ptr,1,_buf_size,stream);
- OP_ASSERT(ret<=(size_t)_buf_size);
- /*If ret==0 and !feof(stream), there was a read error.*/
- return ret>0||feof(stream)?(int)ret:OP_EREAD;
- }
- static int op_fseek(void *_stream,opus_int64 _offset,int _whence){
- #if defined(_WIN32)
- /*_fseeki64() is not exposed until MSCVCRT80.
- This is the default starting with MSVC 2005 (_MSC_VER>=1400), but we want
- to allow linking against older MSVCRT versions for compatibility back to
- XP without installing extra runtime libraries.
- i686-pc-mingw32 does not have fseeko() and requires
- __MSVCRT_VERSION__>=0x800 for _fseeki64(), which screws up linking with
- other libraries (that don't use MSVCRT80 from MSVC 2005 by default).
- i686-w64-mingw32 does have fseeko() and respects _FILE_OFFSET_BITS, but I
- don't know how to detect that at compile time.
- We could just use fseeko64() (which is available in both), but its
- implemented using fgetpos()/fsetpos() just like this code, except without
- the overflow checking, so we prefer our version.*/
- opus_int64 pos;
- /*We don't use fpos_t directly because it might be a struct if __STDC__ is
- non-zero or _INTEGRAL_MAX_BITS < 64.
- I'm not certain when the latter is true, but someone could in theory set
- the former.
- Either way, it should be binary compatible with a normal 64-bit int (this
- assumption is not portable, but I believe it is true for MSVCRT).*/
- OP_ASSERT(sizeof(pos)==sizeof(fpos_t));
- /*Translate the seek to an absolute one.*/
- if(_whence==SEEK_CUR){
- int ret;
- ret=fgetpos((FILE *)_stream,(fpos_t *)&pos);
- if(ret)return ret;
- }
- else if(_whence==SEEK_END)pos=_filelengthi64(_fileno((FILE *)_stream));
- else if(_whence==SEEK_SET)pos=0;
- else return -1;
- /*Check for errors or overflow.*/
- if(pos<0||_offset<-pos||_offset>OP_INT64_MAX-pos)return -1;
- pos+=_offset;
- return fsetpos((FILE *)_stream,(fpos_t *)&pos);
- #else
- /*This function actually conforms to the SUSv2 and POSIX.1-2001, so we prefer
- it except on Windows.*/
- return fseeko((FILE *)_stream,(off_t)_offset,_whence);
- #endif
- }
- static opus_int64 op_ftell(void *_stream){
- #if defined(_WIN32)
- /*_ftelli64() is not exposed until MSCVCRT80, and ftello()/ftello64() have
- the same problems as fseeko()/fseeko64() in MingW.
- See above for a more detailed explanation.*/
- opus_int64 pos;
- OP_ASSERT(sizeof(pos)==sizeof(fpos_t));
- return fgetpos((FILE *)_stream,(fpos_t *)&pos)?-1:pos;
- #else
- /*This function actually conforms to the SUSv2 and POSIX.1-2001, so we prefer
- it except on Windows.*/
- return ftello((FILE *)_stream);
- #endif
- }
- static const OpusFileCallbacks OP_FILE_CALLBACKS={
- op_fread,
- op_fseek,
- op_ftell,
- (op_close_func)fclose
- };
- #if defined(_WIN32)
- # include <stddef.h>
- # include <errno.h>
- /*Windows doesn't accept UTF-8 by default, and we don't have a wchar_t API,
- so if we just pass the path to fopen(), then there'd be no way for a user
- of our API to open a Unicode filename.
- Instead, we translate from UTF-8 to UTF-16 and use Windows' wchar_t API.
- This makes this API more consistent with platforms where the character set
- used by fopen is the same as used on disk, which is generally UTF-8, and
- with our metadata API, which always uses UTF-8.*/
- static wchar_t *op_utf8_to_utf16(const char *_src){
- wchar_t *dst;
- size_t len;
- len=strlen(_src);
- /*Worst-case output is 1 wide character per 1 input character.*/
- dst=(wchar_t *)_ogg_malloc(sizeof(*dst)*(len+1));
- if(dst!=NULL){
- size_t si;
- size_t di;
- for(di=si=0;si<len;si++){
- int c0;
- c0=(unsigned char)_src[si];
- if(!(c0&0x80)){
- /*Start byte says this is a 1-byte sequence.*/
- dst[di++]=(wchar_t)c0;
- continue;
- }
- else{
- int c1;
- /*This is safe, because c0 was not 0 and _src is NUL-terminated.*/
- c1=(unsigned char)_src[si+1];
- if((c1&0xC0)==0x80){
- /*Found at least one continuation byte.*/
- if((c0&0xE0)==0xC0){
- wchar_t w;
- /*Start byte says this is a 2-byte sequence.*/
- w=(c0&0x1F)<<6|c1&0x3F;
- if(w>=0x80U){
- /*This is a 2-byte sequence that is not overlong.*/
- dst[di++]=w;
- si++;
- continue;
- }
- }
- else{
- int c2;
- /*This is safe, because c1 was not 0 and _src is NUL-terminated.*/
- c2=(unsigned char)_src[si+2];
- if((c2&0xC0)==0x80){
- /*Found at least two continuation bytes.*/
- if((c0&0xF0)==0xE0){
- wchar_t w;
- /*Start byte says this is a 3-byte sequence.*/
- w=(c0&0xF)<<12|(c1&0x3F)<<6|c2&0x3F;
- if(w>=0x800U&&(w<0xD800||w>=0xE000)&&w<0xFFFE){
- /*This is a 3-byte sequence that is not overlong, not a
- UTF-16 surrogate pair value, and not a 'not a character'
- value.*/
- dst[di++]=w;
- si+=2;
- continue;
- }
- }
- else{
- int c3;
- /*This is safe, because c2 was not 0 and _src is
- NUL-terminated.*/
- c3=(unsigned char)_src[si+3];
- if((c3&0xC0)==0x80){
- /*Found at least three continuation bytes.*/
- if((c0&0xF8)==0xF0){
- opus_uint32 w;
- /*Start byte says this is a 4-byte sequence.*/
- w=(c0&7)<<18|(c1&0x3F)<<12|(c2&0x3F)<<6&(c3&0x3F);
- if(w>=0x10000U&&w<0x110000U){
- /*This is a 4-byte sequence that is not overlong and not
- greater than the largest valid Unicode code point.
- Convert it to a surrogate pair.*/
- w-=0x10000;
- dst[di++]=(wchar_t)(0xD800+(w>>10));
- dst[di++]=(wchar_t)(0xDC00+(w&0x3FF));
- si+=3;
- continue;
- }
- }
- }
- }
- }
- }
- }
- }
- /*If we got here, we encountered an illegal UTF-8 sequence.*/
- _ogg_free(dst);
- return NULL;
- }
- OP_ASSERT(di<=len);
- dst[di]='\0';
- }
- return dst;
- }
- #endif
- void *op_fopen(OpusFileCallbacks *_cb,const char *_path,const char *_mode){
- FILE *fp;
- #if !defined(_WIN32)
- fp=fopen(_path,_mode);
- #else
- fp=NULL;
- if(_path==NULL||_mode==NULL)errno=EINVAL;
- else{
- wchar_t *wpath;
- wchar_t *wmode;
- wpath=op_utf8_to_utf16(_path);
- wmode=op_utf8_to_utf16(_mode);
- if(wmode==NULL)errno=EINVAL;
- else if(wpath==NULL)errno=ENOENT;
- else fp=_wfopen(wpath,wmode);
- _ogg_free(wmode);
- _ogg_free(wpath);
- }
- #endif
- if(fp!=NULL)*_cb=*&OP_FILE_CALLBACKS;
- return fp;
- }
- void *op_fdopen(OpusFileCallbacks *_cb,int _fd,const char *_mode){
- FILE *fp;
- fp=fdopen(_fd,_mode);
- if(fp!=NULL)*_cb=*&OP_FILE_CALLBACKS;
- return fp;
- }
- void *op_freopen(OpusFileCallbacks *_cb,const char *_path,const char *_mode,
- void *_stream){
- FILE *fp;
- #if !defined(_WIN32)
- fp=freopen(_path,_mode,(FILE *)_stream);
- #else
- fp=NULL;
- if(_path==NULL||_mode==NULL)errno=EINVAL;
- else{
- wchar_t *wpath;
- wchar_t *wmode;
- wpath=op_utf8_to_utf16(_path);
- wmode=op_utf8_to_utf16(_mode);
- if(wmode==NULL)errno=EINVAL;
- else if(wpath==NULL)errno=ENOENT;
- else fp=_wfreopen(wpath,wmode,(FILE *)_stream);
- _ogg_free(wmode);
- _ogg_free(wpath);
- }
- #endif
- if(fp!=NULL)*_cb=*&OP_FILE_CALLBACKS;
- return fp;
- }
- static int op_mem_read(void *_stream,unsigned char *_ptr,int _buf_size){
- OpusMemStream *stream;
- ptrdiff_t size;
- ptrdiff_t pos;
- stream=(OpusMemStream *)_stream;
- /*Check for empty read.*/
- if(_buf_size<=0)return 0;
- size=stream->size;
- pos=stream->pos;
- /*Check for EOF.*/
- if(pos>=size)return 0;
- /*Check for a short read.*/
- _buf_size=(int)OP_MIN(size-pos,_buf_size);
- memcpy(_ptr,stream->data+pos,_buf_size);
- pos+=_buf_size;
- stream->pos=pos;
- return _buf_size;
- }
- static int op_mem_seek(void *_stream,opus_int64 _offset,int _whence){
- OpusMemStream *stream;
- ptrdiff_t pos;
- stream=(OpusMemStream *)_stream;
- pos=stream->pos;
- OP_ASSERT(pos>=0);
- switch(_whence){
- case SEEK_SET:{
- /*Check for overflow:*/
- if(_offset<0||_offset>OP_MEM_DIFF_MAX)return -1;
- pos=(ptrdiff_t)_offset;
- }break;
- case SEEK_CUR:{
- /*Check for overflow:*/
- if(_offset<-pos||_offset>OP_MEM_DIFF_MAX-pos)return -1;
- pos=(ptrdiff_t)(pos+_offset);
- }break;
- case SEEK_END:{
- ptrdiff_t size;
- size=stream->size;
- OP_ASSERT(size>=0);
- /*Check for overflow:*/
- if(_offset>size||_offset<size-OP_MEM_DIFF_MAX)return -1;
- pos=(ptrdiff_t)(size-_offset);
- }break;
- default:return -1;
- }
- stream->pos=pos;
- return 0;
- }
- static opus_int64 op_mem_tell(void *_stream){
- OpusMemStream *stream;
- stream=(OpusMemStream *)_stream;
- return (ogg_int64_t)stream->pos;
- }
- static int op_mem_close(void *_stream){
- _ogg_free(_stream);
- return 0;
- }
- static const OpusFileCallbacks OP_MEM_CALLBACKS={
- op_mem_read,
- op_mem_seek,
- op_mem_tell,
- op_mem_close
- };
- void *op_mem_stream_create(OpusFileCallbacks *_cb,
- const unsigned char *_data,size_t _size){
- OpusMemStream *stream;
- if(_size>OP_MEM_SIZE_MAX)return NULL;
- stream=(OpusMemStream *)_ogg_malloc(sizeof(*stream));
- if(stream!=NULL){
- *_cb=*&OP_MEM_CALLBACKS;
- stream->data=_data;
- stream->size=_size;
- stream->pos=0;
- }
- return stream;
- }
|