| 
									
										
										
										
											2015-10-02 14:20:50 -03:00
										 |  |  | /********************************************************************
 | 
					
						
							|  |  |  |  *                                                                  * | 
					
						
							|  |  |  |  * 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 $ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |  ********************************************************************/ | 
					
						
							| 
									
										
										
										
											2015-11-27 19:29:48 -02:00
										 |  |  | #include "opus/opus_config.h"
 | 
					
						
							| 
									
										
										
										
											2015-10-02 14:20:50 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-27 19:29:48 -02:00
										 |  |  | #include "opus/internal.h"
 | 
					
						
							| 
									
										
										
										
											2015-10-02 14:20:50 -03:00
										 |  |  | #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; | 
					
						
							|  |  |  | } |