StdStream.h 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  1. // This code is in the public domain -- Ignacio Castaño <castano@gmail.com>
  2. //#pragma once
  3. //#ifndef NV_CORE_STDSTREAM_H
  4. //#define NV_CORE_STDSTREAM_H
  5. #include "nvcore.h"
  6. #include "Stream.h"
  7. #include "Array.h"
  8. #include <stdio.h> // fopen
  9. #include <string.h> // memcpy
  10. namespace nv
  11. {
  12. // Portable version of fopen.
  13. inline FILE * fileOpen(const char * fileName, const char * mode)
  14. {
  15. nvCheck(fileName != NULL);
  16. #if NV_CC_MSVC && _MSC_VER >= 1400
  17. FILE * fp;
  18. if (fopen_s(&fp, fileName, mode) == 0) {
  19. return fp;
  20. }
  21. return NULL;
  22. #else
  23. return fopen(fileName, mode);
  24. #endif
  25. }
  26. /// Base stdio stream.
  27. class NVCORE_CLASS StdStream : public Stream
  28. {
  29. NV_FORBID_COPY(StdStream);
  30. public:
  31. /// Ctor.
  32. StdStream( FILE * fp, bool autoclose ) : m_fp(fp), m_autoclose(autoclose) { }
  33. /// Dtor.
  34. virtual ~StdStream()
  35. {
  36. if( m_fp != NULL && m_autoclose ) {
  37. #if NV_OS_WIN32
  38. _fclose_nolock( m_fp );
  39. #else
  40. fclose( m_fp );
  41. #endif
  42. }
  43. }
  44. /** @name Stream implementation. */
  45. //@{
  46. virtual void seek( uint pos )
  47. {
  48. nvDebugCheck(m_fp != NULL);
  49. nvDebugCheck(pos <= size());
  50. #if NV_OS_WIN32
  51. _fseek_nolock(m_fp, pos, SEEK_SET);
  52. #else
  53. fseek(m_fp, pos, SEEK_SET);
  54. #endif
  55. }
  56. virtual uint tell() const
  57. {
  58. nvDebugCheck(m_fp != NULL);
  59. #if NV_OS_WIN32
  60. return _ftell_nolock(m_fp);
  61. #else
  62. return (uint)ftell(m_fp);
  63. #endif
  64. }
  65. virtual uint size() const
  66. {
  67. nvDebugCheck(m_fp != NULL);
  68. #if NV_OS_WIN32
  69. uint pos = _ftell_nolock(m_fp);
  70. _fseek_nolock(m_fp, 0, SEEK_END);
  71. uint end = _ftell_nolock(m_fp);
  72. _fseek_nolock(m_fp, pos, SEEK_SET);
  73. #else
  74. uint pos = (uint)ftell(m_fp);
  75. fseek(m_fp, 0, SEEK_END);
  76. uint end = (uint)ftell(m_fp);
  77. fseek(m_fp, pos, SEEK_SET);
  78. #endif
  79. return end;
  80. }
  81. virtual bool isError() const
  82. {
  83. return m_fp == NULL || ferror( m_fp ) != 0;
  84. }
  85. virtual void clearError()
  86. {
  87. nvDebugCheck(m_fp != NULL);
  88. clearerr(m_fp);
  89. }
  90. // @@ The original implementation uses feof, which only returns true when we attempt to read *past* the end of the stream.
  91. // That is, if we read the last byte of a file, then isAtEnd would still return false, even though the stream pointer is at the file end. This is not the intent and was inconsistent with the implementation of the MemoryStream, a better
  92. // implementation uses use ftell and fseek to determine our location within the file.
  93. virtual bool isAtEnd() const
  94. {
  95. if (m_fp == NULL) return true;
  96. //nvDebugCheck(m_fp != NULL);
  97. //return feof( m_fp ) != 0;
  98. #if NV_OS_WIN32
  99. uint pos = _ftell_nolock(m_fp);
  100. _fseek_nolock(m_fp, 0, SEEK_END);
  101. uint end = _ftell_nolock(m_fp);
  102. _fseek_nolock(m_fp, pos, SEEK_SET);
  103. #else
  104. uint pos = (uint)ftell(m_fp);
  105. fseek(m_fp, 0, SEEK_END);
  106. uint end = (uint)ftell(m_fp);
  107. fseek(m_fp, pos, SEEK_SET);
  108. #endif
  109. return pos == end;
  110. }
  111. /// Always true.
  112. virtual bool isSeekable() const { return true; }
  113. //@}
  114. protected:
  115. FILE * m_fp;
  116. bool m_autoclose;
  117. };
  118. /// Standard output stream.
  119. class NVCORE_CLASS StdOutputStream : public StdStream
  120. {
  121. NV_FORBID_COPY(StdOutputStream);
  122. public:
  123. /// Construct stream by file name.
  124. StdOutputStream( const char * name ) : StdStream(fileOpen(name, "wb"), /*autoclose=*/true) { }
  125. /// Construct stream by file handle.
  126. StdOutputStream( FILE * fp, bool autoclose ) : StdStream(fp, autoclose)
  127. {
  128. }
  129. /** @name Stream implementation. */
  130. //@{
  131. /// Write data.
  132. virtual uint serialize( void * data, uint len )
  133. {
  134. nvDebugCheck(data != NULL);
  135. nvDebugCheck(m_fp != NULL);
  136. #if NV_OS_WIN32
  137. return (uint)_fwrite_nolock(data, 1, len, m_fp);
  138. #elif NV_OS_LINUX
  139. return (uint)fwrite_unlocked(data, 1, len, m_fp);
  140. #elif NV_OS_DARWIN
  141. // @@ No error checking, always returns len.
  142. for (uint i = 0; i < len; i++) {
  143. putc_unlocked(((char *)data)[i], m_fp);
  144. }
  145. return len;
  146. #else
  147. return (uint)fwrite(data, 1, len, m_fp);
  148. #endif
  149. }
  150. virtual bool isLoading() const
  151. {
  152. return false;
  153. }
  154. virtual bool isSaving() const
  155. {
  156. return true;
  157. }
  158. //@}
  159. };
  160. /// Standard input stream.
  161. class NVCORE_CLASS StdInputStream : public StdStream
  162. {
  163. NV_FORBID_COPY(StdInputStream);
  164. public:
  165. /// Construct stream by file name.
  166. StdInputStream( const char * name ) : StdStream(fileOpen(name, "rb"), /*autoclose=*/true) { }
  167. /// Construct stream by file handle.
  168. StdInputStream( FILE * fp, bool autoclose=true ) : StdStream(fp, autoclose)
  169. {
  170. }
  171. /** @name Stream implementation. */
  172. //@{
  173. /// Read data.
  174. virtual uint serialize( void * data, uint len )
  175. {
  176. nvDebugCheck(data != NULL);
  177. nvDebugCheck(m_fp != NULL);
  178. #if NV_OS_WIN32
  179. return (uint)_fread_nolock(data, 1, len, m_fp);
  180. #elif NV_OS_LINUX
  181. return (uint)fread_unlocked(data, 1, len, m_fp);
  182. #elif NV_OS_DARWIN
  183. // This is rather lame. Not sure if it's faster than the locked version.
  184. for (uint i = 0; i < len; i++) {
  185. ((char *)data)[i] = getc_unlocked(m_fp);
  186. if (feof_unlocked(m_fp) != 0) {
  187. return i;
  188. }
  189. }
  190. return len;
  191. #else
  192. return (uint)fread(data, 1, len, m_fp);
  193. #endif
  194. }
  195. virtual bool isLoading() const
  196. {
  197. return true;
  198. }
  199. virtual bool isSaving() const
  200. {
  201. return false;
  202. }
  203. //@}
  204. };
  205. /// Memory input stream.
  206. class NVCORE_CLASS MemoryInputStream : public Stream
  207. {
  208. NV_FORBID_COPY(MemoryInputStream);
  209. public:
  210. /// Ctor.
  211. MemoryInputStream( const uint8 * mem, uint size ) : m_mem(mem), m_ptr(mem), m_size(size) { }
  212. /** @name Stream implementation. */
  213. //@{
  214. /// Read data.
  215. virtual uint serialize( void * data, uint len )
  216. {
  217. nvDebugCheck(data != NULL);
  218. nvDebugCheck(!isError());
  219. uint left = m_size - tell();
  220. if (len > left) len = left;
  221. memcpy( data, m_ptr, len );
  222. m_ptr += len;
  223. return len;
  224. }
  225. virtual void seek( uint pos )
  226. {
  227. nvDebugCheck(!isError());
  228. m_ptr = m_mem + pos;
  229. nvDebugCheck(!isError());
  230. }
  231. virtual uint tell() const
  232. {
  233. nvDebugCheck(m_ptr >= m_mem);
  234. return uint(m_ptr - m_mem);
  235. }
  236. virtual uint size() const
  237. {
  238. return m_size;
  239. }
  240. virtual bool isError() const
  241. {
  242. return m_mem == NULL || m_ptr > m_mem + m_size || m_ptr < m_mem;
  243. }
  244. virtual void clearError()
  245. {
  246. // Nothing to do.
  247. }
  248. virtual bool isAtEnd() const
  249. {
  250. return m_ptr == m_mem + m_size;
  251. }
  252. /// Always true.
  253. virtual bool isSeekable() const
  254. {
  255. return true;
  256. }
  257. virtual bool isLoading() const
  258. {
  259. return true;
  260. }
  261. virtual bool isSaving() const
  262. {
  263. return false;
  264. }
  265. //@}
  266. const uint8 * ptr() const { return m_ptr; }
  267. private:
  268. const uint8 * m_mem;
  269. const uint8 * m_ptr;
  270. uint m_size;
  271. };
  272. /// Buffer output stream.
  273. class NVCORE_CLASS BufferOutputStream : public Stream
  274. {
  275. NV_FORBID_COPY(BufferOutputStream);
  276. public:
  277. BufferOutputStream(Array<uint8> & buffer) : m_buffer(buffer) { }
  278. virtual uint serialize( void * data, uint len )
  279. {
  280. nvDebugCheck(data != NULL);
  281. m_buffer.append((uint8 *)data, len);
  282. return len;
  283. }
  284. virtual void seek( uint /*pos*/ ) { /*Not implemented*/ }
  285. virtual uint tell() const { return m_buffer.size(); }
  286. virtual uint size() const { return m_buffer.size(); }
  287. virtual bool isError() const { return false; }
  288. virtual void clearError() {}
  289. virtual bool isAtEnd() const { return true; }
  290. virtual bool isSeekable() const { return false; }
  291. virtual bool isLoading() const { return false; }
  292. virtual bool isSaving() const { return true; }
  293. private:
  294. Array<uint8> & m_buffer;
  295. };
  296. /// Protected input stream.
  297. class NVCORE_CLASS ProtectedStream : public Stream
  298. {
  299. NV_FORBID_COPY(ProtectedStream);
  300. public:
  301. /// Ctor.
  302. ProtectedStream( Stream & s ) : m_s(&s), m_autodelete(false)
  303. {
  304. }
  305. /// Ctor.
  306. ProtectedStream( Stream * s, bool autodelete = true ) :
  307. m_s(s), m_autodelete(autodelete)
  308. {
  309. nvDebugCheck(m_s != NULL);
  310. }
  311. /// Dtor.
  312. virtual ~ProtectedStream()
  313. {
  314. if( m_autodelete ) {
  315. delete m_s;
  316. }
  317. }
  318. /** @name Stream implementation. */
  319. //@{
  320. /// Read data.
  321. virtual uint serialize( void * data, uint len )
  322. {
  323. nvDebugCheck(data != NULL);
  324. len = m_s->serialize( data, len );
  325. if( m_s->isError() ) {
  326. #if NV_OS_ORBIS
  327. //SBtodoORBIS disabled (no exceptions)
  328. #else
  329. throw;
  330. #endif
  331. }
  332. return len;
  333. }
  334. virtual void seek( uint pos )
  335. {
  336. m_s->seek( pos );
  337. if( m_s->isError() ) {
  338. #if NV_OS_ORBIS
  339. //SBtodoORBIS disabled (no exceptions)
  340. #else
  341. throw;
  342. #endif
  343. }
  344. }
  345. virtual uint tell() const
  346. {
  347. return m_s->tell();
  348. }
  349. virtual uint size() const
  350. {
  351. return m_s->size();
  352. }
  353. virtual bool isError() const
  354. {
  355. return m_s->isError();
  356. }
  357. virtual void clearError()
  358. {
  359. m_s->clearError();
  360. }
  361. virtual bool isAtEnd() const
  362. {
  363. return m_s->isAtEnd();
  364. }
  365. virtual bool isSeekable() const
  366. {
  367. return m_s->isSeekable();
  368. }
  369. virtual bool isLoading() const
  370. {
  371. return m_s->isLoading();
  372. }
  373. virtual bool isSaving() const
  374. {
  375. return m_s->isSaving();
  376. }
  377. //@}
  378. private:
  379. Stream * const m_s;
  380. bool const m_autodelete;
  381. };
  382. } // nv namespace
  383. //#endif // NV_CORE_STDSTREAM_H