Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

418 lines
11 KiB

  1. //====== Copyright (c) 1996-2005, Valve Corporation, All rights reserved. =======//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. // Serialization/unserialization buffer
  8. //=============================================================================//
  9. #include "tier2/utlstreambuffer.h"
  10. #include "tier2/tier2.h"
  11. #include "filesystem.h"
  12. // NOTE: This has to be the last file included!
  13. #include "tier0/memdbgon.h"
  14. //-----------------------------------------------------------------------------
  15. // default stream chunk size
  16. //-----------------------------------------------------------------------------
  17. enum
  18. {
  19. DEFAULT_STREAM_CHUNK_SIZE = 16 * 1024
  20. };
  21. //-----------------------------------------------------------------------------
  22. // Constructor, destructor
  23. //-----------------------------------------------------------------------------
  24. CUtlStreamBuffer::CUtlStreamBuffer( ) : BaseClass( DEFAULT_STREAM_CHUNK_SIZE, DEFAULT_STREAM_CHUNK_SIZE, 0 )
  25. {
  26. SetUtlBufferOverflowFuncs( &CUtlStreamBuffer::StreamGetOverflow, &CUtlStreamBuffer::StreamPutOverflow );
  27. m_hFileHandle = FILESYSTEM_INVALID_HANDLE;
  28. m_pFileName = NULL;
  29. m_pPath = NULL;
  30. }
  31. CUtlStreamBuffer::CUtlStreamBuffer( const char *pFileName, const char *pPath, int nFlags, bool bDelayOpen, int nOpenFileFlags ) :
  32. BaseClass( DEFAULT_STREAM_CHUNK_SIZE, DEFAULT_STREAM_CHUNK_SIZE, nFlags )
  33. {
  34. if ( nFlags & TEXT_BUFFER )
  35. {
  36. Warning( "CUtlStreamBuffer does not support TEXT_BUFFER's use CUtlBuffer\n" );
  37. Assert( 0 );
  38. m_Error |= FILE_OPEN_ERROR;
  39. return;
  40. }
  41. SetUtlBufferOverflowFuncs( &CUtlStreamBuffer::StreamGetOverflow, &CUtlStreamBuffer::StreamPutOverflow );
  42. if ( bDelayOpen )
  43. {
  44. int nFileNameLen = Q_strlen( pFileName );
  45. m_pFileName = new char[ nFileNameLen + 1 ];
  46. Q_strcpy( m_pFileName, pFileName );
  47. if ( pPath )
  48. {
  49. int nPathLen = Q_strlen( pPath );
  50. m_pPath = new char[ nPathLen + 1 ];
  51. Q_strcpy( m_pPath, pPath );
  52. }
  53. else
  54. {
  55. m_pPath = new char[ 1 ];
  56. m_pPath[0] = 0;
  57. }
  58. m_nOpenFileFlags = nOpenFileFlags;
  59. m_hFileHandle = FILESYSTEM_INVALID_HANDLE;
  60. }
  61. else
  62. {
  63. m_pFileName = NULL;
  64. m_pPath = NULL;
  65. m_nOpenFileFlags = 0;
  66. m_hFileHandle = OpenFile( pFileName, pPath, nOpenFileFlags );
  67. if ( m_hFileHandle == FILESYSTEM_INVALID_HANDLE )
  68. {
  69. m_Error |= FILE_OPEN_ERROR;
  70. return;
  71. }
  72. }
  73. if ( IsReadOnly() )
  74. {
  75. // NOTE: MaxPut may not actually be this exact size for text files;
  76. // it could be slightly less owing to the /r/n -> /n conversion
  77. m_nMaxPut = g_pFullFileSystem->Size( m_hFileHandle );
  78. // Read in the first bytes of the file
  79. if ( Size() > 0 )
  80. {
  81. int nSizeToRead = MIN( Size(), m_nMaxPut );
  82. ReadBytesFromFile( nSizeToRead, 0 );
  83. }
  84. }
  85. }
  86. void CUtlStreamBuffer::Close()
  87. {
  88. if ( !IsReadOnly() )
  89. {
  90. // Write the final bytes
  91. int nBytesToWrite = TellPut() - m_nOffset;
  92. if ( nBytesToWrite > 0 )
  93. {
  94. if ( ( m_hFileHandle == FILESYSTEM_INVALID_HANDLE ) && m_pFileName )
  95. {
  96. m_hFileHandle = OpenFile( m_pFileName, m_pPath, m_nOpenFileFlags );
  97. }
  98. if ( m_hFileHandle != FILESYSTEM_INVALID_HANDLE )
  99. {
  100. if ( g_pFullFileSystem )
  101. g_pFullFileSystem->Write( Base(), nBytesToWrite, m_hFileHandle );
  102. }
  103. }
  104. }
  105. if ( m_hFileHandle != FILESYSTEM_INVALID_HANDLE )
  106. {
  107. if ( g_pFullFileSystem )
  108. g_pFullFileSystem->Close( m_hFileHandle );
  109. m_hFileHandle = FILESYSTEM_INVALID_HANDLE;
  110. }
  111. if ( m_pFileName )
  112. {
  113. delete[] m_pFileName;
  114. m_pFileName = NULL;
  115. }
  116. if ( m_pPath )
  117. {
  118. delete[] m_pPath;
  119. m_pPath = NULL;
  120. }
  121. m_Error = 0;
  122. }
  123. CUtlStreamBuffer::~CUtlStreamBuffer()
  124. {
  125. Close();
  126. }
  127. //-----------------------------------------------------------------------------
  128. // Open the file. normally done in constructor
  129. //-----------------------------------------------------------------------------
  130. void CUtlStreamBuffer::Open( const char *pFileName, const char *pPath, int nFlags, int nOpenFileFlags )
  131. {
  132. if ( IsOpen() )
  133. {
  134. Close();
  135. }
  136. m_Get = 0;
  137. m_Put = 0;
  138. m_nTab = 0;
  139. m_nOffset = 0;
  140. m_Flags = nFlags;
  141. m_hFileHandle = OpenFile( pFileName, pPath, nOpenFileFlags );
  142. if ( m_hFileHandle == FILESYSTEM_INVALID_HANDLE )
  143. {
  144. m_Error |= FILE_OPEN_ERROR;
  145. return;
  146. }
  147. if ( IsReadOnly() )
  148. {
  149. // NOTE: MaxPut may not actually be this exact size for text files;
  150. // it could be slightly less owing to the /r/n -> /n conversion
  151. m_nMaxPut = g_pFullFileSystem->Size( m_hFileHandle );
  152. // Read in the first bytes of the file
  153. if ( Size() > 0 )
  154. {
  155. int nSizeToRead = MIN( Size(), m_nMaxPut );
  156. ReadBytesFromFile( nSizeToRead, 0 );
  157. }
  158. }
  159. else
  160. {
  161. if ( m_Memory.NumAllocated() != 0 )
  162. {
  163. m_nMaxPut = -1;
  164. AddNullTermination( m_Put );
  165. }
  166. else
  167. {
  168. m_nMaxPut = 0;
  169. }
  170. }
  171. }
  172. //-----------------------------------------------------------------------------
  173. // Is the file open?
  174. //-----------------------------------------------------------------------------
  175. bool CUtlStreamBuffer::IsOpen() const
  176. {
  177. if ( m_hFileHandle != FILESYSTEM_INVALID_HANDLE )
  178. return true;
  179. // Delayed open case
  180. return ( m_pFileName != 0 );
  181. }
  182. //-----------------------------------------------------------------------------
  183. // Grow allocation size to fit requested size
  184. //-----------------------------------------------------------------------------
  185. void CUtlStreamBuffer::GrowAllocatedSize( int nSize )
  186. {
  187. int nNewSize = Size();
  188. if ( nNewSize < nSize + 1 )
  189. {
  190. while ( nNewSize < nSize + 1 )
  191. {
  192. nNewSize += DEFAULT_STREAM_CHUNK_SIZE;
  193. }
  194. m_Memory.Grow( nNewSize - Size() );
  195. }
  196. }
  197. //-----------------------------------------------------------------------------
  198. // Commit some of the stream to disk when we overflow.
  199. //-----------------------------------------------------------------------------
  200. bool CUtlStreamBuffer::StreamPutOverflow( int nSize )
  201. {
  202. if ( !IsValid() || IsReadOnly() )
  203. return false;
  204. // Make sure the allocated size is at least as big as the requested size
  205. if ( nSize > 0 )
  206. {
  207. GrowAllocatedSize( nSize + 2 );
  208. }
  209. // m_nOffset represents the location in the virtual buffer of m_Memory[0].
  210. // Compute the number of bytes that we've buffered up in memory so that we know what to write to disk.
  211. int nBytesToWrite = TellPut() - m_nOffset;
  212. if ( ( nBytesToWrite > 0 ) || ( nSize < 0 ) )
  213. {
  214. if ( m_hFileHandle == FILESYSTEM_INVALID_HANDLE )
  215. {
  216. m_hFileHandle = OpenFile( m_pFileName, m_pPath, m_nOpenFileFlags );
  217. }
  218. }
  219. // Write out the data that we have buffered if we have any.
  220. if ( nBytesToWrite > 0 )
  221. {
  222. int nBytesWritten = g_pFullFileSystem->Write( Base(), nBytesToWrite, m_hFileHandle );
  223. if ( nBytesWritten != nBytesToWrite )
  224. return false;
  225. // Set the offset to the current Put location to indicate that the buffer is now empty.
  226. m_nOffset = TellPut();
  227. }
  228. if ( nSize < 0 )
  229. {
  230. m_nOffset = -nSize-1;
  231. g_pFullFileSystem->Seek( m_hFileHandle, m_nOffset, FILESYSTEM_SEEK_HEAD );
  232. }
  233. return true;
  234. }
  235. //-----------------------------------------------------------------------------
  236. // Commit some of the stream to disk upon requests
  237. //-----------------------------------------------------------------------------
  238. bool CUtlStreamBuffer::TryFlushToFile( int nFlushToFileBytes )
  239. {
  240. if ( !IsValid() || IsReadOnly() || ( m_Error & PUT_OVERFLOW ) )
  241. return false;
  242. // m_nOffset represents the location in the virtual buffer of m_Memory[0].
  243. // Compute the number of bytes that we've buffered up in memory so that we know what to write to disk.
  244. int nBytesToWrite = TellPut() - m_nOffset;
  245. if ( nFlushToFileBytes < nBytesToWrite )
  246. nBytesToWrite = nFlushToFileBytes; // cannot write more than what we have, but can flush beginning of buffer up to certain amount
  247. if ( nBytesToWrite <= 0 )
  248. return true; // nothing buffered to write
  249. if ( m_hFileHandle == FILESYSTEM_INVALID_HANDLE )
  250. {
  251. m_hFileHandle = OpenFile( m_pFileName, m_pPath, m_nOpenFileFlags );
  252. }
  253. // Write out the data that we have buffered if we have any.
  254. int nBytesWritten = g_pFullFileSystem->Write( Base(), nBytesToWrite, m_hFileHandle );
  255. if ( nBytesWritten != nBytesToWrite )
  256. {
  257. m_Error |= PUT_OVERFLOW; // Flag the buffer the same way as if put overflow callback failed
  258. return false; // Let caller know about file IO failure
  259. }
  260. // Move the uncommitted data over and advance the offset up by as many bytes
  261. memmove( Base(), ( const char* ) Base() + nBytesWritten, ( TellPut() - m_nOffset ) - nBytesWritten + 1 ); // + 1 byte for null termination character
  262. m_nOffset += nBytesWritten;
  263. return true;
  264. }
  265. //-----------------------------------------------------------------------------
  266. // Reads bytes from the file; fixes up maxput if necessary and null terminates
  267. //-----------------------------------------------------------------------------
  268. int CUtlStreamBuffer::ReadBytesFromFile( int nBytesToRead, int nReadOffset )
  269. {
  270. if ( m_hFileHandle == FILESYSTEM_INVALID_HANDLE )
  271. {
  272. if ( !m_pFileName )
  273. {
  274. Warning( "File has not been opened!\n" );
  275. Assert(0);
  276. return 0;
  277. }
  278. m_hFileHandle = OpenFile( m_pFileName, m_pPath, m_nOpenFileFlags );
  279. if ( m_hFileHandle == FILESYSTEM_INVALID_HANDLE )
  280. {
  281. Error( "Unable to read file %s!\n", m_pFileName );
  282. return 0;
  283. }
  284. if ( m_nOffset != 0 )
  285. {
  286. g_pFullFileSystem->Seek( m_hFileHandle, m_nOffset, FILESYSTEM_SEEK_HEAD );
  287. }
  288. }
  289. char *pReadPoint = (char*)Base() + nReadOffset;
  290. int nBytesRead = g_pFullFileSystem->Read( pReadPoint, nBytesToRead, m_hFileHandle );
  291. if ( nBytesRead != nBytesToRead )
  292. {
  293. // Since max put is a guess at the start,
  294. // we need to shrink it based on the actual # read
  295. if ( m_nMaxPut > TellGet() + nReadOffset + nBytesRead )
  296. {
  297. m_nMaxPut = TellGet() + nReadOffset + nBytesRead;
  298. }
  299. }
  300. if ( nReadOffset + nBytesRead < Size() )
  301. {
  302. // This is necessary to deal with auto-NULL terminiation
  303. pReadPoint[nBytesRead] = 0;
  304. }
  305. return nBytesRead;
  306. }
  307. //-----------------------------------------------------------------------------
  308. // Load up more of the stream when we overflow
  309. //-----------------------------------------------------------------------------
  310. bool CUtlStreamBuffer::StreamGetOverflow( int nSize )
  311. {
  312. if ( !IsValid() || !IsReadOnly() )
  313. return false;
  314. // Shift the unread bytes down
  315. // NOTE: Can't use the partial overlap path if we're seeking. We'll
  316. // get negative sizes passed in if we're seeking.
  317. int nUnreadBytes;
  318. bool bHasPartialOverlap = ( nSize >= 0 ) && ( TellGet() >= m_nOffset ) && ( TellGet() <= m_nOffset + Size() );
  319. if ( bHasPartialOverlap )
  320. {
  321. nUnreadBytes = Size() - ( TellGet() - m_nOffset );
  322. if ( ( TellGet() != m_nOffset ) && ( nUnreadBytes > 0 ) )
  323. {
  324. memmove( Base(), (const char*)Base() + TellGet() - m_nOffset, nUnreadBytes );
  325. }
  326. }
  327. else
  328. {
  329. m_nOffset = TellGet();
  330. g_pFullFileSystem->Seek( m_hFileHandle, m_nOffset, FILESYSTEM_SEEK_HEAD );
  331. nUnreadBytes = 0;
  332. }
  333. // Make sure the allocated size is at least as big as the requested size
  334. if ( nSize > 0 )
  335. {
  336. GrowAllocatedSize( nSize );
  337. }
  338. int nBytesToRead = Size() - nUnreadBytes;
  339. int nBytesRead = ReadBytesFromFile( nBytesToRead, nUnreadBytes );
  340. if ( nBytesRead == 0 )
  341. return false;
  342. m_nOffset = TellGet();
  343. return ( nBytesRead + nUnreadBytes >= nSize );
  344. }
  345. //-----------------------------------------------------------------------------
  346. // open file unless already failed to open
  347. //-----------------------------------------------------------------------------
  348. FileHandle_t CUtlStreamBuffer::OpenFile( const char *pFileName, const char *pPath, int nOpenFileFlags )
  349. {
  350. if ( m_Error & FILE_OPEN_ERROR )
  351. return FILESYSTEM_INVALID_HANDLE;
  352. char options[ 3 ] = "xx";
  353. options[ 0 ] = IsReadOnly() ? 'r' : 'w';
  354. options[ 1 ] = IsText() && !ContainsCRLF() ? 't' : 'b';
  355. return g_pFullFileSystem->OpenEx( pFileName, options, nOpenFileFlags, pPath );
  356. }