#pragma warning(disable:4996) #include "BeLibManger.h" #include "BeefySysLib/util/BeefPerf.h" #include "BeefySysLib/util/AllocDebug.h" USING_NS_BF; BeLibManager gBfLibManager; void BeLibEntry::AddSymbol(const StringImpl& sym) { mSymbols.push_back(sym); } ////////////////////////////////////////////////////////////////////////// BeLibFile::BeLibFile() { mFailed = false; } BeLibFile::~BeLibFile() { for (auto& entry : mEntries) delete entry.mValue; for (auto& entry : mOldEntries) delete entry.mValue; } bool BeLibFile::ReadLib() { BP_ZONE("BeLibFile::ReadLib"); char fileId[8]; mOldFileStream.Read(fileId, 8); if (strncmp(fileId, "!\n", 8) != 0) return false; const char* libStrTable = NULL; Dictionary pendingLibEntryMap; int memberIdx = 0; while (true) { int headerFilePos = mOldFileStream.GetPos(); BeLibMemberHeader header; mOldFileStream.Read(&header, sizeof(header)); if (mOldFileStream.mReadPastEnd) break; int len = atoi(header.mSize); if (strncmp(header.mName, "/ ", 2) == 0) { if (memberIdx == 0) { uint8* data = new uint8[len]; mOldFileStream.Read(data, len); int numSymbols = FromBigEndian(*(int32*)data); uint8* strTab = data + 4 + numSymbols * 4; for (int symIdx = 0; symIdx < numSymbols; symIdx++) { const char* str = (char*)strTab; strTab += strlen((char*)strTab) + 1; int offset = FromBigEndian(((int32*)(data + 4))[symIdx]); BeLibEntry* pendingEntry; BeLibEntry** pendingEntryPtr = NULL; if (!pendingLibEntryMap.TryAdd(offset, NULL, &pendingEntryPtr)) { pendingEntry = *pendingEntryPtr; } else { pendingEntry = new BeLibEntry(); pendingEntry->mLibFile = this; *pendingEntryPtr = pendingEntry; } pendingEntry->mSymbols.push_back(str); } delete data; } else { } } else if (strncmp(header.mName, "// ", 3) == 0) { libStrTable = new char[len]; mOldFileStream.Read((uint8*)libStrTable, len); } else { String fileName; if (header.mName[0] == '/') { int tabIdx = atoi(&header.mName[1]); for (int checkIdx = tabIdx; true; checkIdx++) { char c = libStrTable[checkIdx]; if ((c == 0) || (c == '/')) { fileName.Append(&libStrTable[tabIdx], checkIdx - tabIdx); break; } } } else { for (int i = 0; i < 16; i++) { if (header.mName[i] == '/') fileName.Append(&header.mName[0], i); } } BeLibEntry* libEntry = NULL; if (!pendingLibEntryMap.TryGetValue(headerFilePos, &libEntry)) { libEntry = new BeLibEntry(); libEntry->mLibFile = this; } BeLibEntry** namedEntry; if (mOldEntries.TryAdd(fileName, NULL, &namedEntry)) { *namedEntry = libEntry; } else { auto prevEntry = *namedEntry; libEntry->mNextWithSameName = prevEntry->mNextWithSameName; prevEntry->mNextWithSameName = libEntry; } libEntry->mName = fileName; libEntry->mOldDataPos = headerFilePos; libEntry->mLength = len; } len = BF_ALIGN(len, 2); // Even addr mOldFileStream.SetPos(headerFilePos + sizeof(BeLibMemberHeader) + len); memberIdx++; } for (auto& entry : pendingLibEntryMap) { auto libEntry = entry.mValue; // Not used? if (libEntry->mOldDataPos == -1) delete libEntry; } delete libStrTable; return true; } bool BeLibFile::Init(const StringImpl& filePath, bool moveFile) { bool isInitialized = false; if (FileExists(filePath)) { String altName; if (moveFile) { altName = filePath + "_"; if (!::MoveFileA(filePath.c_str(), altName.c_str())) { ::DeleteFileA(altName.c_str()); if (!::MoveFileA(filePath.c_str(), altName.c_str())) { mFilePath = filePath; return false; } } } else altName = filePath; mOldFilePath = altName; if (!mOldFileStream.Open(altName, "rb")) return false; if (!ReadLib()) return false; } String newLibName = filePath; mFilePath = newLibName; return true; } bool BeLibFile::Finish() { BP_ZONE("BeLibFile::Finish"); //mOldEntries.clear(); Dictionary* libEntryMaps[2] = { &mEntries, &mOldEntries }; Array libEntries; bool isAllReferenced = true; for (auto entryMap : libEntryMaps) { for (auto& libEntryPair : *entryMap) { auto libEntry = libEntryPair.mValue; if (libEntry->mReferenced) libEntries.push_back(libEntry); else isAllReferenced = false; } } if ((isAllReferenced) && (mEntries.IsEmpty()) && (mOldFileStream.IsOpen())) { mOldFileStream.Close(); String altName = mFilePath + "_"; if (::MoveFileA(altName.c_str(), mFilePath.c_str())) { return true; // There are no changes } } if (!mFileStream.Open(mFilePath, "wb")) { mFailed = true; return false; } if ((!mOldEntries.IsEmpty()) && (!mOldFileStream.IsOpen())) { // We failed to open the old stream but we still had old entries to references.. mFailed = true; return false; } mFileStream.Write("!\n", 8); std::sort(libEntries.begin(), libEntries.end(), [&](BeLibEntry* lhs, BeLibEntry* rhs) { return lhs->mName < rhs->mName; }); int longNamesSize = 0; int tabSize = 4; // num symbols int numSymbols = 0; for (auto libEntry : libEntries) { if (libEntry->mName.length() > 15) { longNamesSize += (int)libEntry->mName.length() + 2; } for (auto& sym : libEntry->mSymbols) { numSymbols++; tabSize += 4; // Offset tabSize += (int)sym.length() + 1; // String table } } // Determine where all these entries will be placed int predictPos = mFileStream.GetPos() + sizeof(BeLibMemberHeader) + BF_ALIGN(tabSize, 2); if (longNamesSize > 0) predictPos += sizeof(BeLibMemberHeader) + BF_ALIGN(longNamesSize, 2); for (auto libEntry : libEntries) { libEntry->mNewDataPos = predictPos; predictPos += sizeof(BeLibMemberHeader); predictPos += BF_ALIGN(libEntry->mLength, 2); } int tabStartPos = mFileStream.GetPos(); BeLibMemberHeader header; header.Init("/", "0", tabSize); mFileStream.WriteT(header); mFileStream.Write(ToBigEndian((int32)numSymbols)); // Offset table for (auto libEntry : libEntries) { for (auto& sym : libEntry->mSymbols) { mFileStream.Write((int32)ToBigEndian(libEntry->mNewDataPos)); } } // String map table for (auto libEntry : libEntries) { for (auto& sym : libEntry->mSymbols) { mFileStream.Write((uint8*)sym.c_str(), (int)sym.length() + 1); } } int actualTabSize = mFileStream.GetPos() - tabStartPos - sizeof(BeLibMemberHeader); //return true; if ((tabSize % 2) != 0) mFileStream.Write((uint8)0); BF_ASSERT(actualTabSize == tabSize); // Create long names table if (longNamesSize > 0) { header.Init("//", "0", longNamesSize); mFileStream.WriteT(header); for (auto libEntry : libEntries) { if (libEntry->mName.length() > 15) { mFileStream.Write((uint8*)libEntry->mName.c_str(), (int)libEntry->mName.length()); mFileStream.Write("/\n", 2); } } if ((longNamesSize % 2) != 0) mFileStream.Write((uint8)'\n'); } int longNamesPos = 0; for (auto libEntry : libEntries) { int actualPos = mFileStream.GetPos(); BF_ASSERT(actualPos == libEntry->mNewDataPos); String entryName; if (libEntry->mName.length() > 15) { char idxStr[32]; _itoa(longNamesPos, idxStr, 10); entryName = "/"; entryName += idxStr; longNamesPos += (int)libEntry->mName.length() + 2; } else { entryName = libEntry->mName; entryName += "/"; } header.Init(entryName.c_str(), "644", libEntry->mLength); mFileStream.WriteT(header); if (libEntry->mOldDataPos != -1) { uint8* data = new uint8[libEntry->mLength]; mOldFileStream.SetPos(libEntry->mOldDataPos + sizeof(BeLibMemberHeader)); mOldFileStream.Read(data, libEntry->mLength); mFileStream.Write(data, libEntry->mLength); delete data; } else if (libEntry->mData.size() != 0) { mFileStream.Write((uint8*)&libEntry->mData[0], (int)libEntry->mData.size()); } if ((libEntry->mLength % 2) != 0) mFileStream.Write((uint8)0); } mFileStream.Close(); mOldFileStream.Close(); ::DeleteFileA(mOldFilePath.c_str()); return true; } ////////////////////////////////////////////////////////////////////////// BeLibManager::BeLibManager() { /*BeLibFile libFile; libFile.Init("Hey");*/ /*BeLibFile libFile; libFile.Init("c:/beef/IDE/mintest/build/Debug_Win64/minlib/minlib__.lib"); libFile.Finish();*/ /*BeLibFile libFile2; libFile2.Init("c:\\temp\\Beefy2D.lib_new");*/ } BeLibManager::~BeLibManager() { Clear(); } void BeLibManager::Clear() { BP_ZONE("BeLibManager::Clear"); for (auto& entry : mLibFiles) delete entry.mValue; mLibFiles.Clear(); } BeLibEntry* BeLibManager::AddFile(const StringImpl& filePath, void* data, int size) { BP_ZONE("BeLibManager::AddFile"); String fileDir = GetFileDir(filePath); String fileName = GetFileName(filePath); String fixedFileDir = FixPathAndCase(fileDir); AutoCrit autoCrit(mCritSect); BeLibFile* libFile = NULL; // if ((data == NULL) && (!mLibFiles.ContainsKey(fixedFileDir))) // return NULL; BeLibFile** libFilePtr = NULL; if (!mLibFiles.TryAdd(fixedFileDir, NULL, &libFilePtr)) { libFile = *libFilePtr; } else { libFile = new BeLibFile(); *libFilePtr = libFile; String libPath = GetLibFilePath(filePath); libFile->Init(libPath, true); } if (libFile->mFailed) return NULL; BeLibEntry* oldEntry = NULL; if (libFile->mOldEntries.TryGetValue(fileName, &oldEntry)) { if (data == NULL) { oldEntry->mReferenced = true; return oldEntry; } delete oldEntry; libFile->mOldEntries.Remove(fileName); } else { if (data == NULL) return NULL; } BeLibEntry* libEntry = NULL; BeLibEntry** libEntryPtr = NULL; libFile->mEntries.TryAdd(fileName, NULL, &libEntryPtr); if (*libEntryPtr != NULL) { // It's possible that we rebuild a type (generic, probably), decide we don't have any refs so we delete the type, // but then we specialize methods and then have to recreate it. Thus two entries here. delete *libEntryPtr; } libEntry = new BeLibEntry(); libEntry->mLibFile = libFile; *libEntryPtr = libEntry; libEntry->mReferenced = true; libEntry->mName = fileName; libEntry->mData.Insert(0, (uint8*)data, size); libEntry->mLength = size; return libEntry; } bool BeLibManager::AddUsedFileName(const StringImpl& fileName) { return AddFile(fileName, NULL, -1) != NULL; } void BeLibManager::Finish() { BP_ZONE("BeLibManager::Finish"); AutoCrit autoCrit(mCritSect); for (auto& libPair : mLibFiles) { auto libFile = libPair.mValue; if (!libFile->mFilePath.IsEmpty()) { if (!libFile->Finish()) { mErrors.Add(StrFormat("Failed to write lib file '%s'", libFile->mFilePath.c_str())); } } delete libFile; } mLibFiles.Clear(); } String BeLibManager::GetLibFilePath(const StringImpl& objFilePath) { String fileDir = RemoveTrailingSlash(GetFileDir(objFilePath)); return fileDir + "/" + GetFileName(fileDir) + "__.lib"; } BeLibManager* BeLibManager::Get() { return &gBfLibManager; }