BlMsf.cpp 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. #include "BlMsf.h"
  2. #include "BeefySysLib/MemStream.h"
  3. #include "../Compiler/BfAstAllocator.h"
  4. USING_NS_BF;
  5. #define MSF_SIGNATURE_700 "Microsoft C/C++ MSF 7.00\r\n\032DS\0\0"
  6. #define CV_BLOCK_SIZE 0x1000
  7. #define PTR_ALIGN(ptr, origPtr, alignSize) ptr = ( (origPtr)+( ((ptr - origPtr) + (alignSize - 1)) & ~(alignSize - 1) ) )
  8. //////////////////////////////////////////////////////////////////////////
  9. BlMsfWorkThread::BlMsfWorkThread()
  10. {
  11. mMsf = NULL;
  12. mDone = false;
  13. mSortDirty = false;
  14. }
  15. BlMsfWorkThread::~BlMsfWorkThread()
  16. {
  17. Stop();
  18. }
  19. void BlMsfWorkThread::Stop()
  20. {
  21. mDone = true;
  22. mWorkEvent.Set();
  23. WorkThread::Stop();
  24. }
  25. void BlMsfWorkThread::Run()
  26. {
  27. while (!mDone)
  28. {
  29. if (mSortedWriteBlockWorkQueue.empty())
  30. {
  31. // Only fill in new entries when our queue is empty. This is not a strong ordering guarantee.
  32. bool sortDirty = false;
  33. mCritSect.Lock();
  34. if (!mWriteBlockWorkQueue.empty())
  35. {
  36. sortDirty = true;
  37. mSortedWriteBlockWorkQueue.insert(mSortedWriteBlockWorkQueue.begin(), mWriteBlockWorkQueue.begin(), mWriteBlockWorkQueue.end());
  38. mWriteBlockWorkQueue.clear();
  39. }
  40. mCritSect.Unlock();
  41. BlMsfBlock* block = NULL;
  42. if (sortDirty)
  43. {
  44. std::sort(mSortedWriteBlockWorkQueue.begin(), mSortedWriteBlockWorkQueue.end(), [](BlMsfBlock* lhs, BlMsfBlock* rhs)
  45. {
  46. return lhs->mIdx < rhs->mIdx;
  47. });
  48. }
  49. }
  50. BlMsfBlock* block = NULL;
  51. if (!mSortedWriteBlockWorkQueue.empty())
  52. {
  53. block = mSortedWriteBlockWorkQueue.front();
  54. mSortedWriteBlockWorkQueue.pop_front();
  55. }
  56. if (block == NULL)
  57. {
  58. mWorkEvent.WaitFor();
  59. continue;
  60. }
  61. mMsf->WriteBlock(block);
  62. }
  63. }
  64. void BlMsfWorkThread::Add(BlMsfBlock* block)
  65. {
  66. AutoCrit autoCrit(mCritSect);
  67. mWriteBlockWorkQueue.push_back(block);
  68. mWorkEvent.Set();
  69. mSortDirty = true;
  70. }
  71. //////////////////////////////////////////////////////////////////////////
  72. BlMsf::BlMsf()
  73. {
  74. mCodeView = NULL;
  75. mBlockSize = 0x1000;
  76. mFreePageBitmapIdx = 1;
  77. mFileSize = 0;
  78. mAllocatedFileSize = 0;
  79. mWorkThread.mMsf = this;
  80. }
  81. BlMsf::~BlMsf()
  82. {
  83. for (auto block : mBlocks)
  84. delete block->mData;
  85. }
  86. void BlMsf::WriteBlock(BlMsfBlock* block)
  87. {
  88. if (mAllocatedFileSize > mFileSize)
  89. {
  90. mFS.SetSizeFast(mAllocatedFileSize);
  91. mFileSize = mAllocatedFileSize;
  92. }
  93. int wantPos = block->mIdx * CV_BLOCK_SIZE;
  94. if (wantPos > mFileSize)
  95. {
  96. mFS.SetSizeFast(wantPos);
  97. mFileSize = wantPos;
  98. }
  99. if (mFileSize == wantPos)
  100. mFileSize = wantPos + CV_BLOCK_SIZE;
  101. mFS.SetPos(block->mIdx * CV_BLOCK_SIZE);
  102. mFS.Write(block->mData, CV_BLOCK_SIZE);
  103. delete block->mData;
  104. block->mData = NULL;
  105. }
  106. bool BlMsf::Create(const StringImpl& fileName)
  107. {
  108. mFileName = fileName;
  109. //if (!mFS.Open(fileName, GENERIC_WRITE | GENERIC_READ))
  110. //return false;
  111. if (!mFS.Open(fileName, BfpFileCreateKind_CreateAlways, (BfpFileCreateFlags)(BfpFileCreateFlag_Write | BfpFileCreateFlag_Read)))
  112. return false;
  113. // Header
  114. Alloc(true, false);
  115. // First FPM block
  116. Alloc(true, false);
  117. // 'Old' FPM block
  118. Alloc(true, false);
  119. return true;
  120. }
  121. int BlMsf::Alloc(bool clear, bool skipFPMBlock)
  122. {
  123. int blockIdx = (int)mBlocks.size();
  124. BlMsfBlock* msfBlock = mBlocks.Alloc();
  125. msfBlock->mData = new uint8[CV_BLOCK_SIZE];
  126. if (clear)
  127. memset(msfBlock->mData, 0, CV_BLOCK_SIZE);
  128. msfBlock->mIdx = blockIdx;
  129. if ((skipFPMBlock) && ((blockIdx % CV_BLOCK_SIZE) == mFreePageBitmapIdx))
  130. {
  131. // We need this page as a Free Page Bitmap. This will be triggered once for
  132. // every 16MB of PDB. It should be once every 128MB but isn't because of a
  133. // Microsoft bug in their initial implementation.
  134. return Alloc();
  135. }
  136. mAllocatedFileSize = (int)(mBlocks.size() * CV_BLOCK_SIZE);
  137. return blockIdx;
  138. }
  139. void BlMsf::FlushBlock(int blockIdx)
  140. {
  141. if (mWorkThread.mThread == NULL)
  142. mWorkThread.Start();
  143. mWorkThread.Add(mBlocks[blockIdx]);
  144. }
  145. void BlMsf::Finish(int rootBlockNum, int streamDirLen)
  146. {
  147. mWorkThread.Stop();
  148. int numBlocks = (int)mBlocks.size();
  149. MemStream headerStream(mBlocks[0]->mData, CV_BLOCK_SIZE, false);
  150. headerStream.Write(MSF_SIGNATURE_700, 32);
  151. headerStream.Write((int32)CV_BLOCK_SIZE); // Page size
  152. headerStream.Write((int32)mFreePageBitmapIdx); // FreeBlockMapBlock - always use page 1. It's allowed to flip between 1 and 2.
  153. headerStream.Write((int32)numBlocks); // Total page count
  154. headerStream.Write((int32)streamDirLen);
  155. headerStream.Write((int32)0); // Unknown
  156. headerStream.Write((int32)rootBlockNum);
  157. // Create Free Page Bitmap
  158. BfBitSet bitset;
  159. bitset.Init(numBlocks);
  160. int numBytes = (numBlocks + 7) / 8;
  161. memset(bitset.mBits, 0xFF, numBytes);
  162. for (int i = 0; i < numBlocks; i++)
  163. bitset.Clear(i);
  164. // Bits are written in blocks at block nums (mFreePageBitmapIdx + k*CV_BLOCK_SIZE)
  165. // This is a little strange, but the actual mFreePageBitmapIdx block can only hold enough bits for
  166. // 128MB of pages, so PSBs over that size get spread at 'CV_BLOCK_SIZE' intervals. This is technically
  167. // wrong, as it should be 'CV_BLOCK_SIZE*8', but that's an Microsoft bug that can't be fixed now.
  168. uint8* data = (uint8*)bitset.mBits;
  169. int bytesLeft = numBytes;
  170. int curBlockNum = mFreePageBitmapIdx;
  171. while (bytesLeft > 0)
  172. {
  173. int writeBytes = std::min(bytesLeft, CV_BLOCK_SIZE);
  174. uint8* dataDest = (uint8*)mBlocks[curBlockNum]->mData;
  175. memcpy(dataDest, data, writeBytes);
  176. bytesLeft -= writeBytes;
  177. data += writeBytes;
  178. curBlockNum += CV_BLOCK_SIZE;
  179. }
  180. // Do actual write
  181. for (auto block : mBlocks)
  182. {
  183. if (block->mData != NULL)
  184. WriteBlock(block);
  185. }
  186. mFS.Close();
  187. }