mirror of
https://github.com/beefytech/Beef.git
synced 2025-06-08 19:48:20 +02:00
229 lines
5.4 KiB
C++
229 lines
5.4 KiB
C++
![]() |
#include "BlMsf.h"
|
||
|
#include "BeefySysLib/MemStream.h"
|
||
|
#include "../Compiler/BfAstAllocator.h"
|
||
|
|
||
|
USING_NS_BF;
|
||
|
|
||
|
#define MSF_SIGNATURE_700 "Microsoft C/C++ MSF 7.00\r\n\032DS\0\0"
|
||
|
#define CV_BLOCK_SIZE 0x1000
|
||
|
#define PTR_ALIGN(ptr, origPtr, alignSize) ptr = ( (origPtr)+( ((ptr - origPtr) + (alignSize - 1)) & ~(alignSize - 1) ) )
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
|
||
|
BlMsfWorkThread::BlMsfWorkThread()
|
||
|
{
|
||
|
mMsf = NULL;
|
||
|
mDone = false;
|
||
|
mSortDirty = false;
|
||
|
}
|
||
|
|
||
|
BlMsfWorkThread::~BlMsfWorkThread()
|
||
|
{
|
||
|
Stop();
|
||
|
}
|
||
|
|
||
|
void BlMsfWorkThread::Stop()
|
||
|
{
|
||
|
mDone = true;
|
||
|
mWorkEvent.Set();
|
||
|
WorkThread::Stop();
|
||
|
}
|
||
|
|
||
|
void BlMsfWorkThread::Run()
|
||
|
{
|
||
|
while (!mDone)
|
||
|
{
|
||
|
if (mSortedWriteBlockWorkQueue.empty())
|
||
|
{
|
||
|
// Only fill in new entries when our queue is empty. This is not a strong ordering guarantee.
|
||
|
bool sortDirty = false;
|
||
|
mCritSect.Lock();
|
||
|
if (!mWriteBlockWorkQueue.empty())
|
||
|
{
|
||
|
sortDirty = true;
|
||
|
mSortedWriteBlockWorkQueue.insert(mSortedWriteBlockWorkQueue.begin(), mWriteBlockWorkQueue.begin(), mWriteBlockWorkQueue.end());
|
||
|
mWriteBlockWorkQueue.clear();
|
||
|
}
|
||
|
mCritSect.Unlock();
|
||
|
|
||
|
BlMsfBlock* block = NULL;
|
||
|
if (sortDirty)
|
||
|
{
|
||
|
std::sort(mSortedWriteBlockWorkQueue.begin(), mSortedWriteBlockWorkQueue.end(), [](BlMsfBlock* lhs, BlMsfBlock* rhs)
|
||
|
{
|
||
|
return lhs->mIdx < rhs->mIdx;
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
BlMsfBlock* block = NULL;
|
||
|
if (!mSortedWriteBlockWorkQueue.empty())
|
||
|
{
|
||
|
block = mSortedWriteBlockWorkQueue.front();
|
||
|
mSortedWriteBlockWorkQueue.pop_front();
|
||
|
}
|
||
|
|
||
|
if (block == NULL)
|
||
|
{
|
||
|
mWorkEvent.WaitFor();
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
mMsf->WriteBlock(block);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void BlMsfWorkThread::Add(BlMsfBlock* block)
|
||
|
{
|
||
|
AutoCrit autoCrit(mCritSect);
|
||
|
mWriteBlockWorkQueue.push_back(block);
|
||
|
mWorkEvent.Set();
|
||
|
mSortDirty = true;
|
||
|
}
|
||
|
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////////
|
||
|
BlMsf::BlMsf()
|
||
|
{
|
||
|
mCodeView = NULL;
|
||
|
mBlockSize = 0x1000;
|
||
|
mFreePageBitmapIdx = 1;
|
||
|
mFileSize = 0;
|
||
|
mAllocatedFileSize = 0;
|
||
|
|
||
|
mWorkThread.mMsf = this;
|
||
|
}
|
||
|
|
||
|
BlMsf::~BlMsf()
|
||
|
{
|
||
|
for (auto block : mBlocks)
|
||
|
delete block->mData;
|
||
|
}
|
||
|
|
||
|
void BlMsf::WriteBlock(BlMsfBlock* block)
|
||
|
{
|
||
|
if (mAllocatedFileSize > mFileSize)
|
||
|
{
|
||
|
mFS.SetSizeFast(mAllocatedFileSize);
|
||
|
mFileSize = mAllocatedFileSize;
|
||
|
}
|
||
|
|
||
|
int wantPos = block->mIdx * CV_BLOCK_SIZE;
|
||
|
if (wantPos > mFileSize)
|
||
|
{
|
||
|
mFS.SetSizeFast(wantPos);
|
||
|
mFileSize = wantPos;
|
||
|
}
|
||
|
|
||
|
if (mFileSize == wantPos)
|
||
|
mFileSize = wantPos + CV_BLOCK_SIZE;
|
||
|
|
||
|
mFS.SetPos(block->mIdx * CV_BLOCK_SIZE);
|
||
|
mFS.Write(block->mData, CV_BLOCK_SIZE);
|
||
|
delete block->mData;
|
||
|
block->mData = NULL;
|
||
|
}
|
||
|
|
||
|
bool BlMsf::Create(const StringImpl& fileName)
|
||
|
{
|
||
|
mFileName = fileName;
|
||
|
//if (!mFS.Open(fileName, GENERIC_WRITE | GENERIC_READ))
|
||
|
//return false;
|
||
|
|
||
|
if (!mFS.Open(fileName, BfpFileCreateKind_CreateAlways, (BfpFileCreateFlags)(BfpFileCreateFlag_Write | BfpFileCreateFlag_Read)))
|
||
|
return false;
|
||
|
|
||
|
// Header
|
||
|
Alloc(true, false);
|
||
|
|
||
|
// First FPM block
|
||
|
Alloc(true, false);
|
||
|
|
||
|
// 'Old' FPM block
|
||
|
Alloc(true, false);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
int BlMsf::Alloc(bool clear, bool skipFPMBlock)
|
||
|
{
|
||
|
int blockIdx = (int)mBlocks.size();
|
||
|
|
||
|
BlMsfBlock* msfBlock = mBlocks.Alloc();
|
||
|
msfBlock->mData = new uint8[CV_BLOCK_SIZE];
|
||
|
if (clear)
|
||
|
memset(msfBlock->mData, 0, CV_BLOCK_SIZE);
|
||
|
msfBlock->mIdx = blockIdx;
|
||
|
|
||
|
if ((skipFPMBlock) && ((blockIdx % CV_BLOCK_SIZE) == mFreePageBitmapIdx))
|
||
|
{
|
||
|
// We need this page as a Free Page Bitmap. This will be triggered once for
|
||
|
// every 16MB of PDB. It should be once every 128MB but isn't because of a
|
||
|
// Microsoft bug in their initial implementation.
|
||
|
return Alloc();
|
||
|
}
|
||
|
|
||
|
mAllocatedFileSize = (int)(mBlocks.size() * CV_BLOCK_SIZE);
|
||
|
return blockIdx;
|
||
|
}
|
||
|
|
||
|
void BlMsf::FlushBlock(int blockIdx)
|
||
|
{
|
||
|
if (mWorkThread.mThread == NULL)
|
||
|
mWorkThread.Start();
|
||
|
mWorkThread.Add(mBlocks[blockIdx]);
|
||
|
}
|
||
|
|
||
|
void BlMsf::Finish(int rootBlockNum, int streamDirLen)
|
||
|
{
|
||
|
mWorkThread.Stop();
|
||
|
|
||
|
int numBlocks = (int)mBlocks.size();
|
||
|
|
||
|
MemStream headerStream(mBlocks[0]->mData, CV_BLOCK_SIZE, false);
|
||
|
headerStream.Write(MSF_SIGNATURE_700, 32);
|
||
|
headerStream.Write((int32)CV_BLOCK_SIZE); // Page size
|
||
|
headerStream.Write((int32)mFreePageBitmapIdx); // FreeBlockMapBlock - always use page 1. It's allowed to flip between 1 and 2.
|
||
|
headerStream.Write((int32)numBlocks); // Total page count
|
||
|
headerStream.Write((int32)streamDirLen);
|
||
|
headerStream.Write((int32)0); // Unknown
|
||
|
headerStream.Write((int32)rootBlockNum);
|
||
|
|
||
|
// Create Free Page Bitmap
|
||
|
BfBitSet bitset;
|
||
|
bitset.Init(numBlocks);
|
||
|
int numBytes = (numBlocks + 7) / 8;
|
||
|
memset(bitset.mBits, 0xFF, numBytes);
|
||
|
for (int i = 0; i < numBlocks; i++)
|
||
|
bitset.Clear(i);
|
||
|
|
||
|
// Bits are written in blocks at block nums (mFreePageBitmapIdx + k*CV_BLOCK_SIZE)
|
||
|
// This is a little strange, but the actual mFreePageBitmapIdx block can only hold enough bits for
|
||
|
// 128MB of pages, so PSBs over that size get spread at 'CV_BLOCK_SIZE' intervals. This is technically
|
||
|
// wrong, as it should be 'CV_BLOCK_SIZE*8', but that's an Microsoft bug that can't be fixed now.
|
||
|
uint8* data = (uint8*)bitset.mBits;
|
||
|
int bytesLeft = numBytes;
|
||
|
int curBlockNum = mFreePageBitmapIdx;
|
||
|
while (bytesLeft > 0)
|
||
|
{
|
||
|
int writeBytes = std::min(bytesLeft, CV_BLOCK_SIZE);
|
||
|
|
||
|
uint8* dataDest = (uint8*)mBlocks[curBlockNum]->mData;
|
||
|
memcpy(dataDest, data, writeBytes);
|
||
|
bytesLeft -= writeBytes;
|
||
|
data += writeBytes;
|
||
|
|
||
|
curBlockNum += CV_BLOCK_SIZE;
|
||
|
}
|
||
|
|
||
|
// Do actual write
|
||
|
for (auto block : mBlocks)
|
||
|
{
|
||
|
if (block->mData != NULL)
|
||
|
WriteBlock(block);
|
||
|
}
|
||
|
|
||
|
mFS.Close();
|
||
|
}
|