1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-08 19:48:20 +02:00
Beef/IDEHelper/Linker/BlMsf.cpp

229 lines
5.4 KiB
C++
Raw Permalink Normal View History

2019-08-23 11:56:54 -07:00
#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();
}