chunkres.cpp
上传用户:zhongxx05
上传日期:2007-06-06
资源大小:33641k
文件大小:44k
源码类别:

Symbian

开发平台:

C/C++

  1. /* ***** BEGIN LICENSE BLOCK ***** 
  2.  * Version: RCSL 1.0/RPSL 1.0 
  3.  *  
  4.  * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. 
  5.  *      
  6.  * The contents of this file, and the files included with this file, are 
  7.  * subject to the current version of the RealNetworks Public Source License 
  8.  * Version 1.0 (the "RPSL") available at 
  9.  * http://www.helixcommunity.org/content/rpsl unless you have licensed 
  10.  * the file under the RealNetworks Community Source License Version 1.0 
  11.  * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl, 
  12.  * in which case the RCSL will apply. You may also obtain the license terms 
  13.  * directly from RealNetworks.  You may not use this file except in 
  14.  * compliance with the RPSL or, if you have a valid RCSL with RealNetworks 
  15.  * applicable to this file, the RCSL.  Please see the applicable RPSL or 
  16.  * RCSL for the rights, obligations and limitations governing use of the 
  17.  * contents of the file.  
  18.  *  
  19.  * This file is part of the Helix DNA Technology. RealNetworks is the 
  20.  * developer of the Original Code and owns the copyrights in the portions 
  21.  * it created. 
  22.  *  
  23.  * This file, and the files included with this file, is distributed and made 
  24.  * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 
  25.  * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, 
  26.  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS 
  27.  * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 
  28.  * 
  29.  * Technology Compatibility Kit Test Suite(s) Location: 
  30.  *    http://www.helixcommunity.org/content/tck 
  31.  * 
  32.  * Contributor(s): 
  33.  *  
  34.  * ***** END LICENSE BLOCK ***** */ 
  35. #include "hxslist.h"
  36. #include "chunkres.h"
  37. #include "hlxclib/stdlib.h" // needed for MAX_PATH
  38. #include "hlxclib/stdio.h" // for fopen(), etc.
  39. #include "hlxclib/limits.h" // for INT_MAX, etc.
  40. #include "hlxclib/fcntl.h" // for O_CREAT, etc.
  41. #include "hlxclib/io.h"
  42. #include "hlxclib/windows.h"
  43. #include "chxdataf.h" // cross platform file object
  44. #ifdef _MACINTOSH
  45. #ifdef _MAC_MACHO
  46. #include <unistd.h> // for unlink call
  47. #else
  48. #include <unix.h> // for unlink call
  49. #undef _UNIX //defined in unixmac.h
  50. #endif
  51. #endif
  52. #ifdef _UNIX
  53. #include <unistd.h>  // for unlink
  54. #ifndef _MAX_PATH
  55. #define _MAX_PATH 256
  56. #endif
  57. #endif
  58. #ifdef _SYMBIAN
  59. #include <unistd.h> //for unlink
  60. #endif
  61. #include "hxheap.h"
  62. #ifdef _DEBUG
  63. #undef HX_THIS_FILE
  64. static const char HX_THIS_FILE[] = __FILE__;
  65. #endif
  66. /////////////////////////////////////////////////////////////////////////////
  67. //
  68. // Method:
  69. //
  70. // CChunkyResMgr::OpenResource()
  71. //
  72. // Purpose:
  73. //
  74. // Opens an existing resource or creates a new resource.
  75. //
  76. // Parameters:
  77. //
  78. // CChunkyRes** ppChunkyRes
  79. // Memory location that will be filled in on output with the pointer
  80. // to the opened CChunkRes object.
  81. //
  82. // const char* pResName
  83. // Unique name of the resource to open or create.
  84. //
  85. // Return:
  86. //
  87. // HX_RESULT
  88. // Possible errors include: TBD.
  89. //
  90. HX_RESULT CChunkyResMgr::OpenResource(CChunkyRes** ppChunkyRes, const char* pResName)
  91. {
  92.     HX_RESULT theErr = HXR_OK;
  93.     HX_ASSERT(ppChunkyRes && pResName);
  94.     
  95.     void* pData;
  96.     if (m_OpenResources.Lookup(pResName, pData))
  97.     {
  98. *ppChunkyRes = (CChunkyRes*)pData;
  99.     }
  100.     else if (m_ClosedResources.Lookup(pResName, pData))
  101.     {
  102. *ppChunkyRes = (CChunkyRes*)pData;
  103. HX_VERIFY(m_ClosedResources.RemoveKey(pResName));
  104. m_OpenResources.SetAt(pResName, pData);
  105. RemoveFromLRU(pResName);
  106.     }
  107.     else
  108.     {
  109. *ppChunkyRes = new CChunkyRes;
  110. if (*ppChunkyRes)
  111. {
  112.     m_OpenResources.SetAt(pResName, (void*)*ppChunkyRes);
  113. }
  114. else
  115. {
  116.     theErr = HXR_OUTOFMEMORY;
  117. }
  118.     }
  119.     return theErr;
  120. }
  121. /////////////////////////////////////////////////////////////////////////////
  122. //
  123. // Method:
  124. //
  125. // CChunkyResMgr::CloseResource()
  126. //
  127. // Purpose:
  128. //
  129. // Closes an existing resource. Closed resources may be discarded.
  130. //
  131. // Parameters:
  132. //
  133. // CChunkyRes* pChunkyRes
  134. // Pointer to a previously opened CChunkRes object.
  135. //
  136. // Return:
  137. //
  138. // HX_RESULT
  139. // Possible errors include: TBD.
  140. //
  141. HX_RESULT CChunkyResMgr::CloseResource(CChunkyRes* pChunkyRes)
  142. {
  143.     HX_RESULT theErr = HXR_FAIL;
  144.     POSITION pPos = m_OpenResources.GetStartPosition();
  145.     while (pPos)
  146.     {
  147. CHXString key;
  148. void* pData;
  149. m_OpenResources.GetNextAssoc(pPos, key, pData);
  150. if (pData == (void*)pChunkyRes)
  151. {
  152.     HX_VERIFY(m_OpenResources.RemoveKey(key));
  153.     m_ClosedResources.SetAt(key, pData);
  154.     HX_ASSERT(!m_LRUResources.FindString(key));
  155.     m_LRUResources.AddTailString(key);
  156.     theErr = HXR_OK;
  157. }
  158.     }
  159.     if (theErr == HXR_OK)
  160.     {
  161. DiscardDiskData();
  162.     }
  163.     return theErr;
  164. }
  165. /////////////////////////////////////////////////////////////////////////////
  166. //
  167. // Method:
  168. //
  169. // CChunkyResMgr::CloseResource()
  170. //
  171. // Purpose:
  172. //
  173. // Closes an existing resource. Closed resources may be discarded.
  174. //
  175. // Parameters:
  176. //
  177. // const char* pResName
  178. // Unique name of a previously opened resource.
  179. //
  180. // Return:
  181. //
  182. // HX_RESULT
  183. // Possible errors include: TBD.
  184. //
  185. HX_RESULT CChunkyResMgr::CloseResource(const char* pResName)
  186. {
  187.     HX_RESULT theErr = HXR_FAIL;
  188.     void* pData;
  189.     if (m_OpenResources.Lookup(pResName, pData))
  190.     {
  191. HX_VERIFY(m_OpenResources.RemoveKey(pResName));
  192. m_ClosedResources.SetAt(pResName, pData);
  193. HX_ASSERT(!m_LRUResources.FindString(pResName));
  194. m_LRUResources.AddTailString(pResName);
  195. theErr = HXR_OK;
  196.     }
  197.     if (theErr == HXR_OK)
  198.     {
  199. DiscardDiskData();
  200.     }
  201.     return theErr;
  202. }
  203. /////////////////////////////////////////////////////////////////////////////
  204. //
  205. // Method:
  206. //
  207. // CChunkyResMgr::DiscardResource()
  208. //
  209. // Purpose:
  210. //
  211. // Discards a resource. Closed resources may be discarded.
  212. //
  213. // Parameters:
  214. //
  215. // const char* pResName
  216. // Unique name of a previously opened resource.
  217. //
  218. // Return:
  219. //
  220. // HX_RESULT
  221. // Possible errors include: TBD.
  222. //
  223. HX_RESULT CChunkyResMgr::DiscardResource(const char* pResName)
  224. {
  225.     HX_RESULT theErr = HXR_FAIL;
  226.     void* pData;
  227.     if (m_OpenResources.Lookup(pResName, pData))
  228.     {
  229. HX_VERIFY(m_OpenResources.RemoveKey(pResName));
  230. CChunkyRes* pRes = (CChunkyRes*)pData;
  231. delete pRes;
  232. theErr = HXR_OK;
  233.     }
  234.     if (m_ClosedResources.Lookup(pResName, pData))
  235.     {
  236. HX_VERIFY(m_ClosedResources.RemoveKey(pResName));
  237. RemoveFromLRU(pResName);
  238. CChunkyRes* pRes = (CChunkyRes*)pData;
  239. delete pRes;
  240. theErr = HXR_OK;
  241.     }
  242.     return theErr;
  243. }
  244. /////////////////////////////////////////////////////////////////////////////
  245. //
  246. // Method:
  247. //
  248. // CChunkyResMgr::FindResource()
  249. //
  250. // Purpose:
  251. //
  252. // Looks to see if the resource exists.
  253. //
  254. // Parameters:
  255. //
  256. // const char* pResName
  257. // Unique name of a previously opened resource.
  258. //
  259. // Return:
  260. //
  261. // HX_RESULT
  262. HX_RESULT
  263. CChunkyResMgr::FindResource(const char* pResName)
  264. {
  265.     void* pData;
  266.     if (m_OpenResources.Lookup(pResName, pData) ||
  267. m_ClosedResources.Lookup(pResName, pData))
  268.     {
  269. return HXR_OK;
  270.     }
  271.     return HXR_FAIL;
  272. }
  273. /////////////////////////////////////////////////////////////////////////////
  274. //
  275. // Method:
  276. //
  277. // CChunkyResMgr::SetDiskUsageThreshold()
  278. //
  279. // Purpose:
  280. //
  281. // Sets the Disk Usage threshold for the chunky resource manager.
  282. // If closed resources amount to more than the threshold, then they
  283. // will be discarded.
  284. //
  285. // Parameters:
  286. //
  287. // ULONG32 diskUsage
  288. // Disk usage in bytes which will be allowed for closed resources.
  289. //
  290. // Return:
  291. //
  292. // None.
  293. //
  294. void CChunkyResMgr::SetDiskUsageThreshold(ULONG32 diskUsage)
  295. {
  296.     m_ulDiskUsage = diskUsage;
  297.     DiscardDiskData();
  298. }
  299. void CChunkyResMgr::DiscardDiskData()
  300. {
  301.     void* pData = NULL;
  302.     CChunkyRes* pRes = NULL;
  303.     ULONG32 ulTotal = 0;
  304.     // Count the total disk usage
  305.     POSITION pPos = m_ClosedResources.GetStartPosition();
  306.     while (pPos)
  307.     {
  308. CHXString key;
  309. m_ClosedResources.GetNextAssoc(pPos, key, pData);
  310. HX_ASSERT(pData);
  311. pRes = (CChunkyRes*)pData;
  312. ulTotal += pRes->GetDiskUsage();
  313.     }
  314.     // Trim as much as we need until we're under the disk usage threshold.
  315.     pPos = m_LRUResources.GetHeadPosition();
  316.     while (pPos && ulTotal > m_ulDiskUsage)
  317.     {
  318. CHXString* pResName = m_LRUResources.GetNext(pPos);
  319. HX_ASSERT(pResName);
  320. if (m_ClosedResources.Lookup(*pResName, pData))
  321. {
  322.     HX_ASSERT(pData);
  323.     pRes = (CChunkyRes*)pData;
  324.     ULONG32 ulSize = pRes->GetDiskUsage();
  325.     if (ulSize)
  326.     {
  327. HX_ASSERT(ulSize <= ulTotal);
  328. ulTotal -= ulSize;
  329. m_ClosedResources.RemoveKey(*pResName);
  330. RemoveFromLRU(*pResName);
  331. delete pRes;
  332.     }
  333. }
  334.     }
  335. }
  336. void CChunkyResMgr::RemoveFromLRU(const char* pResName)
  337. {
  338.     POSITION pPos = m_LRUResources.GetHeadPosition();
  339.     POSITION pPrev; 
  340.     while (pPos)
  341.     {
  342. pPrev = pPos;
  343. CHXString* pStr = m_LRUResources.GetNext(pPos);
  344. if (!strcmp(*pStr, pResName))
  345. {
  346.     m_LRUResources.RemoveAt(pPrev);
  347. }
  348.     }
  349. }
  350. /////////////////////////////////////////////////////////////////////////////
  351. //
  352. // Method:
  353. //
  354. // CChunkyResMgr::CChunkyResMgr()
  355. //
  356. // Purpose:
  357. //
  358. // Construtor for chunky resource manager.
  359. //
  360. // Parameters:
  361. //
  362. // None.
  363. //
  364. // Return:
  365. //
  366. // N/A
  367. //
  368. CChunkyResMgr::CChunkyResMgr()
  369.     : m_ulDiskUsage(0)
  370. {
  371. }
  372. /////////////////////////////////////////////////////////////////////////////
  373. //
  374. // Method:
  375. //
  376. // CChunkyResMgr::~CChunkyResMgr()
  377. //
  378. // Purpose:
  379. //
  380. // Destructor for chunky resource manager.
  381. //
  382. // Parameters:
  383. //
  384. // None.
  385. //
  386. // Return:
  387. //
  388. // N/A
  389. //
  390. CChunkyResMgr::~CChunkyResMgr()
  391. {
  392.     CHXString key;
  393.     CChunkyRes* pRes;
  394.     POSITION p;
  395.     
  396.     p = m_OpenResources.GetStartPosition();
  397.     while (p)
  398.     {
  399. m_OpenResources.GetNextAssoc(p, key, (void*&)pRes);
  400. HX_DELETE(pRes);
  401.     }
  402.     p = m_ClosedResources.GetStartPosition();
  403.     while (p)
  404.     {
  405. m_ClosedResources.GetNextAssoc(p, key, (void*&)pRes);
  406. HX_DELETE(pRes);
  407.     }
  408. }
  409. /////////////////////////////////////////////////////////////////////////////
  410. //
  411. // Method:
  412. //
  413. // CChunkyRes::DiscardRange()
  414. //
  415. // Purpose:
  416. //
  417. // Discards the specified range of the file.
  418. //
  419. // Parameters:
  420. //
  421. // The location and length of the range to be discarded.
  422. //
  423. // Return:
  424. //
  425. // HX_RESULT
  426. // Possible errors include: TBD.
  427. //
  428. HX_RESULT CChunkyRes::DiscardRange( ULONG32 offset, ULONG32 count )
  429. {
  430.     HX_RESULT theErr = HXR_OK;
  431.     
  432.     // Big picture of this function is that it takes
  433.     // care of the end cases, where part of a chunk may
  434.     // be invalidated; then it totally removes all the
  435.     // chunks wholly contained by the range.
  436.     
  437.     ULONG32 ulOffsetIntoChunk;
  438.     
  439.     ULONG32 ulFirstChunk = offset/DEF_CHUNKYRES_CHUNK_SIZE;
  440.     
  441.     ulOffsetIntoChunk = offset % DEF_CHUNKYRES_CHUNK_SIZE;
  442.     
  443.     ULONG32 ulLastChunk  = (offset+count)/DEF_CHUNKYRES_CHUNK_SIZE;
  444.     
  445.     if (ulFirstChunk == ulLastChunk)
  446.     {
  447. // if the range is all in one chunk, deal with that simplest
  448. // case and ignore the more complicated scenarios.
  449. CChunkyResChunk* pChunk = (CChunkyResChunk*)m_Chunks[ulFirstChunk];
  450. HX_ASSERT(pChunk);
  451. pChunk->AddValidRange(ulOffsetIntoChunk, count, FALSE);
  452. return theErr;
  453.     }
  454.     
  455.     if (ulOffsetIntoChunk)
  456.     {
  457.         // OK, we have a chunk that needs to be partially invalidated.
  458.         
  459. CChunkyResChunk* pChunk = (CChunkyResChunk*)m_Chunks[ulFirstChunk];
  460. HX_ASSERT(pChunk);
  461. pChunk->AddValidRange(ulOffsetIntoChunk, DEF_CHUNKYRES_CHUNK_SIZE - ulOffsetIntoChunk, FALSE);
  462.         ulFirstChunk++;
  463.     }
  464.     
  465.     ulOffsetIntoChunk = (offset+count) % DEF_CHUNKYRES_CHUNK_SIZE;
  466.     if (ulOffsetIntoChunk)
  467.     {
  468.         // OK, the final chunk needs to be partially invalidated.
  469.         
  470. CChunkyResChunk* pChunk = (CChunkyResChunk*)m_Chunks[ulLastChunk];
  471. HX_ASSERT(pChunk);
  472. pChunk->AddValidRange(0, ulOffsetIntoChunk, FALSE);
  473.     }
  474.     
  475.     for (ULONG32 ulWhichChunk = ulFirstChunk; ulWhichChunk < ulLastChunk; ulWhichChunk++)
  476.     {
  477. CChunkyResChunk* pChunk = (CChunkyResChunk*)m_Chunks[ulWhichChunk];
  478. // if the chunk doesn't (yet?) exist, then it's considered invalid.
  479. if (pChunk)
  480. {
  481.     ULONG32 ulTempOffset = pChunk->GetTempFileOffset();
  482.     if (ulTempOffset)
  483.     {
  484. m_FreeDiskOffsets.AddHead((void*)ulTempOffset);
  485.     }
  486.     
  487.     delete pChunk;
  488.     m_Chunks[ulWhichChunk] = NULL;
  489. }
  490.     }
  491.     
  492.     return theErr;
  493. }
  494. /////////////////////////////////////////////////////////////////////////////
  495. //
  496. // Method:
  497. //
  498. // CChunkyRes::GetDiskUsage()
  499. //
  500. // Purpose:
  501. //
  502. // Returns the entire disk usage of a resource.
  503. //
  504. // Parameters:
  505. //
  506. // None.
  507. //
  508. // Return:
  509. //
  510. // ULONG32
  511. // Total amount of diskspace the resource's chunks consume.
  512. //
  513. ULONG32 CChunkyRes::GetDiskUsage() const
  514. {
  515. return (m_Chunks.GetSize() * DEF_CHUNKYRES_CHUNK_SIZE);
  516. }
  517. BOOL CChunkyRes::HasPartialData(ULONG32 length, ULONG32 offset /* = 0 */)
  518. {
  519. return (GetContiguousLength(offset) >= length);
  520. }
  521. ULONG32 CChunkyRes::GetContiguousLength(ULONG32 offset /* = 0 */)
  522. {
  523. ULONG32 contiguousLength = 0;
  524. int ndx;
  525. int startNdx = offset / DEF_CHUNKYRES_CHUNK_SIZE;
  526. if (startNdx < m_Chunks.GetSize())
  527. {
  528. CChunkyResChunk* pChunk = (CChunkyResChunk*)m_Chunks[startNdx];
  529. if (!pChunk) goto exit;
  530. contiguousLength = pChunk->GetValidLength(offset % DEF_CHUNKYRES_CHUNK_SIZE);
  531. if (contiguousLength != DEF_CHUNKYRES_CHUNK_SIZE - (offset % DEF_CHUNKYRES_CHUNK_SIZE))
  532. {
  533. goto exit;
  534. }
  535. }
  536. startNdx++;
  537. for (ndx = startNdx; ndx < m_Chunks.GetSize(); ndx++)
  538. {
  539. CChunkyResChunk* pChunk = (CChunkyResChunk*)m_Chunks[ndx];
  540. // if there is no chunk then we are no longer contiguous.
  541. if (!pChunk)
  542. {
  543. break;
  544. }
  545. ULONG32 chunkLength = pChunk->GetValidLength();
  546. contiguousLength += chunkLength;
  547. // if this chunk is not the max length then we are no longer contiguous
  548. if (chunkLength < DEF_CHUNKYRES_CHUNK_SIZE)
  549. {
  550. break;
  551. }
  552. }
  553. exit:
  554. return contiguousLength;
  555. }
  556. /////////////////////////////////////////////////////////////////////////////
  557. //
  558. // Method:
  559. //
  560. // CChunkyRes::GetData()
  561. //
  562. // Purpose:
  563. //
  564. // Gets a block of data out of a resource.
  565. //
  566. // Parameters:
  567. //
  568. // ULONG32 offset
  569. // char* buf
  570. // ULONG32 count
  571. // ULONG32* actual
  572. //
  573. // Return:
  574. //
  575. // HX_RESULT
  576. // Possible errors include: TBD.
  577. //
  578. HX_RESULT CChunkyRes::GetData(ULONG32 offset, char* buf, ULONG32 count, ULONG32* actual)
  579. {
  580. HX_RESULT theErr = HXR_OK;
  581. int ndx;
  582. ULONG32 ulFirstChunk = offset/DEF_CHUNKYRES_CHUNK_SIZE;
  583. ULONG32 ulLastChunk  = (offset+count)/DEF_CHUNKYRES_CHUNK_SIZE;
  584. HX_ASSERT(ulFirstChunk < INT_MAX);
  585. HX_ASSERT(ulLastChunk < INT_MAX);
  586. int nFirstChunk  = (int)ulFirstChunk;
  587. int nLastChunk   = (int)ulLastChunk;
  588. HX_ASSERT(m_Chunks.GetSize() >= nLastChunk+1);
  589. ULONG32 chunkOffset = offset - (ulFirstChunk*DEF_CHUNKYRES_CHUNK_SIZE);
  590. ULONG32 chunkCount  = count;
  591. ULONG32 baseOffset  = 0;
  592. *actual = 0; // -fst
  593. for (ndx = nFirstChunk; (ndx <= nLastChunk) && chunkCount; ndx++)
  594. {
  595. CChunkyResChunk* pChunk = (CChunkyResChunk*)m_Chunks[ndx];
  596. if (!pChunk)
  597. {
  598.     // with random access, it's feasible that there's a null chunk.
  599.     
  600.     theErr = HXR_CHUNK_MISSING;
  601.     goto exit;
  602. }
  603. HX_ASSERT_VALID_PTR(pChunk);
  604. ULONG32 chunkActual = 0;
  605. // Actually get the data from the chunk!
  606. ULONG32 chunkAmount = min(DEF_CHUNKYRES_CHUNK_SIZE-chunkOffset,chunkCount);
  607. theErr = pChunk->GetData(chunkOffset,buf+baseOffset,chunkAmount,&chunkActual);
  608. if (theErr != HXR_OK)
  609. {
  610. goto exit;
  611. }
  612. // What?!?!
  613. HX_ASSERT(chunkActual == chunkAmount);
  614. *actual += chunkActual; // -fst
  615. // reduce the chunk count...
  616. chunkCount -= chunkAmount;
  617. baseOffset += chunkAmount;
  618. // only the first chunk has an offset!
  619. chunkOffset = 0;
  620. }
  621. // Remember how many bytes have been served to the user,
  622. // in case they want us to discard used data
  623. m_ulUsedBytes = offset + *actual;
  624. // Discard chunks that have been fully served to the user
  625. if (m_bDiscardUsedData)
  626. {
  627. nLastChunk = (int)(m_ulUsedBytes/DEF_CHUNKYRES_CHUNK_SIZE);
  628. for (ndx = m_ulFirstChunkIdx; ndx < nLastChunk - 1; ndx++)
  629. {
  630. CChunkyResChunk* pChunk = (CChunkyResChunk*)m_Chunks[ndx];
  631. HX_ASSERT_VALID_PTR(pChunk);
  632. UINT32 ulTempOffset = pChunk->GetTempFileOffset();
  633. pChunk->DiscardDiskData();
  634. // Increment the first valid chunk index
  635. m_ulFirstChunkIdx++;
  636. if (ulTempOffset)
  637. {
  638.     // Add the disk space to the free space list
  639.     m_FreeDiskOffsets.AddHead((void*)ulTempOffset);
  640. }
  641. }
  642. }
  643. exit:
  644. return theErr;
  645. }
  646. HX_RESULT 
  647. CChunkyRes::GetContiguousDataPointer(ULONG32 offset, char*& buf, ULONG32 count)
  648. {
  649.     HX_RESULT theErr = HXR_OK;
  650.     HX_ASSERT(m_bDiscardUsedData == FALSE && m_bDisableDiskIO == FALSE);
  651.     ULONG32 ulFirstChunk = offset/DEF_CHUNKYRES_CHUNK_SIZE;
  652.     ULONG32 ulLastChunk  = (offset+count)/DEF_CHUNKYRES_CHUNK_SIZE;
  653.     HX_ASSERT(ulFirstChunk < INT_MAX);
  654.     HX_ASSERT(ulLastChunk < INT_MAX);
  655.     // if the required data length spans two chunks, we cannot have 
  656.     // contiguous memory
  657.     if (ulFirstChunk != ulLastChunk)
  658.     {
  659. return HXR_FAIL;
  660.     }
  661.     int nFirstChunk  = (int)ulFirstChunk;
  662.     if (m_Chunks.GetSize() < nFirstChunk+1)
  663.     {
  664. m_Chunks.SetSize(nFirstChunk+1);
  665.     }
  666.     
  667.     CChunkyResChunk* pChunk = (CChunkyResChunk*)m_Chunks[nFirstChunk];
  668.     if (!pChunk)
  669.     {
  670. pChunk = new CChunkyResChunk(this);
  671. if (m_bDisableDiskIO)
  672. {
  673.     pChunk->DisableDiskIO();
  674. }
  675. m_Chunks[nFirstChunk] = pChunk;
  676.     }
  677.     HX_ASSERT(m_Chunks.GetSize() >= nFirstChunk+1);
  678.     HX_ASSERT_VALID_PTR(pChunk);
  679.     ULONG32 chunkOffset = offset - (ulFirstChunk*DEF_CHUNKYRES_CHUNK_SIZE);
  680.     // Actually get the data from the chunk!
  681.     ULONG32 chunkAmount = min(DEF_CHUNKYRES_CHUNK_SIZE-chunkOffset, count);
  682.     theErr = pChunk->GetContiguousDataPointer(chunkOffset,buf,chunkAmount);
  683.     return theErr;
  684. }
  685. /////////////////////////////////////////////////////////////////////////////
  686. //
  687. // Method:
  688. //
  689. // CChunkyRes::SetData()
  690. //
  691. // Purpose:
  692. //
  693. // Sets a block of data in a resource.
  694. //
  695. // Parameters:
  696. //
  697. // ULONG32 offset
  698. // const char* buf
  699. // ULONG32 count
  700. //
  701. // Return:
  702. //
  703. // HX_RESULT
  704. // Possible errors include: TBD.
  705. //
  706. HX_RESULT CChunkyRes::SetData(ULONG32 offset, const char* buf, ULONG32 count)
  707. {
  708. HX_RESULT theErr = HXR_OK;
  709. ULONG32 ulFirstChunk = offset/DEF_CHUNKYRES_CHUNK_SIZE;
  710. ULONG32 ulLastChunk  = (offset+count)/DEF_CHUNKYRES_CHUNK_SIZE;
  711. HX_ASSERT(ulFirstChunk < INT_MAX);
  712. HX_ASSERT(ulLastChunk < INT_MAX);
  713. int nFirstChunk  = (int)ulFirstChunk;
  714. int nLastChunk   = (int)ulLastChunk;
  715. if (m_Chunks.GetSize() < nLastChunk+1)
  716. {
  717. m_Chunks.SetSize(nLastChunk+1);
  718. }
  719. ULONG32 chunkOffset = offset - (ulFirstChunk*DEF_CHUNKYRES_CHUNK_SIZE);
  720. ULONG32 chunkCount  = count;
  721. ULONG32 baseOffset  = 0;
  722. for (int ndx = nFirstChunk; ndx <= nLastChunk; ndx++)
  723. {
  724. CChunkyResChunk* pChunk = (CChunkyResChunk*)m_Chunks[ndx];
  725. if (!pChunk)
  726. {
  727. pChunk = new CChunkyResChunk(this);
  728. if (m_bDisableDiskIO)
  729. {
  730.     pChunk->DisableDiskIO();
  731. }
  732. m_Chunks[ndx] = pChunk;
  733. }
  734. // Actually set the data for the chunk!
  735. theErr = pChunk->SetData(chunkOffset,buf+baseOffset,min(DEF_CHUNKYRES_CHUNK_SIZE-chunkOffset,chunkCount));
  736. if (theErr != HXR_OK)
  737. {
  738. goto exit;
  739. }
  740. // reduce the chunk count...
  741. chunkCount -= (DEF_CHUNKYRES_CHUNK_SIZE-chunkOffset);
  742. baseOffset += (DEF_CHUNKYRES_CHUNK_SIZE-chunkOffset);
  743. // only the first chunk has an offset!
  744. chunkOffset = 0;
  745. }
  746. exit:
  747. return theErr;
  748. }
  749. void CChunkyRes::TrimDownMemoryMRU()
  750. {
  751. // If we have just reduced our allowed memory usage, then
  752. // discard the least recently used chunks till we are under
  753. // the ne threshold...
  754. if (m_CurMemUsage > m_MemUsageThreshold)
  755. {
  756. while (!m_ChunksMemoryMRU->IsEmpty() && (m_CurMemUsage > m_MemUsageThreshold))
  757. {
  758. // Get the least recently used chunk.
  759. CChunkyResChunk* pChunk = (CChunkyResChunk*)m_ChunksMemoryMRU->GetTail();
  760. HX_ASSERT_VALID_PTR(pChunk);
  761. // Discount its usage.
  762. m_CurMemUsage -= pChunk->GetSize();
  763. // Spill this chunk to disk...
  764. pChunk->SpillToDisk();
  765. // Remove the chunk from the end of the Memory MRU
  766. m_ChunksMemoryMRU->RemoveTail();
  767. // And add the chunk to the front of the Disk MRU
  768. m_ChunksDiskMRU->AddHead(pChunk);
  769. }
  770. // How can this be?!?! Did you really mean to set the memory usage such
  771. // that there are no chunks in memory?!?
  772. HX_ASSERT(!m_ChunksMemoryMRU->IsEmpty());
  773. }
  774. }
  775. /////////////////////////////////////////////////////////////////////////////
  776. //
  777. // Method:
  778. //
  779. // CChunkyResChunk::SetMemUsageThreshold()
  780. //
  781. // Purpose:
  782. //
  783. // Sets the memory usage threshold for the chunky resource chunks.
  784. // If if chunk sizes amount to more than the threshold, then the
  785. // least recently used ones will be spilled to disk.
  786. //
  787. // Parameters:
  788. //
  789. // ULONG32 memUsage
  790. // Memory usage in bytes which will be allowed for all chunks before
  791. // least recently used chunks will be spilled to disk.
  792. //
  793. // Return:
  794. //
  795. // None.
  796. //
  797. void CChunkyRes::SetMemUsageThreshold(ULONG32 memUsage)
  798. {
  799. m_MemUsageThreshold = memUsage;
  800. TrimDownMemoryMRU();
  801. }
  802. /////////////////////////////////////////////////////////////////////////////
  803. //
  804. // Method:
  805. //
  806. // CChunkyRes::CChunkyRes()
  807. //
  808. // Purpose:
  809. //
  810. // Constructor for a chunky resource.
  811. //
  812. // Parameters:
  813. //
  814. // None.
  815. //
  816. // Return:
  817. //
  818. // N/A
  819. //
  820. CChunkyRes::CChunkyRes()
  821. : m_Chunks()
  822. , m_strTempFileName()
  823. , m_ulNextTempFileChunk(DEF_START_CHUNK_OFFSET)
  824. , m_bHasBeenOpened(FALSE)
  825. , m_bDisableDiskIO(FALSE)
  826. , m_bDiscardUsedData(FALSE)
  827. , m_ulFirstChunkIdx(0)
  828. , m_ulUsedBytes(0)
  829. , m_pMutex(0)
  830. , m_MemUsageThreshold(DEF_CHUNKYRES_MEM_THRESHOLD)
  831. , m_CurMemUsage(0)
  832. , m_ChunksMemoryMRU(NULL)
  833. , m_ChunksDiskMRU(NULL)
  834. , m_ChunkSize(DEF_CHUNKYRES_CHUNK_SIZE)
  835. {
  836. #if defined(THREADS_SUPPORTED)
  837.     HXMutex::MakeMutex(m_pMutex);
  838. #else
  839.     HXMutex::MakeStubMutex(m_pMutex);
  840. #endif
  841.     HX_ASSERT(m_pMutex);
  842.     m_ChunksMemoryMRU = new CHXSimpleList;
  843.     m_ChunksDiskMRU = new CHXSimpleList;
  844.     HX_ASSERT(m_ChunksMemoryMRU);
  845.     HX_ASSERT(m_ChunksDiskMRU);
  846. }
  847. /////////////////////////////////////////////////////////////////////////////
  848. //
  849. // Method:
  850. //
  851. // CChunkyRes::~CChunkyRes()
  852. //
  853. // Purpose:
  854. //
  855. // Destructor for a chunky resource.
  856. //
  857. // Parameters:
  858. //
  859. // None.
  860. //
  861. // Return:
  862. //
  863. // N/A
  864. //
  865. CChunkyRes::~CChunkyRes()
  866. {
  867. // If we are getting rid of the resource, then
  868. // we should discard all of the chunks...
  869. for (int ndx = 0; ndx < m_Chunks.GetSize(); ndx++)
  870. {
  871. CChunkyResChunk* pChunk = (CChunkyResChunk*)m_Chunks[ndx];
  872. if (pChunk)
  873. {
  874. delete pChunk;
  875. }
  876. }
  877. HX_RESULT theErr = DiscardDiskData();
  878. HX_ASSERT(theErr == HXR_OK);
  879. if(m_ChunksMemoryMRU)
  880. {
  881.     HX_ASSERT(m_ChunksMemoryMRU->GetCount() == 0);
  882.     delete m_ChunksMemoryMRU;
  883.     m_ChunksMemoryMRU = NULL;
  884. }
  885. if(m_ChunksDiskMRU)
  886. {
  887.     HX_ASSERT(m_ChunksDiskMRU->GetCount() == 0);
  888.     delete m_ChunksDiskMRU;
  889.     m_ChunksDiskMRU = NULL;
  890. }
  891. HX_DELETE(m_pMutex);
  892. }
  893. /////////////////////////////////////////////////////////////////////////////
  894. //
  895. // Method:
  896. //
  897. // CChunkyResChunk::MakeSureChunkIsInMemory()
  898. //
  899. // Purpose:
  900. //
  901. // Get a portion of the data for a chunk.
  902. //
  903. // Parameters:
  904. //
  905. // ULONG32 offset
  906. // char* buf
  907. // ULONG32 count
  908. // ULONG32* actual
  909. //
  910. // Return:
  911. //
  912. // HX_RESULT
  913. // Possible errors include: TBD.
  914. //
  915. HX_RESULT CChunkyResChunk::MakeSureChunkIsInMemory()
  916. {
  917. HX_RESULT theErr = HXR_OK;
  918. // If we don't have a chunk pointer, then we aren't in
  919. // memory...
  920. if (!m_pChunkData)
  921. {
  922. // Find ourselves in the MRU list for Disk chunks...
  923. LISTPOSITION pos = m_pChunkRes->m_ChunksDiskMRU->Find(this);
  924. // If were found in the disk MRU list, then we have
  925. // some work to do...
  926. if (pos)
  927. {
  928. // First, remove ourselves from the disk list...
  929. m_pChunkRes->m_ChunksDiskMRU->RemoveAt(pos);
  930. // Load from disk...
  931. theErr = LoadFromDisk();
  932. if (theErr != HXR_OK)
  933. {
  934. goto exit;
  935. }
  936. }
  937. else
  938. {
  939. #ifdef _DEBUG
  940. {
  941. // We shouldn't find ourselves in the MRU list
  942. // for memory chunks... but we want to check!
  943. LISTPOSITION pos = m_pChunkRes->m_ChunksMemoryMRU->Find(this);
  944. // We shouldn't be in this list!!!!
  945. HX_ASSERT(pos == NULL);
  946. }
  947. #endif // end _DEBUG section
  948. m_pChunkData = new UCHAR[m_pChunkRes->m_ChunkSize];
  949. HX_ASSERT(GetValidLength() == 0);
  950. }
  951. // Add to the front of the Memory list...
  952. m_pChunkRes->m_ChunksMemoryMRU->AddHead(this);
  953. m_pChunkRes->m_CurMemUsage += GetSize();
  954. // Make sure we don't have to much info in memory...
  955. if (!m_bDisableDiskIO)
  956. {
  957.     m_pChunkRes->TrimDownMemoryMRU();
  958.     HX_ASSERT(m_pChunkData);
  959. }
  960. }
  961. // If we are already in memory, then make sure we are at the
  962. // top of the Memory MRU list!!!
  963. else
  964. {
  965. // We should find ourselves in the MRU list
  966. // for memory chunks... but we want to check!
  967. LISTPOSITION pos = m_pChunkRes->m_ChunksMemoryMRU->Find(this);
  968. // XXXNH: If we aren't in this list it means we were paged out,
  969. // so we only need to put ourselves at the top of the MRU list
  970. if (pos)
  971. {
  972.     // First, remove ourselves from wherever we are in the
  973.     // Memory MRU list...
  974.     m_pChunkRes->m_ChunksMemoryMRU->RemoveAt(pos);
  975. }
  976. // And add ourselves to the top of the list!
  977. m_pChunkRes->m_ChunksMemoryMRU->AddHead(this);
  978. }
  979. exit:
  980. return theErr;
  981. }
  982. /////////////////////////////////////////////////////////////////////////////
  983. //
  984. // Method:
  985. //
  986. // CChunkyResChunk::GetValidLength()
  987. //
  988. // Purpose:
  989. //
  990. // Determines how much of a chunk is valid
  991. //
  992. // Parameters:
  993. //
  994. // ULONG32 offset
  995. //
  996. // Return:
  997. //
  998. // HX_RESULT
  999. // Possible errors include: TBD.
  1000. //
  1001. ULONG32 CChunkyResChunk::GetValidLength(ULONG32 offset /* = 0 */) const
  1002. {
  1003.     
  1004.     HX_ASSERT(offset < GetSize());
  1005.     
  1006.     ULONG32 ulValidLength = 0;
  1007.     
  1008. LISTPOSITION rangePos = m_ValidRanges.GetHeadPosition();
  1009. if (rangePos)
  1010. {
  1011. do
  1012. {
  1013. ValidRange* pRange = (ValidRange*)m_ValidRanges.GetNext(rangePos);
  1014. HX_ASSERT(pRange);
  1015. // see if the offset points into this particular range
  1016. if (offset >= pRange->offset
  1017. && offset <= pRange->offset + pRange->length)
  1018. {
  1019.     ulValidLength = pRange->offset + pRange->length - offset;
  1020. }
  1021. }
  1022. while (rangePos);
  1023. }
  1024.     return ulValidLength;
  1025. }
  1026. /////////////////////////////////////////////////////////////////////////////
  1027. //
  1028. // Method:
  1029. //
  1030. // CChunkyResChunk::GetData()
  1031. //
  1032. // Purpose:
  1033. //
  1034. // Get a portion of the data for a chunk.
  1035. //
  1036. // Parameters:
  1037. //
  1038. // ULONG32 offset
  1039. // char* buf
  1040. // ULONG32 count
  1041. // ULONG32* actual
  1042. //
  1043. // Return:
  1044. //
  1045. // HX_RESULT
  1046. // Possible errors include: TBD.
  1047. //
  1048. HX_RESULT CChunkyResChunk::GetData(ULONG32 offset, char* buf, ULONG32 count, ULONG32* actual)
  1049. {
  1050. HX_RESULT theErr;
  1051. if(!count)
  1052. {
  1053. *actual = count;
  1054. return HXR_OK;
  1055. }
  1056. // We should have a non-zero valid size
  1057. if (!GetValidLength(offset))
  1058. {
  1059. // This chunk must have been discarded
  1060. theErr = HXR_CHUNK_MISSING;
  1061. goto exit;
  1062. }
  1063. // Make sure this chunk is in memory!
  1064. theErr = MakeSureChunkIsInMemory();
  1065. if (theErr != HXR_OK)
  1066. {
  1067. goto exit;
  1068. }
  1069. // You can't read more than there is room in this chunk.
  1070. // CChunkyRes should prevent this case...
  1071. HX_ASSERT(offset+count <= GetSize());
  1072. // The call to MakeSureChunkIsInMemory() should have handled this!
  1073. HX_ASSERT_VALID_PTR(m_pChunkData);
  1074. *actual = min(count,GetValidLength(offset));
  1075. HX_ASSERT(*actual < UINT_MAX);
  1076. memcpy(buf,m_pChunkData+offset,(int)(*actual)); /* Flawfinder: ignore */
  1077. exit:
  1078. return theErr;
  1079. }
  1080. HX_RESULT 
  1081. CChunkyResChunk::GetContiguousDataPointer(ULONG32 offset, char*& buf, ULONG32 count)
  1082. {
  1083.     HX_RESULT theErr = HXR_OK;
  1084.     if(!count)
  1085.     {
  1086. theErr = HXR_FAIL;
  1087. goto exit;
  1088.     }
  1089.     // First, make sure this chunk is in memory!
  1090.     theErr = MakeSureChunkIsInMemory();
  1091.     if (theErr != HXR_OK)
  1092.     {
  1093. goto exit;
  1094.     }
  1095.     // You can't write more than there is room in this chunk.
  1096.     // CChunkyRes should prevent this case...
  1097.     HX_ASSERT(offset+count <= GetSize());
  1098.     // Currently, you must write to chunks in order from the
  1099.     // start of the chunk first. Random access may come in the
  1100.     // future...
  1101.     HX_ASSERT(GetValidLength(offset) > 0); // needed still at all?
  1102.     // The call to MakeSureChunkIsInMemory() should have handled this!
  1103.     HX_ASSERT_VALID_PTR(m_pChunkData);
  1104.     AddValidRange(offset, count);
  1105.     
  1106.     HX_ASSERT(count < UINT_MAX);
  1107.     buf = (char*) (m_pChunkData+offset);
  1108.     m_bModified = TRUE;
  1109. exit:
  1110.     return theErr;
  1111. }
  1112. /////////////////////////////////////////////////////////////////////////////
  1113. //
  1114. // Method:
  1115. //
  1116. // CChunkyResChunk::AddValidRange()
  1117. //
  1118. // Purpose:
  1119. //
  1120. // Mark a part of this CChunkyResChunk as valid
  1121. // Called from SetData.
  1122. //
  1123. // Parameters:
  1124. //
  1125. // ULONG32 offset
  1126. // ULONG32 length
  1127. // BOOL bValid
  1128. //
  1129. // Return:
  1130. //
  1131. // HX_RESULT
  1132. // Possible errors include: TBD.
  1133. //
  1134. HX_RESULT CChunkyResChunk::AddValidRange(ULONG32 offset, ULONG32 length, BOOL bValid /* = TRUE */)
  1135. {
  1136. HX_RESULT theErr = HXR_OK;
  1137.     int nCount = m_ValidRanges.GetCount();
  1138.     LISTPOSITION pos = m_ValidRanges.GetHeadPosition();
  1139.     if (bValid)
  1140. {
  1141. // I don't think we need to ensure that the chunk is in
  1142. // memory, although it always probably will be if this is
  1143. // called from SetData.
  1144. // Ensure that it's saying that a legal range is valid.
  1145. HX_ASSERT(offset+length <= GetSize());
  1146. // Create a new range element.
  1147. ValidRange* pNewRange = new ValidRange;
  1148. pNewRange->offset = offset;
  1149. pNewRange->length = length;
  1150. // Iterate through the valid ranges to ensure that
  1151. // none of them overlap the range we're adding now.
  1152.         for (int i=0; i<nCount; i++)
  1153. {
  1154.             ValidRange* pRange = (ValidRange*)m_ValidRanges.GetAt(pos);
  1155.             BOOL bNeedToMerge = FALSE;
  1156.             // See if this range element overlaps the front end of the
  1157.             // new range element.
  1158.             if (pRange->offset <= pNewRange->offset
  1159.             && pRange->offset + pRange->length >= pNewRange->offset)
  1160.             {
  1161.             bNeedToMerge = TRUE;
  1162.             }
  1163.             // see if this range element overlaps the back end of the
  1164.             // new range element.
  1165.             if (pRange->offset <= pNewRange->offset + pNewRange->length
  1166.             && pRange->offset + pRange->length >= pNewRange->offset + pNewRange->length)
  1167.             {
  1168.             bNeedToMerge = TRUE;
  1169.             }
  1170. // if an overlap happened, make the new range element hold
  1171. // the union of both ranges.
  1172.             if (bNeedToMerge)
  1173.             {
  1174.                 ULONG32 ulStartOfRange = min(pNewRange->offset, pRange->offset);
  1175.                 ULONG32 ulEndOfRange = max(pNewRange->offset+pNewRange->length,
  1176.                 pRange->offset+pRange->length);
  1177.                 HX_ASSERT(ulEndOfRange >= ulStartOfRange);
  1178.                 pNewRange->offset = ulStartOfRange;
  1179.                 pNewRange->length = ulEndOfRange-ulStartOfRange;
  1180.                 // delete the one we overlap with since we've ensured
  1181.                 // that pNewRange's range covers both.
  1182.                 pos = m_ValidRanges.RemoveAt(pos);
  1183.                 delete pRange;
  1184. }
  1185.             else
  1186.                 m_ValidRanges.GetAtNext(pos);
  1187. }
  1188. // Now that we're sure that nobody overlaps us, we can
  1189. // add this range.
  1190. m_ValidRanges.AddTail((void*)pNewRange);
  1191. }
  1192.     else
  1193.     {
  1194.         // bValid is false, so we're INVALIDATING a range.
  1195.         // iterate through the list of valid ranges, and for each of them
  1196.         // that overlaps the incoming range, either trim it appropriately
  1197.         // or delete it entirely.
  1198.         for (int i=0; i<nCount; i++)
  1199.         {
  1200.             ValidRange* pRange = (ValidRange*)m_ValidRanges.GetAt(pos);
  1201.             HX_ASSERT(pRange);
  1202.             // see if it's totally covered by the incoming range
  1203.             if (offset <= pRange->offset && offset+length >= pRange->offset + pRange->length)
  1204.             {
  1205.                 pos = m_ValidRanges.RemoveAt(pos);
  1206.                 delete pRange;
  1207.             }
  1208.             else
  1209.             {
  1210.                 // see if it needs to be trimmed
  1211.                 ULONG32 ulCurrentRangeEnd = pRange->offset + pRange->length;
  1212.                 ULONG32 ulRangeEnd = offset + length;
  1213.                 BOOL bNeedToTrimOffBackEnd = pRange->offset < offset && ulCurrentRangeEnd >= offset;
  1214.                 BOOL bNeedToTrimOffFrontEnd = pRange->offset < ulRangeEnd
  1215.                 && ulCurrentRangeEnd > ulRangeEnd;
  1216.                 if (bNeedToTrimOffBackEnd)
  1217.                 {
  1218.                     pRange->length = offset - pRange->offset;
  1219.                 }
  1220.                 if (bNeedToTrimOffFrontEnd)
  1221.                 {
  1222.                     // if we've also trimmed off the back end
  1223.                     // then we need to create a new range element
  1224.                     // to hold this sans-front-end element.
  1225.                     if (bNeedToTrimOffBackEnd)
  1226.                     {
  1227.                         pRange = new ValidRange;
  1228.                         m_ValidRanges.AddHead(pRange);
  1229.                     }
  1230.                     pRange->offset = ulRangeEnd;
  1231.                     pRange->length = ulCurrentRangeEnd - pRange->offset;
  1232.                 }
  1233.                 m_ValidRanges.GetAtNext(pos);
  1234.             }
  1235. }
  1236.     }
  1237.     return theErr;
  1238. }
  1239. /////////////////////////////////////////////////////////////////////////////
  1240. //
  1241. // Method:
  1242. //
  1243. // CChunkyResChunk::SetData()
  1244. //
  1245. // Purpose:
  1246. //
  1247. // Set a portion of the data for a chunk.
  1248. //
  1249. // Parameters:
  1250. //
  1251. // ULONG32 offset
  1252. // const char* buf
  1253. // ULONG32 count
  1254. //
  1255. // Return:
  1256. //
  1257. // HX_RESULT
  1258. // Possible errors include: TBD.
  1259. //
  1260. HX_RESULT CChunkyResChunk::SetData(ULONG32 offset, const char* buf, ULONG32 count)
  1261. {
  1262. // First, make sure this chunk is in memory!
  1263. HX_RESULT theErr = MakeSureChunkIsInMemory();
  1264. if (theErr != HXR_OK)
  1265. {
  1266. goto exit;
  1267. }
  1268. // You can't write more than there is room in this chunk.
  1269. // CChunkyRes should prevent this case...
  1270. HX_ASSERT(offset+count <= GetSize());
  1271. // The call to MakeSureChunkIsInMemory() should have handled this!
  1272. HX_ASSERT_VALID_PTR(m_pChunkData);
  1273. HX_ASSERT(count < UINT_MAX);
  1274.         memcpy(m_pChunkData+offset,buf,(int)(offset+count <= GetSize() ? count : GetSize() - offset));
  1275. m_bModified = TRUE;
  1276. // Make sure that it remembers that this is now a valid
  1277. // range.
  1278. AddValidRange(offset, count);
  1279. exit:
  1280. return theErr;
  1281. }
  1282. /////////////////////////////////////////////////////////////////////////////
  1283. //
  1284. // Method:
  1285. //
  1286. // CChunkyResChunk::SpillToDisk()
  1287. //
  1288. // Purpose:
  1289. //
  1290. // Spills to disk the data of a chunk.
  1291. //
  1292. // Parameters:
  1293. //
  1294. // None.
  1295. //
  1296. // Return:
  1297. //
  1298. // HX_RESULT
  1299. // Possible errors include: TBD.
  1300. //
  1301. HX_RESULT CChunkyResChunk::SpillToDisk()
  1302. {
  1303. Lock();
  1304. HX_RESULT theErr = HXR_OK;
  1305. CHXDataFile* pFile = NULL;
  1306. ULONG32 actualCount = 0;
  1307. // Don't waste any time unless we are actually modified.
  1308. // And only actually spill, if there is something to spill
  1309. if (!m_bModified || !m_pChunkData)
  1310. {
  1311. goto exit;
  1312. }
  1313. // If we have never spilled to disk, then ask the ChunkyRes
  1314. // for the temp file name and a slot to spill to.
  1315. if (!m_bPreviouslySpilled)
  1316. {
  1317. theErr = m_pChunkRes->GetTempFileChunk(pFile,m_ulTempFileOffset);
  1318. }
  1319. // Otherwise, just get the temp file name.
  1320. else
  1321. {
  1322. theErr = m_pChunkRes->GetTempFile(pFile);
  1323. }
  1324. // If we failed to open the file, then set the valid
  1325. // size to 0. If the user wants to use the data, they
  1326. // will need to handle the case of not having the data!
  1327. if (theErr != HXR_OK)
  1328. {
  1329. HX_ASSERT(pFile == NULL);
  1330. theErr = HXR_TEMP_FILE;
  1331. goto exit;
  1332. }
  1333. theErr = pFile->Seek(m_ulTempFileOffset,SEEK_SET);
  1334. if (theErr != HXR_OK)
  1335. {
  1336. theErr = HXR_TEMP_FILE;
  1337. goto exit;
  1338. }
  1339. HX_ASSERT(m_pChunkData);
  1340. actualCount = pFile->Write((char *)m_pChunkData, m_pChunkRes->m_ChunkSize);
  1341. m_bPreviouslySpilled = TRUE;
  1342. if (actualCount != m_pChunkRes->m_ChunkSize)
  1343. {
  1344. theErr = HXR_TEMP_FILE;
  1345. }
  1346. exit:
  1347. // If we created a file, then clean it up!
  1348. if (pFile)
  1349. {
  1350. delete pFile;
  1351. }
  1352. // If we had an error then record that our size is now invalid.
  1353. if (theErr != HXR_OK)
  1354. {
  1355. AddValidRange(0, m_pChunkRes->m_ChunkSize, FALSE);
  1356. m_bPreviouslySpilled = FALSE;
  1357. }
  1358. // Never the less, we do get rid of the data!
  1359. if (m_pChunkData)
  1360. {
  1361. delete[] m_pChunkData;
  1362. m_pChunkData = NULL;
  1363. }
  1364. Unlock();
  1365. return theErr;
  1366. }
  1367. /////////////////////////////////////////////////////////////////////////////
  1368. //
  1369. // Method:
  1370. //
  1371. // CChunkyResChunk::LoadFromDisk()
  1372. //
  1373. // Purpose:
  1374. //
  1375. // Loads into memory the data from the chunk previously spilled to
  1376. // disk.
  1377. //
  1378. // Parameters:
  1379. //
  1380. // None.
  1381. //
  1382. // Return:
  1383. //
  1384. // HX_RESULT
  1385. // Possible errors include: TBD.
  1386. //
  1387. HX_RESULT CChunkyResChunk::LoadFromDisk()
  1388. {
  1389. Lock();
  1390. HX_RESULT theErr = HXR_OK;
  1391. CHXDataFile* pFile = NULL;
  1392. ULONG32 amountRead = 0;
  1393. // We shouldn't be here if we have memory already allocated!
  1394. HX_ASSERT(m_pChunkData == NULL);
  1395. // If we have never spilled to disk, then there is nothing to
  1396. // load from disk!
  1397. if (!m_bPreviouslySpilled)
  1398. {
  1399. // Even if we've never been spilled, we need to make
  1400. // sure we have memory available...
  1401. m_pChunkData = new UCHAR[m_pChunkRes->m_ChunkSize];
  1402. if(!m_pChunkData)
  1403. {
  1404. theErr = HXR_OUTOFMEMORY;
  1405. goto exit;
  1406. }
  1407. goto exit;
  1408. }
  1409. // Get the temp file name.
  1410. theErr = m_pChunkRes->GetTempFile(pFile);
  1411. // If we failed to open the file, then set the valid
  1412. // size to 0. If the user wants to use the data, they
  1413. // will need to handle the case of not having the data!
  1414. if (theErr != HXR_OK)
  1415. {
  1416. HX_ASSERT(pFile == NULL);
  1417. theErr = HXR_TEMP_FILE;
  1418. goto exit;
  1419. }
  1420. theErr = pFile->Seek(m_ulTempFileOffset,SEEK_SET);
  1421. if (theErr != HXR_OK)
  1422. {
  1423. theErr = HXR_TEMP_FILE;
  1424. goto exit;
  1425. }
  1426. m_pChunkData = new UCHAR[m_pChunkRes->m_ChunkSize];
  1427. if(!m_pChunkData)
  1428. {
  1429. theErr = HXR_OUTOFMEMORY;
  1430. goto exit;
  1431. }
  1432. amountRead = pFile->Read((char *)m_pChunkData, m_pChunkRes->m_ChunkSize);
  1433. if(amountRead != m_pChunkRes->m_ChunkSize)
  1434. {
  1435. theErr = HXR_TEMP_FILE;
  1436. delete[] m_pChunkData;
  1437. m_pChunkData = NULL;
  1438. goto exit;
  1439. }
  1440. exit:
  1441. // If we actually, loaded the data from disk, then
  1442. // we are not modified!
  1443. if (theErr == HXR_OK)
  1444. {
  1445. m_bModified = FALSE;
  1446. }
  1447. // If we created a file, then clean it up!
  1448. if (pFile)
  1449. {
  1450. delete pFile;
  1451. }
  1452. // If we had an error then record that our size is now invalid.
  1453. if (theErr != HXR_OK)
  1454. {
  1455. AddValidRange(0, m_pChunkRes->m_ChunkSize, FALSE);
  1456. m_bPreviouslySpilled = FALSE;
  1457. }
  1458. Unlock();
  1459. return theErr;
  1460. }
  1461. /////////////////////////////////////////////////////////////////////////////
  1462. //
  1463. // Method:
  1464. //
  1465. // CChunkyResChunk::CChunkyResChunk()
  1466. //
  1467. // Purpose:
  1468. //
  1469. // Constructor for CChunkyResChunk.
  1470. //
  1471. // Parameters:
  1472. //
  1473. // None.
  1474. //
  1475. // Return:
  1476. //
  1477. // N/A
  1478. //
  1479. CChunkyResChunk::CChunkyResChunk(CChunkyRes* pChunkyRes)
  1480. : m_ChunkOffset(0)
  1481. , m_pChunkData(NULL)
  1482. , m_ulTempFileOffset(0)
  1483. , m_bPreviouslySpilled(FALSE)
  1484. , m_bModified(FALSE)
  1485. , m_pChunkRes(pChunkyRes)
  1486. , m_bDisableDiskIO(FALSE)
  1487. {
  1488.     HX_ASSERT(m_pChunkRes);
  1489. }
  1490. /////////////////////////////////////////////////////////////////////////////
  1491. //
  1492. // Method:
  1493. //
  1494. // CChunkyResChunk::~CChunkyResChunk()
  1495. //
  1496. // Purpose:
  1497. //
  1498. // Destructor for CChunkyResChunk.
  1499. //
  1500. // Parameters:
  1501. //
  1502. // N/A
  1503. //
  1504. // Return:
  1505. //
  1506. // N/A
  1507. //
  1508. CChunkyResChunk::~CChunkyResChunk()
  1509. {
  1510. HX_RESULT theErr = DiscardDiskData();
  1511. HX_ASSERT(theErr == HXR_OK);
  1512. if (m_pChunkData)
  1513. {
  1514. delete[] m_pChunkData;
  1515. m_pChunkData = NULL;
  1516. }
  1517. while (!m_ValidRanges.IsEmpty())
  1518. {
  1519. ValidRange* pRange = (ValidRange*)m_ValidRanges.RemoveHead();
  1520. delete pRange;
  1521. }
  1522. }
  1523. HX_RESULT CChunkyRes::GetTempFileChunk(CHXDataFile*& pFile,ULONG32& ulTempFileOffset)
  1524. {
  1525. // You should set this to NULL on input.
  1526. HX_ASSERT(pFile == NULL);
  1527. // Get the temporary file...
  1528. HX_RESULT theErr = GetTempFile(pFile);
  1529. if (theErr == HXR_OK)
  1530. {
  1531. // If there are free chunk spaces in the file, use those first
  1532. if (!m_FreeDiskOffsets.IsEmpty())
  1533. {
  1534. ulTempFileOffset = (UINT32)(PTR_INT)m_FreeDiskOffsets.GetTail();
  1535. m_FreeDiskOffsets.RemoveTail();
  1536. }
  1537. else
  1538. {
  1539.     // return the previous next chunk offset...
  1540.     ulTempFileOffset = m_ulNextTempFileChunk;
  1541.     // bump the next chunk offset
  1542.     m_ulNextTempFileChunk += m_ChunkSize;
  1543. }
  1544. }
  1545. return theErr;
  1546. }
  1547. /////////////////////////////////////////////////////////////////////////////
  1548. //
  1549. // Method:
  1550. //
  1551. // CChunkyResChunk::DiscardDiskData()
  1552. //
  1553. // Purpose:
  1554. //
  1555. // Discard the disk data for a chunk. This is normally done on
  1556. // destruction of the chunk when the resource associated with this
  1557. // chunk is discarded from disk, but can also be done when we
  1558. // are downloading a live stream and want to discard chunks that
  1559. // have already been served up to the user.
  1560. //
  1561. // Parameters:
  1562. //
  1563. // None.
  1564. //
  1565. // Return:
  1566. //
  1567. // HX_RESULT
  1568. // Possible errors include: TBD.
  1569. //
  1570. HX_RESULT CChunkyResChunk::DiscardDiskData()
  1571. {
  1572. HX_RESULT theErr = HXR_OK;
  1573. // Remove ourselves from the Memory MRU list...
  1574. LISTPOSITION posMem = m_pChunkRes->m_ChunksMemoryMRU->Find(this);
  1575. if (posMem)
  1576. {
  1577. m_pChunkRes->m_ChunksMemoryMRU->RemoveAt(posMem);
  1578. m_pChunkRes->m_CurMemUsage -= GetSize();
  1579. }
  1580. // Remove ourselves from the Disks MRU list...
  1581. LISTPOSITION posDisk = m_pChunkRes->m_ChunksDiskMRU->Find(this);
  1582. if (posDisk)
  1583. {
  1584. m_pChunkRes->m_ChunksDiskMRU->RemoveAt(posDisk);
  1585. }
  1586. // Reset a bunch of our members in case someone tries
  1587. // to access this chunk after its data has been discarded
  1588. m_ChunkOffset = 0;
  1589. AddValidRange(0, m_pChunkRes->m_ChunkSize, FALSE);
  1590. HX_VECTOR_DELETE(m_pChunkData);
  1591. m_ulTempFileOffset = 0;
  1592. m_bPreviouslySpilled = FALSE;
  1593. m_bModified = FALSE;
  1594. m_bDisableDiskIO = TRUE;
  1595. return theErr;
  1596. }
  1597. /////////////////////////////////////////////////////////////////////////////
  1598. //
  1599. // Method:
  1600. //
  1601. // CChunkyRes::DiscardDiskData()
  1602. //
  1603. // Purpose:
  1604. //
  1605. // Discard the disk data for a chunk. This is normally done on
  1606. // destruction of the chunk when the resource associated with this
  1607. // chunk is discarded from disk.
  1608. //
  1609. // Parameters:
  1610. //
  1611. // None.
  1612. //
  1613. // Return:
  1614. //
  1615. // HX_RESULT
  1616. // Possible errors include: TBD.
  1617. //
  1618. HX_RESULT CChunkyRes::DiscardDiskData()
  1619. {
  1620. HX_RESULT theErr = HXR_OK;
  1621. const char* pFileName = m_strTempFileName;
  1622. if (pFileName && *pFileName)
  1623. {
  1624. #if defined (_MACINTOSH) || defined (_UNIX) || defined(_SYMBIAN) || defined(_OPENWAVE)
  1625. int nRet = unlink(pFileName);
  1626. #else
  1627. int nRet = -1;
  1628. if (DeleteFile(OS_STRING(pFileName)))
  1629.     nRet = 0;
  1630. #endif
  1631. HX_ASSERT(nRet == 0);
  1632. m_strTempFileName = "";
  1633. }
  1634. return theErr;
  1635. }
  1636. HX_RESULT CChunkyRes::GetTempFile(CHXDataFile*& pFile)
  1637. {
  1638. // You should set this to NULL on input.
  1639. HX_ASSERT(pFile == NULL);
  1640. HX_RESULT theErr = HXR_OK;
  1641. const char* pFileName = m_strTempFileName;
  1642. char szTempFileName[_MAX_PATH]; /* Flawfinder: ignore */
  1643. // Create the OS Specific File object...
  1644. pFile = CHXDataFile::Construct();
  1645. if (!pFile)
  1646. {
  1647. theErr = HXR_TEMP_FILE;
  1648. goto exit;
  1649. }
  1650. // If we don't have a filename, then we need to
  1651. // get a temp filename, and we know we are creating
  1652. // a file.
  1653. if (!pFileName || !*pFileName)
  1654. {
  1655. #if defined(_MAC_MACHO) || defined(_MAC_CFM) // GR 7/15/03 other platforms may want a clearer name here too, since 8.3 restrictions don't apply
  1656.   if(!pFile->GetTemporaryFileName("Helix", szTempFileName, _MAX_PATH))
  1657. #else
  1658. if(!pFile->GetTemporaryFileName("PNX",szTempFileName, _MAX_PATH))
  1659. #endif
  1660. {
  1661. goto exit;
  1662. }
  1663. m_strTempFileName = szTempFileName;
  1664. pFileName = m_strTempFileName;
  1665. }
  1666. // Open the file...
  1667. if (!pFileName)
  1668. {
  1669. theErr = HXR_TEMP_FILE;
  1670. goto exit;
  1671. }
  1672. if (!m_bHasBeenOpened)
  1673. {
  1674. // Note: _O_RDWR does not work on the mac. O_RDWR is standard ANSI.
  1675. theErr = pFile->Open(pFileName,O_CREAT + O_RDWR);
  1676. if (!theErr)
  1677. {
  1678. m_bHasBeenOpened = TRUE;
  1679. }
  1680. }
  1681. else
  1682. {
  1683. // Note: _O_RDWR does not work on the mac. O_RDWR is standard ANSI.
  1684. theErr = pFile->Open(pFileName, O_RDWR);
  1685. }
  1686. exit:
  1687. return theErr;
  1688. }