stream.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  1. /********************************************************************
  2. * *
  3. * THIS FILE IS PART OF THE libopusfile SOFTWARE CODEC SOURCE CODE. *
  4. * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
  5. * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
  6. * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
  7. * *
  8. * THE libopusfile SOURCE CODE IS (C) COPYRIGHT 1994-2018 *
  9. * by the Xiph.Org Foundation and contributors https://xiph.org/ *
  10. * *
  11. ********************************************************************
  12. function: stdio-based convenience library for opening/seeking/decoding
  13. last mod: $Id: vorbisfile.c 17573 2010-10-27 14:53:59Z xiphmont $
  14. ********************************************************************/
  15. #ifdef HAVE_CONFIG_H
  16. #include "config.h"
  17. #endif
  18. #include "internal.h"
  19. #include <sys/types.h>
  20. #include <stdio.h>
  21. #include <stdlib.h>
  22. #include <errno.h>
  23. #include <string.h>
  24. #if defined(_WIN32)
  25. # include <io.h>
  26. #endif
  27. typedef struct OpusMemStream OpusMemStream;
  28. #define OP_MEM_SIZE_MAX (~(size_t)0>>1)
  29. #define OP_MEM_DIFF_MAX ((ptrdiff_t)OP_MEM_SIZE_MAX)
  30. /*The context information needed to read from a block of memory as if it were a
  31. file.*/
  32. struct OpusMemStream{
  33. /*The block of memory to read from.*/
  34. const unsigned char *data;
  35. /*The total size of the block.
  36. This must be at most OP_MEM_SIZE_MAX to prevent signed overflow while
  37. seeking.*/
  38. ptrdiff_t size;
  39. /*The current file position.
  40. This is allowed to be set arbitrarily greater than size (i.e., past the end
  41. of the block, though we will not read data past the end of the block), but
  42. is not allowed to be negative (i.e., before the beginning of the block).*/
  43. ptrdiff_t pos;
  44. };
  45. static int op_fread(void *_stream,unsigned char *_ptr,int _buf_size){
  46. FILE *stream;
  47. size_t ret;
  48. /*Check for empty read.*/
  49. if(_buf_size<=0)return 0;
  50. stream=(FILE *)_stream;
  51. ret=fread(_ptr,1,_buf_size,stream);
  52. OP_ASSERT(ret<=(size_t)_buf_size);
  53. /*If ret==0 and !feof(stream), there was a read error.*/
  54. return ret>0||feof(stream)?(int)ret:OP_EREAD;
  55. }
  56. static int op_fseek(void *_stream,opus_int64 _offset,int _whence){
  57. #if defined(_WIN32)
  58. /*_fseeki64() is not exposed until MSVCRT80.
  59. This is the default starting with MSVC 2005 (_MSC_VER>=1400), but we want
  60. to allow linking against older MSVCRT versions for compatibility back to
  61. XP without installing extra runtime libraries.
  62. i686-pc-mingw32 does not have fseeko() and requires
  63. __MSVCRT_VERSION__>=0x800 for _fseeki64(), which screws up linking with
  64. other libraries (that don't use MSVCRT80 from MSVC 2005 by default).
  65. i686-w64-mingw32 does have fseeko() and respects _FILE_OFFSET_BITS, but I
  66. don't know how to detect that at compile time.
  67. We could just use fseeko64() (which is available in both), but it's
  68. implemented using fgetpos()/fsetpos() just like this code, except without
  69. the overflow checking, so we prefer our version.*/
  70. opus_int64 pos;
  71. /*We don't use fpos_t directly because it might be a struct if __STDC__ is
  72. non-zero or _INTEGRAL_MAX_BITS < 64.
  73. I'm not certain when the latter is true, but someone could in theory set
  74. the former.
  75. Either way, it should be binary compatible with a normal 64-bit int (this
  76. assumption is not portable, but I believe it is true for MSVCRT).*/
  77. OP_ASSERT(sizeof(pos)==sizeof(fpos_t));
  78. /*Translate the seek to an absolute one.*/
  79. if(_whence==SEEK_CUR){
  80. int ret;
  81. ret=fgetpos((FILE *)_stream,(fpos_t *)&pos);
  82. if(ret)return ret;
  83. }
  84. else if(_whence==SEEK_END)pos=_filelengthi64(_fileno((FILE *)_stream));
  85. else if(_whence==SEEK_SET)pos=0;
  86. else return -1;
  87. /*Check for errors or overflow.*/
  88. if(pos<0||_offset<-pos||_offset>OP_INT64_MAX-pos)return -1;
  89. pos+=_offset;
  90. return fsetpos((FILE *)_stream,(fpos_t *)&pos);
  91. #else
  92. /*This function actually conforms to the SUSv2 and POSIX.1-2001, so we prefer
  93. it except on Windows.*/
  94. return fseeko((FILE *)_stream,(off_t)_offset,_whence);
  95. #endif
  96. }
  97. static opus_int64 op_ftell(void *_stream){
  98. #if defined(_WIN32)
  99. /*_ftelli64() is not exposed until MSVCRT80, and ftello()/ftello64() have
  100. the same problems as fseeko()/fseeko64() in MingW.
  101. See above for a more detailed explanation.*/
  102. opus_int64 pos;
  103. OP_ASSERT(sizeof(pos)==sizeof(fpos_t));
  104. return fgetpos((FILE *)_stream,(fpos_t *)&pos)?-1:pos;
  105. #else
  106. /*This function actually conforms to the SUSv2 and POSIX.1-2001, so we prefer
  107. it except on Windows.*/
  108. return ftello((FILE *)_stream);
  109. #endif
  110. }
  111. static const OpusFileCallbacks OP_FILE_CALLBACKS={
  112. op_fread,
  113. op_fseek,
  114. op_ftell,
  115. (op_close_func)fclose
  116. };
  117. #if defined(_WIN32)
  118. # include <stddef.h>
  119. # include <errno.h>
  120. /*Windows doesn't accept UTF-8 by default, and we don't have a wchar_t API,
  121. so if we just pass the path to fopen(), then there'd be no way for a user
  122. of our API to open a Unicode filename.
  123. Instead, we translate from UTF-8 to UTF-16 and use Windows' wchar_t API.
  124. This makes this API more consistent with platforms where the character set
  125. used by fopen is the same as used on disk, which is generally UTF-8, and
  126. with our metadata API, which always uses UTF-8.*/
  127. static wchar_t *op_utf8_to_utf16(const char *_src){
  128. wchar_t *dst;
  129. size_t len;
  130. len=strlen(_src);
  131. /*Worst-case output is 1 wide character per 1 input character.*/
  132. dst=(wchar_t *)_ogg_malloc(sizeof(*dst)*(len+1));
  133. if(dst!=NULL){
  134. size_t si;
  135. size_t di;
  136. for(di=si=0;si<len;si++){
  137. int c0;
  138. c0=(unsigned char)_src[si];
  139. if(!(c0&0x80)){
  140. /*Start byte says this is a 1-byte sequence.*/
  141. dst[di++]=(wchar_t)c0;
  142. continue;
  143. }
  144. else{
  145. int c1;
  146. /*This is safe, because c0 was not 0 and _src is NUL-terminated.*/
  147. c1=(unsigned char)_src[si+1];
  148. if((c1&0xC0)==0x80){
  149. /*Found at least one continuation byte.*/
  150. if((c0&0xE0)==0xC0){
  151. wchar_t w;
  152. /*Start byte says this is a 2-byte sequence.*/
  153. w=(c0&0x1F)<<6|c1&0x3F;
  154. if(w>=0x80U){
  155. /*This is a 2-byte sequence that is not overlong.*/
  156. dst[di++]=w;
  157. si++;
  158. continue;
  159. }
  160. }
  161. else{
  162. int c2;
  163. /*This is safe, because c1 was not 0 and _src is NUL-terminated.*/
  164. c2=(unsigned char)_src[si+2];
  165. if((c2&0xC0)==0x80){
  166. /*Found at least two continuation bytes.*/
  167. if((c0&0xF0)==0xE0){
  168. wchar_t w;
  169. /*Start byte says this is a 3-byte sequence.*/
  170. w=(c0&0xF)<<12|(c1&0x3F)<<6|c2&0x3F;
  171. if(w>=0x800U&&(w<0xD800||w>=0xE000)&&w<0xFFFE){
  172. /*This is a 3-byte sequence that is not overlong, not a
  173. UTF-16 surrogate pair value, and not a 'not a character'
  174. value.*/
  175. dst[di++]=w;
  176. si+=2;
  177. continue;
  178. }
  179. }
  180. else{
  181. int c3;
  182. /*This is safe, because c2 was not 0 and _src is
  183. NUL-terminated.*/
  184. c3=(unsigned char)_src[si+3];
  185. if((c3&0xC0)==0x80){
  186. /*Found at least three continuation bytes.*/
  187. if((c0&0xF8)==0xF0){
  188. opus_uint32 w;
  189. /*Start byte says this is a 4-byte sequence.*/
  190. w=(c0&7)<<18|(c1&0x3F)<<12|(c2&0x3F)<<6&(c3&0x3F);
  191. if(w>=0x10000U&&w<0x110000U){
  192. /*This is a 4-byte sequence that is not overlong and not
  193. greater than the largest valid Unicode code point.
  194. Convert it to a surrogate pair.*/
  195. w-=0x10000;
  196. dst[di++]=(wchar_t)(0xD800+(w>>10));
  197. dst[di++]=(wchar_t)(0xDC00+(w&0x3FF));
  198. si+=3;
  199. continue;
  200. }
  201. }
  202. }
  203. }
  204. }
  205. }
  206. }
  207. }
  208. /*If we got here, we encountered an illegal UTF-8 sequence.*/
  209. _ogg_free(dst);
  210. return NULL;
  211. }
  212. OP_ASSERT(di<=len);
  213. dst[di]='\0';
  214. }
  215. return dst;
  216. }
  217. /*fsetpos() internally dispatches to the win32 API call SetFilePointer().
  218. According to SetFilePointer()'s documentation [0], the behavior is
  219. undefined if you do not call it on "a file stored on a seeking device".
  220. However, none of the MSVCRT seeking functions verify what kind of file is
  221. being used before calling it (which I believe is a bug, since they are
  222. supposed to fail and return an error, but it is a bug that has been there
  223. for multiple decades now).
  224. In practice, SetFilePointer() appears to succeed for things like stdin,
  225. even when you are not just piping in a regular file, which prevents the use
  226. of this API to determine whether it is possible to seek in a file at all.
  227. Therefore, we take the approach recommended by the SetFilePointer()
  228. documentation and confirm the type of file using GetFileType() first.
  229. We do this once, when the file is opened, and return the corresponding
  230. callback in order to avoid an extra win32 API call on every seek in the
  231. common case.
  232. Hopefully the return value of GetFileType() cannot actually change for the
  233. lifetime of a file handle.
  234. [0] https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-setfilepointer
  235. */
  236. static int op_fseek_fail(void *_stream,opus_int64 _offset,int _whence){
  237. (void)_stream;
  238. (void)_offset;
  239. (void)_whence;
  240. return -1;
  241. }
  242. static const OpusFileCallbacks OP_UNSEEKABLE_FILE_CALLBACKS={
  243. op_fread,
  244. op_fseek_fail,
  245. op_ftell,
  246. (op_close_func)fclose
  247. };
  248. # define WIN32_LEAN_AND_MEAN
  249. # define WIN32_EXTRA_LEAN
  250. # include <windows.h>
  251. static const OpusFileCallbacks *op_get_file_callbacks(FILE *_fp){
  252. intptr_t h_file;
  253. h_file=_get_osfhandle(_fileno(_fp));
  254. if(h_file!=-1
  255. &&(GetFileType((HANDLE)h_file)&~FILE_TYPE_REMOTE)==FILE_TYPE_DISK){
  256. return &OP_FILE_CALLBACKS;
  257. }
  258. return &OP_UNSEEKABLE_FILE_CALLBACKS;
  259. }
  260. #else
  261. static const OpusFileCallbacks *op_get_file_callbacks(FILE *_fp){
  262. (void)_fp;
  263. return &OP_FILE_CALLBACKS;
  264. }
  265. #endif
  266. void *op_fopen(OpusFileCallbacks *_cb,const char *_path,const char *_mode){
  267. FILE *fp;
  268. #if !defined(_WIN32)
  269. fp=fopen(_path,_mode);
  270. #else
  271. fp=NULL;
  272. {
  273. wchar_t *wpath;
  274. wchar_t *wmode;
  275. wpath=op_utf8_to_utf16(_path);
  276. wmode=op_utf8_to_utf16(_mode);
  277. if(wmode==NULL)errno=EINVAL;
  278. else if(wpath==NULL)errno=ENOENT;
  279. else fp=_wfopen(wpath,wmode);
  280. _ogg_free(wmode);
  281. _ogg_free(wpath);
  282. }
  283. #endif
  284. if(fp!=NULL)*_cb=*op_get_file_callbacks(fp);
  285. return fp;
  286. }
  287. void *op_fdopen(OpusFileCallbacks *_cb,int _fd,const char *_mode){
  288. FILE *fp;
  289. fp=fdopen(_fd,_mode);
  290. if(fp!=NULL)*_cb=*op_get_file_callbacks(fp);
  291. return fp;
  292. }
  293. void *op_freopen(OpusFileCallbacks *_cb,const char *_path,const char *_mode,
  294. void *_stream){
  295. FILE *fp;
  296. #if !defined(_WIN32)
  297. fp=freopen(_path,_mode,(FILE *)_stream);
  298. #else
  299. fp=NULL;
  300. {
  301. wchar_t *wpath;
  302. wchar_t *wmode;
  303. wpath=op_utf8_to_utf16(_path);
  304. wmode=op_utf8_to_utf16(_mode);
  305. if(wmode==NULL)errno=EINVAL;
  306. else if(wpath==NULL)errno=ENOENT;
  307. else fp=_wfreopen(wpath,wmode,(FILE *)_stream);
  308. _ogg_free(wmode);
  309. _ogg_free(wpath);
  310. }
  311. #endif
  312. if(fp!=NULL)*_cb=*op_get_file_callbacks(fp);
  313. return fp;
  314. }
  315. static int op_mem_read(void *_stream,unsigned char *_ptr,int _buf_size){
  316. OpusMemStream *stream;
  317. ptrdiff_t size;
  318. ptrdiff_t pos;
  319. stream=(OpusMemStream *)_stream;
  320. /*Check for empty read.*/
  321. if(_buf_size<=0)return 0;
  322. size=stream->size;
  323. pos=stream->pos;
  324. /*Check for EOF.*/
  325. if(pos>=size)return 0;
  326. /*Check for a short read.*/
  327. _buf_size=(int)OP_MIN(size-pos,_buf_size);
  328. memcpy(_ptr,stream->data+pos,_buf_size);
  329. pos+=_buf_size;
  330. stream->pos=pos;
  331. return _buf_size;
  332. }
  333. static int op_mem_seek(void *_stream,opus_int64 _offset,int _whence){
  334. OpusMemStream *stream;
  335. ptrdiff_t pos;
  336. stream=(OpusMemStream *)_stream;
  337. pos=stream->pos;
  338. OP_ASSERT(pos>=0);
  339. switch(_whence){
  340. case SEEK_SET:{
  341. /*Check for overflow:*/
  342. if(_offset<0||_offset>OP_MEM_DIFF_MAX)return -1;
  343. pos=(ptrdiff_t)_offset;
  344. }break;
  345. case SEEK_CUR:{
  346. /*Check for overflow:*/
  347. if(_offset<-pos||_offset>OP_MEM_DIFF_MAX-pos)return -1;
  348. pos=(ptrdiff_t)(pos+_offset);
  349. }break;
  350. case SEEK_END:{
  351. ptrdiff_t size;
  352. size=stream->size;
  353. OP_ASSERT(size>=0);
  354. /*Check for overflow:*/
  355. if(_offset<-size||_offset>OP_MEM_DIFF_MAX-size)return -1;
  356. pos=(ptrdiff_t)(size+_offset);
  357. }break;
  358. default:return -1;
  359. }
  360. stream->pos=pos;
  361. return 0;
  362. }
  363. static opus_int64 op_mem_tell(void *_stream){
  364. OpusMemStream *stream;
  365. stream=(OpusMemStream *)_stream;
  366. return (ogg_int64_t)stream->pos;
  367. }
  368. static int op_mem_close(void *_stream){
  369. _ogg_free(_stream);
  370. return 0;
  371. }
  372. static const OpusFileCallbacks OP_MEM_CALLBACKS={
  373. op_mem_read,
  374. op_mem_seek,
  375. op_mem_tell,
  376. op_mem_close
  377. };
  378. void *op_mem_stream_create(OpusFileCallbacks *_cb,
  379. const unsigned char *_data,size_t _size){
  380. OpusMemStream *stream;
  381. if(_size>OP_MEM_SIZE_MAX)return NULL;
  382. stream=(OpusMemStream *)_ogg_malloc(sizeof(*stream));
  383. if(stream!=NULL){
  384. *_cb=*&OP_MEM_CALLBACKS;
  385. stream->data=_data;
  386. stream->size=_size;
  387. stream->pos=0;
  388. }
  389. return stream;
  390. }