From 8c289ec126edf510a542c86e4c9df1787a37acb6 Mon Sep 17 00:00:00 2001 From: Callum Prentice Date: Fri, 11 Sep 2020 10:00:17 -0700 Subject: [PATCH 01/85] Effective rename of DRTVWR-506-simple since there is no way to rename a branch. Cloned canonical viewer into DRTVWR-519, copied over the files that changed from DRTVWR-506-simple and pushed back. Once I am satisfied everything is correct, DRTVWR-506-simple will be removed --- autobuild.xml | 6 +- indra/cmake/00-Common.cmake | 10 +- indra/llcommon/llerror.cpp | 61 ++- indra/llvfs/llvfile.cpp | 719 +++++++++++++++++++---------- indra/llvfs/llvfile.h | 19 +- indra/newview/llappviewerwin32.cpp | 2 +- indra/newview/llviewerstats.cpp | 2 +- 7 files changed, 545 insertions(+), 274 deletions(-) diff --git a/autobuild.xml b/autobuild.xml index 51515b3696..c9fb9cd4e9 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -2187,16 +2187,16 @@ archive hash - 8501cbaa7e0f254614694da784a9c61c + b677ee43822212f0a27c838dc8bf3623 url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/64944/606925/llca-202008010216.546021-common-546021.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/67622/646614/llca-202009010215.548269-common-548269.tar.bz2 name common version - 202008010216.546021 + 202009010215.548269 llphysicsextensions_source diff --git a/indra/cmake/00-Common.cmake b/indra/cmake/00-Common.cmake index 865c057e33..9f8ee34034 100644 --- a/indra/cmake/00-Common.cmake +++ b/indra/cmake/00-Common.cmake @@ -63,8 +63,14 @@ if (WINDOWS) # Without PreferredToolArchitecture=x64, as of 2020-06-26 the 32-bit # compiler on our TeamCity build hosts has started running out of virtual # memory for the precompiled header file. - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP /p:PreferredToolArchitecture=x64") - + # CP changed to only append the flag for 32bit builds - on 64bit builds, + # locally at least, the build output is spammed with 1000s of 'D9002' + # warnings about this switch being ignored. + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") + if( ADDRESS_SIZE EQUAL 32 ) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /p:PreferredToolArchitecture=x64") + endif() + set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} /Zo" CACHE STRING "C++ compiler release-with-debug options" FORCE) diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp index 411412c883..832813ba3f 100644 --- a/indra/llcommon/llerror.cpp +++ b/indra/llcommon/llerror.cpp @@ -194,23 +194,64 @@ namespace { { return LLError::getEnabledLogTypesMask() & 0x04; } - + + LL_FORCE_INLINE std::string createBoldANSI() + { + std::string ansi_code; + ansi_code += '\033'; + ansi_code += "["; + ansi_code += "1"; + ansi_code += "m"; + + return ansi_code; + } + + LL_FORCE_INLINE std::string createResetANSI() + { + std::string ansi_code; + ansi_code += '\033'; + ansi_code += "["; + ansi_code += "0"; + ansi_code += "m"; + + return ansi_code; + } + LL_FORCE_INLINE std::string createANSI(const std::string& color) { std::string ansi_code; - ansi_code += '\033'; - ansi_code += "["; - ansi_code += color; + ansi_code += '\033'; + ansi_code += "["; + ansi_code += "38;5;"; + ansi_code += color; ansi_code += "m"; + return ansi_code; } virtual void recordMessage(LLError::ELevel level, const std::string& message) override { - static std::string s_ansi_error = createANSI("31"); // red - static std::string s_ansi_warn = createANSI("34"); // blue - static std::string s_ansi_debug = createANSI("35"); // magenta + // The default colors for error, warn and debug are now a bit more pastel + // and easier to read on the default (black) terminal background but you + // now have the option to set the color of each via an environment variables: + // LL_ANSI_ERROR_COLOR_CODE (default is red) + // LL_ANSI_WARN_COLOR_CODE (default is blue) + // LL_ANSI_DEBUG_COLOR_CODE (default is magenta) + // The list of color codes can be found in many places but I used this page: + // https://www.lihaoyi.com/post/BuildyourownCommandLinewithANSIescapecodes.html#256-colors + // (Note: you may need to restart Visual Studio to pick environment changes) + char* val = nullptr; + std::string s_ansi_error_code = "160"; + if ((val = getenv("LL_ANSI_ERROR_COLOR_CODE")) != nullptr) s_ansi_error_code = std::string(val); + std::string s_ansi_warn_code = "33"; + if ((val = getenv("LL_ANSI_WARN_COLOR_CODE")) != nullptr) s_ansi_warn_code = std::string(val); + std::string s_ansi_debug_code = "177"; + if ((val = getenv("LL_ANSI_DEBUG_COLOR_CODE")) != nullptr) s_ansi_debug_code = std::string(val); + + static std::string s_ansi_error = createANSI(s_ansi_error_code); // default is red + static std::string s_ansi_warn = createANSI(s_ansi_warn_code); // default is blue + static std::string s_ansi_debug = createANSI(s_ansi_debug_code); // default is magenta if (mUseANSI) { @@ -229,11 +270,11 @@ namespace { LL_FORCE_INLINE void writeANSI(const std::string& ansi_code, const std::string& message) { - static std::string s_ansi_bold = createANSI("1"); // bold - static std::string s_ansi_reset = createANSI("0"); // reset + static std::string s_ansi_bold = createBoldANSI(); // bold text + static std::string s_ansi_reset = createResetANSI(); // reset // ANSI color code escape sequence, message, and reset in one fprintf call // Default all message levels to bold so we can distinguish our own messages from those dumped by subprocesses and libraries. - fprintf(stderr, "%s%s%s\n%s", s_ansi_bold.c_str(), ansi_code.c_str(), message.c_str(), s_ansi_reset.c_str() ); + fprintf(stderr, "%s%s\n%s", ansi_code.c_str(), message.c_str(), s_ansi_reset.c_str() ); } static bool checkANSI(void) diff --git a/indra/llvfs/llvfile.cpp b/indra/llvfs/llvfile.cpp index b8588e99f4..797b431b93 100644 --- a/indra/llvfs/llvfile.cpp +++ b/indra/llvfs/llvfile.cpp @@ -35,6 +35,9 @@ #include "llmemory.h" #include "llvfs.h" +#include +#include "lldir.h" + const S32 LLVFile::READ = 0x00000001; const S32 LLVFile::WRITE = 0x00000002; const S32 LLVFile::READ_WRITE = 0x00000003; // LLVFile::READ & LLVFile::WRITE @@ -43,8 +46,8 @@ const S32 LLVFile::APPEND = 0x00000006; // 0x00000004 & LLVFile::WRITE static LLTrace::BlockTimerStatHandle FTM_VFILE_WAIT("VFile Wait"); //---------------------------------------------------------------------------- -LLVFSThread* LLVFile::sVFSThread = NULL; -BOOL LLVFile::sAllocdVFSThread = FALSE; +//CP LLVFSThread* LLVFile::sVFSThread = NULL; +//CP BOOL LLVFile::sAllocdVFSThread = FALSE; //---------------------------------------------------------------------------- //============================================================================ @@ -52,137 +55,256 @@ BOOL LLVFile::sAllocdVFSThread = FALSE; LLVFile::LLVFile(LLVFS *vfs, const LLUUID &file_id, const LLAssetType::EType file_type, S32 mode) { mFileType = file_type; - mFileID = file_id; mPosition = 0; + mBytesRead = 0; + mReadComplete = FALSE; mMode = mode; - mVFS = vfs; + //CP mVFS = vfs; - mBytesRead = 0; - mHandle = LLVFSThread::nullHandle(); - mPriority = 128.f; + //CP mHandle = LLVFSThread::nullHandle(); + //CP mPriority = 128.f; - mVFS->incLock(mFileID, mFileType, VFSLOCK_OPEN); + //CP mVFS->incLock(mFileID, mFileType, VFSLOCK_OPEN); } LLVFile::~LLVFile() { - if (!isReadComplete()) - { - if (mHandle != LLVFSThread::nullHandle()) - { - if (!(mMode & LLVFile::WRITE)) - { - //LL_WARNS() << "Destroying LLVFile with pending async read/write, aborting..." << LL_ENDL; - sVFSThread->setFlags(mHandle, LLVFSThread::FLAG_AUTO_COMPLETE | LLVFSThread::FLAG_ABORT); - } - else // WRITE - { - sVFSThread->setFlags(mHandle, LLVFSThread::FLAG_AUTO_COMPLETE); - } - } - } - mVFS->decLock(mFileID, mFileType, VFSLOCK_OPEN); + //CP BEGIN + //if (!isReadComplete()) + //{ + // if (mHandle != LLVFSThread::nullHandle()) + // { + // if (!(mMode & LLVFile::WRITE)) + // { + // //LL_WARNS() << "Destroying LLVFile with pending async read/write, aborting..." << LL_ENDL; + // sVFSThread->setFlags(mHandle, LLVFSThread::FLAG_AUTO_COMPLETE | LLVFSThread::FLAG_ABORT); + // } + // else // WRITE + // { + // sVFSThread->setFlags(mHandle, LLVFSThread::FLAG_AUTO_COMPLETE); + // } + // } + //} + //mVFS->decLock(mFileID, mFileType, VFSLOCK_OPEN); + //CP END +} + +const std::string assetTypeToString(LLAssetType::EType at) +{ + /** + * Make use of the C++17 (or is it 14) feature that allows + * for inline initialization of an std::map<> + */ + typedef std::map asset_type_to_name_t; + asset_type_to_name_t asset_type_to_name = + { + { LLAssetType::AT_TEXTURE, "TEXTURE" }, + { LLAssetType::AT_SOUND, "SOUND" }, + { LLAssetType::AT_CALLINGCARD, "CALLINGCARD" }, + { LLAssetType::AT_LANDMARK, "LANDMARK" }, + { LLAssetType::AT_SCRIPT, "SCRIPT" }, + { LLAssetType::AT_CLOTHING, "CLOTHING" }, + { LLAssetType::AT_OBJECT, "OBJECT" }, + { LLAssetType::AT_NOTECARD, "NOTECARD" }, + { LLAssetType::AT_CATEGORY, "CATEGORY" }, + { LLAssetType::AT_LSL_TEXT, "LSL_TEXT" }, + { LLAssetType::AT_LSL_BYTECODE, "LSL_BYTECODE" }, + { LLAssetType::AT_TEXTURE_TGA, "TEXTURE_TGA" }, + { LLAssetType::AT_BODYPART, "BODYPART" }, + { LLAssetType::AT_SOUND_WAV, "SOUND_WAV" }, + { LLAssetType::AT_IMAGE_TGA, "IMAGE_TGA" }, + { LLAssetType::AT_IMAGE_JPEG, "IMAGE_JPEG" }, + { LLAssetType::AT_ANIMATION, "ANIMATION" }, + { LLAssetType::AT_GESTURE, "GESTURE" }, + { LLAssetType::AT_SIMSTATE, "SIMSTATE" }, + { LLAssetType::AT_LINK, "LINK" }, + { LLAssetType::AT_LINK_FOLDER, "LINK_FOLDER" }, + { LLAssetType::AT_MARKETPLACE_FOLDER, "MARKETPLACE_FOLDER" }, + { LLAssetType::AT_WIDGET, "WIDGET" }, + { LLAssetType::AT_PERSON, "PERSON" }, + { LLAssetType::AT_MESH, "MESH" }, + { LLAssetType::AT_UNKNOWN, "UNKNOWN" } + }; + + asset_type_to_name_t::iterator iter = asset_type_to_name.find(at); + if (iter != asset_type_to_name.end()) + { + return iter->second; + } + + return std::string("UNKNOWN"); +} + +const std::string idToFilepath(const std::string id, LLAssetType::EType at) +{ + /** + * For the moment this is just {UUID}_{ASSET_TYPE}.txt but of + * course, will be greatly expanded upon + */ + std::ostringstream ss; + ss << "00vfs_"; + ss << id; + ss << "_"; + ss << assetTypeToString(at); + ss << ".txt"; + + const std::string filepath = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, ss.str()); + + return filepath; } BOOL LLVFile::read(U8 *buffer, S32 bytes, BOOL async, F32 priority) { - if (! (mMode & READ)) - { - LL_WARNS() << "Attempt to read from file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << LL_ENDL; - return FALSE; - } + //CP BEGIN + //if (! (mMode & READ)) + //{ + // LL_WARNS() << "Attempt to read from file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << LL_ENDL; + // return FALSE; + //} + + //if (mHandle != LLVFSThread::nullHandle()) + //{ + // LL_WARNS() << "Attempt to read from vfile object " << mFileID << " with pending async operation" << LL_ENDL; + // return FALSE; + //} + //mPriority = priority; + //CP END - if (mHandle != LLVFSThread::nullHandle()) - { - LL_WARNS() << "Attempt to read from vfile object " << mFileID << " with pending async operation" << LL_ENDL; - return FALSE; - } - mPriority = priority; - BOOL success = TRUE; + mReadComplete = FALSE; + + std::string id; + mFileID.toString(id); + const std::string filename = idToFilepath(id, mFileType); + + std::ifstream file(filename, std::ios::binary); + if (file.is_open()) + { + file.seekg(mPosition, std::ios::beg); + + file.read((char*)buffer, bytes); + + if (file) + { + mBytesRead = bytes; + } + else + { + mBytesRead = file.gcount(); + } + + file.close(); + + mPosition += mBytesRead; + if (!mBytesRead) + { + success = FALSE; + } + + mReadComplete = TRUE; + } + + return success; + // We can't do a read while there are pending async writes - waitForLock(VFSLOCK_APPEND); - + //CP waitForLock(VFSLOCK_APPEND); + + //CP BEGIN // *FIX: (?) - if (async) - { - mHandle = sVFSThread->read(mVFS, mFileID, mFileType, buffer, mPosition, bytes, threadPri()); - } - else - { - // We can't do a read while there are pending async writes on this file - mBytesRead = sVFSThread->readImmediate(mVFS, mFileID, mFileType, buffer, mPosition, bytes); - mPosition += mBytesRead; - if (! mBytesRead) - { - success = FALSE; - } - } + //if (async) + //{ + // mHandle = sVFSThread->read(mVFS, mFileID, mFileType, buffer, mPosition, bytes, threadPri()); + //} + //else + //{ + // // We can't do a read while there are pending async writes on this file + // mBytesRead = sVFSThread->readImmediate(mVFS, mFileID, mFileType, buffer, mPosition, bytes); + // mPosition += mBytesRead; + // if (! mBytesRead) + // { + // success = FALSE; + // } + //} - return success; + //return success; + + //CP END } -//static -U8* LLVFile::readFile(LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type, S32* bytes_read) -{ - U8 *data; - LLVFile file(vfs, uuid, type, LLVFile::READ); - S32 file_size = file.getSize(); - if (file_size == 0) - { - // File is empty. - data = NULL; - } - else - { - data = (U8*) ll_aligned_malloc<16>(file_size); - file.read(data, file_size); /* Flawfinder: ignore */ - - if (file.getLastBytesRead() != (S32)file_size) - { - ll_aligned_free<16>(data); - data = NULL; - file_size = 0; - } - } - if (bytes_read) - { - *bytes_read = file_size; - } - return data; -} - -void LLVFile::setReadPriority(const F32 priority) -{ - mPriority = priority; - if (mHandle != LLVFSThread::nullHandle()) - { - sVFSThread->setPriority(mHandle, threadPri()); - } -} +//CP BEGIN +////static +//U8* LLVFile::readFile(LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type, S32* bytes_read) +//{ +// U8 *data; +// LLVFile file(vfs, uuid, type, LLVFile::READ); +// S32 file_size = file.getSize(); +// if (file_size == 0) +// { +// // File is empty. +// data = NULL; +// } +// else +// { +// data = (U8*) ll_aligned_malloc<16>(file_size); +// file.read(data, file_size); /* Flawfinder: ignore */ +// +// if (file.getLastBytesRead() != (S32)file_size) +// { +// ll_aligned_free<16>(data); +// data = NULL; +// file_size = 0; +// } +// } +// if (bytes_read) +// { +// *bytes_read = file_size; +// } +// return data; +//} +//CP END + +//CP BEGIN +//void LLVFile::setReadPriority(const F32 priority) +//{ +// mPriority = priority; +// if (mHandle != LLVFSThread::nullHandle()) +// { +// sVFSThread->setPriority(mHandle, threadPri()); +// } +//} +//CP END BOOL LLVFile::isReadComplete() { - BOOL res = TRUE; - if (mHandle != LLVFSThread::nullHandle()) - { - LLVFSThread::Request* req = (LLVFSThread::Request*)sVFSThread->getRequest(mHandle); - LLVFSThread::status_t status = req->getStatus(); - if (status == LLVFSThread::STATUS_COMPLETE) - { - mBytesRead = req->getBytesRead(); - mPosition += mBytesRead; - sVFSThread->completeRequest(mHandle); - mHandle = LLVFSThread::nullHandle(); - } - else - { - res = FALSE; - } - } - return res; + if (mReadComplete) + { + return TRUE; + } + + return FALSE; + + //CP BEGIN + //BOOL res = TRUE; + //if (mHandle != LLVFSThread::nullHandle()) + //{ + // LLVFSThread::Request* req = (LLVFSThread::Request*)sVFSThread->getRequest(mHandle); + // LLVFSThread::status_t status = req->getStatus(); + // if (status == LLVFSThread::STATUS_COMPLETE) + // { + // mBytesRead = req->getBytesRead(); + // mPosition += mBytesRead; + // sVFSThread->completeRequest(mHandle); + // mHandle = LLVFSThread::nullHandle(); + // } + // else + // { + // res = FALSE; + // } + //} + //return res; + //CP END } S32 LLVFile::getLastBytesRead() @@ -197,48 +319,81 @@ BOOL LLVFile::eof() BOOL LLVFile::write(const U8 *buffer, S32 bytes) { - if (! (mMode & WRITE)) - { - LL_WARNS() << "Attempt to write to file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << LL_ENDL; - } - if (mHandle != LLVFSThread::nullHandle()) - { - LL_ERRS() << "Attempt to write to vfile object " << mFileID << " with pending async operation" << LL_ENDL; - return FALSE; - } - BOOL success = TRUE; - - // *FIX: allow async writes? potential problem wit mPosition... - if (mMode == APPEND) // all appends are async (but WRITEs are not) - { - U8* writebuf = new U8[bytes]; - memcpy(writebuf, buffer, bytes); - S32 offset = -1; - mHandle = sVFSThread->write(mVFS, mFileID, mFileType, - writebuf, offset, bytes, - LLVFSThread::FLAG_AUTO_COMPLETE | LLVFSThread::FLAG_AUTO_DELETE); - mHandle = LLVFSThread::nullHandle(); // FLAG_AUTO_COMPLETE means we don't track this - } - else - { - // We can't do a write while there are pending reads or writes on this file - waitForLock(VFSLOCK_READ); - waitForLock(VFSLOCK_APPEND); + std::string id_str; + mFileID.toString(id_str); + const std::string filename = idToFilepath(id_str, mFileType); - S32 pos = (mMode & APPEND) == APPEND ? -1 : mPosition; + BOOL success = FALSE; - S32 wrote = sVFSThread->writeImmediate(mVFS, mFileID, mFileType, (U8*)buffer, pos, bytes); + if (mMode == APPEND) + { + std::ofstream ofs(filename, std::ios::app | std::ios::binary); + if (ofs) + { + ofs.write((const char*)buffer, bytes); - mPosition += wrote; - - if (wrote < bytes) - { - LL_WARNS() << "Tried to write " << bytes << " bytes, actually wrote " << wrote << LL_ENDL; + success = TRUE; + } + } + else + { + std::ofstream ofs(filename, std::ios::binary); + if (ofs) + { + ofs.write((const char*)buffer, bytes); - success = FALSE; - } - } - return success; + mPosition += bytes; + + success = TRUE; + } + } + + return success; + + //CP BEGIN + //if (! (mMode & WRITE)) + //{ + // LL_WARNS() << "Attempt to write to file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << LL_ENDL; + //} + //if (mHandle != LLVFSThread::nullHandle()) + //{ + // LL_ERRS() << "Attempt to write to vfile object " << mFileID << " with pending async operation" << LL_ENDL; + // return FALSE; + //} + //BOOL success = TRUE; + // + //// *FIX: allow async writes? potential problem wit mPosition... + //if (mMode == APPEND) // all appends are async (but WRITEs are not) + //{ + // U8* writebuf = new U8[bytes]; + // memcpy(writebuf, buffer, bytes); + // S32 offset = -1; + // mHandle = sVFSThread->write(mVFS, mFileID, mFileType, + // writebuf, offset, bytes, + // LLVFSThread::FLAG_AUTO_COMPLETE | LLVFSThread::FLAG_AUTO_DELETE); + // mHandle = LLVFSThread::nullHandle(); // FLAG_AUTO_COMPLETE means we don't track this + //} + //else + //{ + // // We can't do a write while there are pending reads or writes on this file + // waitForLock(VFSLOCK_READ); + // waitForLock(VFSLOCK_APPEND); + + // S32 pos = (mMode & APPEND) == APPEND ? -1 : mPosition; + + // S32 wrote = sVFSThread->writeImmediate(mVFS, mFileID, mFileType, (U8*)buffer, pos, bytes); + + // mPosition += wrote; + // + // if (wrote < bytes) + // { + // LL_WARNS() << "Tried to write " << bytes << " bytes, actually wrote " << wrote << LL_ENDL; + + // success = FALSE; + // } + //} + //return success; + //CP END } //static @@ -251,11 +406,13 @@ BOOL LLVFile::writeFile(const U8 *buffer, S32 bytes, LLVFS *vfs, const LLUUID &u BOOL LLVFile::seek(S32 offset, S32 origin) { - if (mMode == APPEND) - { - LL_WARNS() << "Attempt to seek on append-only file" << LL_ENDL; - return FALSE; - } + //CP BEG + //if (mMode == APPEND) + //{ + // LL_WARNS() << "Attempt to seek on append-only file" << LL_ENDL; + // return FALSE; + //} + //CP END if (-1 == origin) { @@ -292,146 +449,212 @@ S32 LLVFile::tell() const S32 LLVFile::getSize() { - waitForLock(VFSLOCK_APPEND); - S32 size = mVFS->getSize(mFileID, mFileType); + std::string id_str; + mFileID.toString(id_str); + const std::string filename = idToFilepath(id_str, mFileType); - return size; + S32 file_size = 0; + std::ifstream file(filename, std::ios::binary); + if (file.is_open()) + { + file.seekg(0, std::ios::end); + file_size = file.tellg(); + } + return file_size; + + //CP BEG + //waitForLock(VFSLOCK_APPEND); + //S32 size = mVFS->getSize(mFileID, mFileType); + + //return size; + //CP END } S32 LLVFile::getMaxSize() { - S32 size = mVFS->getMaxSize(mFileID, mFileType); + // offer up a huge size since we don't care what the max is + return INT_MAX; - return size; + //CP BEG + //S32 size = mVFS->getMaxSize(mFileID, mFileType); + + //return size; + //CP END } BOOL LLVFile::setMaxSize(S32 size) { - if (! (mMode & WRITE)) - { - LL_WARNS() << "Attempt to change size of file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << LL_ENDL; + // we don't care what the max size is so we do nothing + // and return true to indicate all was okay + return TRUE; - return FALSE; - } + //CP BEG + //if (! (mMode & WRITE)) + //{ + // LL_WARNS() << "Attempt to change size of file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << LL_ENDL; - if (!mVFS->checkAvailable(size)) - { - //LL_RECORD_BLOCK_TIME(FTM_VFILE_WAIT); - S32 count = 0; - while (sVFSThread->getPending() > 1000) - { - if (count % 100 == 0) - { - LL_INFOS() << "VFS catching up... Pending: " << sVFSThread->getPending() << LL_ENDL; - } - if (sVFSThread->isPaused()) - { - sVFSThread->update(0); - } - ms_sleep(10); - } - } - return mVFS->setMaxSize(mFileID, mFileType, size); + // return FALSE; + //} + + //if (!mVFS->checkAvailable(size)) + //{ + // //LL_RECORD_BLOCK_TIME(FTM_VFILE_WAIT); + // S32 count = 0; + // while (sVFSThread->getPending() > 1000) + // { + // if (count % 100 == 0) + // { + // LL_INFOS() << "VFS catching up... Pending: " << sVFSThread->getPending() << LL_ENDL; + // } + // if (sVFSThread->isPaused()) + // { + // sVFSThread->update(0); + // } + // ms_sleep(10); + // } + //} + //return mVFS->setMaxSize(mFileID, mFileType, size); + //CP END } BOOL LLVFile::rename(const LLUUID &new_id, const LLAssetType::EType new_type) { - if (! (mMode & WRITE)) - { - LL_WARNS() << "Attempt to rename file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << LL_ENDL; + std::string old_id_str; + mFileID.toString(old_id_str); + const std::string old_filename = idToFilepath(old_id_str, mFileType); - return FALSE; - } + std::string new_id_str; + new_id.toString(new_id_str); + const std::string new_filename = idToFilepath(new_id_str, new_type); - if (mHandle != LLVFSThread::nullHandle()) - { - LL_WARNS() << "Renaming file with pending async read" << LL_ENDL; - } + if (std::rename(old_filename.c_str(), new_filename.c_str())) + { + // We would like to return FALSE here indicating the operation + // failed but the original code does not and doing so seems to + // break a lot of things so we go with the flow... + //return FALSE; + } + mFileID = new_id; + mFileType = new_type; - waitForLock(VFSLOCK_READ); - waitForLock(VFSLOCK_APPEND); + return TRUE; + //CP BEG + //if (! (mMode & WRITE)) + //{ + // LL_WARNS() << "Attempt to rename file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << LL_ENDL; - // we need to release / replace our own lock - // since the renamed file will inherit locks from the new name - mVFS->decLock(mFileID, mFileType, VFSLOCK_OPEN); - mVFS->renameFile(mFileID, mFileType, new_id, new_type); - mVFS->incLock(new_id, new_type, VFSLOCK_OPEN); - - mFileID = new_id; - mFileType = new_type; + // return FALSE; + //} - return TRUE; + //if (mHandle != LLVFSThread::nullHandle()) + //{ + // LL_WARNS() << "Renaming file with pending async read" << LL_ENDL; + //} + + //waitForLock(VFSLOCK_READ); + //waitForLock(VFSLOCK_APPEND); + + //// we need to release / replace our own lock + //// since the renamed file will inherit locks from the new name + //mVFS->decLock(mFileID, mFileType, VFSLOCK_OPEN); + //mVFS->renameFile(mFileID, mFileType, new_id, new_type); + //mVFS->incLock(new_id, new_type, VFSLOCK_OPEN); + // + //mFileID = new_id; + //mFileType = new_type; + + //return TRUE; + //CP END } BOOL LLVFile::remove() { -// LL_INFOS() << "Removing file " << mFileID << LL_ENDL; - - if (! (mMode & WRITE)) - { - // Leaving paranoia warning just because this should be a very infrequent - // operation. - LL_WARNS() << "Remove file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << LL_ENDL; - } + std::string id_str; + mFileID.toString(id_str); + const std::string filename = idToFilepath(id_str, mFileType); - if (mHandle != LLVFSThread::nullHandle()) - { - LL_WARNS() << "Removing file with pending async read" << LL_ENDL; - } - - // why not seek back to the beginning of the file too? - mPosition = 0; + std::remove(filename.c_str()); + // TODO: check if file was not removed and return false - maybe we don't care? - waitForLock(VFSLOCK_READ); - waitForLock(VFSLOCK_APPEND); - mVFS->removeFile(mFileID, mFileType); + return TRUE; - return TRUE; + //CP BEG + // LL_INFOS() << "Removing file " << mFileID << LL_ENDL; + //if (! (mMode & WRITE)) + //{ + // // Leaving paranoia warning just because this should be a very infrequent + // // operation. + // LL_WARNS() << "Remove file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << LL_ENDL; + //} + + //if (mHandle != LLVFSThread::nullHandle()) + //{ + // LL_WARNS() << "Removing file with pending async read" << LL_ENDL; + //} + // + //// why not seek back to the beginning of the file too? + //mPosition = 0; + + //waitForLock(VFSLOCK_READ); + //waitForLock(VFSLOCK_APPEND); + //mVFS->removeFile(mFileID, mFileType); + + //return TRUE; + //CP END } // static void LLVFile::initClass(LLVFSThread* vfsthread) { - if (!vfsthread) - { - if (LLVFSThread::sLocal != NULL) - { - vfsthread = LLVFSThread::sLocal; - } - else - { - vfsthread = new LLVFSThread(); - sAllocdVFSThread = TRUE; - } - } - sVFSThread = vfsthread; + //CP BEG + //if (!vfsthread) + //{ + // if (LLVFSThread::sLocal != NULL) + // { + // vfsthread = LLVFSThread::sLocal; + // } + // else + // { + // vfsthread = new LLVFSThread(); + // sAllocdVFSThread = TRUE; + // } + //} + //sVFSThread = vfsthread; + //CP END } // static void LLVFile::cleanupClass() { - if (sAllocdVFSThread) - { - delete sVFSThread; - } - sVFSThread = NULL; + //CP BEG + //if (sAllocdVFSThread) + //{ + // delete sVFSThread; + //} + //sVFSThread = NULL; + //CP END } bool LLVFile::isLocked(EVFSLock lock) { - return mVFS->isLocked(mFileID, mFileType, lock) ? true : false; + // I don't think we care about this test since there is no locking + return FALSE; + + //CP return mVFS->isLocked(mFileID, mFileType, lock) ? true : false; } void LLVFile::waitForLock(EVFSLock lock) { - //LL_RECORD_BLOCK_TIME(FTM_VFILE_WAIT); - // spin until the lock clears - while (isLocked(lock)) - { - if (sVFSThread->isPaused()) - { - sVFSThread->update(0); - } - ms_sleep(1); - } + //CP BEG + ////LL_RECORD_BLOCK_TIME(FTM_VFILE_WAIT); + //// spin until the lock clears + //while (isLocked(lock)) + //{ + // if (sVFSThread->isPaused()) + // { + // sVFSThread->update(0); + // } + // ms_sleep(1); + //} + //CP END } diff --git a/indra/llvfs/llvfile.h b/indra/llvfs/llvfile.h index 7e9d9f73e5..4aeb9c9630 100644 --- a/indra/llvfs/llvfile.h +++ b/indra/llvfs/llvfile.h @@ -39,8 +39,8 @@ public: ~LLVFile(); BOOL read(U8 *buffer, S32 bytes, BOOL async = FALSE, F32 priority = 128.f); /* Flawfinder: ignore */ - static U8* readFile(LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type, S32* bytes_read = 0); - void setReadPriority(const F32 priority); + //CP static U8* readFile(LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type, S32* bytes_read = 0); + //CP void setReadPriority(const F32 priority); BOOL isReadComplete(); S32 getLastBytesRead(); BOOL eof(); @@ -61,12 +61,12 @@ public: static void initClass(LLVFSThread* vfsthread = NULL); static void cleanupClass(); - static LLVFSThread* getVFSThread() { return sVFSThread; } + //CP static LLVFSThread* getVFSThread() { return sVFSThread; } protected: - static LLVFSThread* sVFSThread; - static BOOL sAllocdVFSThread; - U32 threadPri() { return LLVFSThread::PRIORITY_NORMAL + llmin((U32)mPriority,(U32)0xfff); } + //CP static LLVFSThread* sVFSThread; + //CP static BOOL sAllocdVFSThread; + //CP U32 threadPri() { return LLVFSThread::PRIORITY_NORMAL + llmin((U32)mPriority,(U32)0xfff); } public: static const S32 READ; @@ -77,14 +77,15 @@ public: protected: LLAssetType::EType mFileType; + BOOL mReadComplete; LLUUID mFileID; S32 mPosition; S32 mMode; - LLVFS *mVFS; - F32 mPriority; + //CP LLVFS *mVFS; + //CP F32 mPriority; S32 mBytesRead; - LLVFSThread::handle_t mHandle; + //CP LLVFSThread::handle_t mHandle; }; #endif diff --git a/indra/newview/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp index 156a1c5893..1c9f3af1e6 100644 --- a/indra/newview/llappviewerwin32.cpp +++ b/indra/newview/llappviewerwin32.cpp @@ -499,7 +499,7 @@ void LLAppViewerWin32::disableWinErrorReporting() } } -const S32 MAX_CONSOLE_LINES = 500; +const S32 MAX_CONSOLE_LINES = 7500; // Only defined in newer SDKs than we currently use #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 4 diff --git a/indra/newview/llviewerstats.cpp b/indra/newview/llviewerstats.cpp index f7ded00318..3fd709b3ce 100644 --- a/indra/newview/llviewerstats.cpp +++ b/indra/newview/llviewerstats.cpp @@ -382,7 +382,7 @@ void update_statistics() F64Bits layer_bits = gVLManager.getLandBits() + gVLManager.getWindBits() + gVLManager.getCloudBits(); add(LLStatViewer::LAYERS_NETWORK_DATA_RECEIVED, layer_bits); add(LLStatViewer::OBJECT_NETWORK_DATA_RECEIVED, gObjectData); - sample(LLStatViewer::PENDING_VFS_OPERATIONS, LLVFile::getVFSThread()->getPending()); + //sample(LLStatViewer::PENDING_VFS_OPERATIONS, LLVFile::getVFSThread()->getPending()); add(LLStatViewer::ASSET_UDP_DATA_RECEIVED, F64Bits(gTransferManager.getTransferBitsIn(LLTCT_ASSET))); gTransferManager.resetTransferBitsIn(LLTCT_ASSET); From 060dd2aeab72df26d6375f2e0fad619113fe7e13 Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Wed, 16 Sep 2020 19:03:48 +0300 Subject: [PATCH 02/85] Check existence of local files instead of checking VFS --- indra/llmessage/llassetstorage.cpp | 2 +- indra/llvfs/llvfile.cpp | 16 ++++++++++++++++ indra/llvfs/llvfile.h | 2 ++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/indra/llmessage/llassetstorage.cpp b/indra/llmessage/llassetstorage.cpp index d7801b6ddc..56bc1d723a 100644 --- a/indra/llmessage/llassetstorage.cpp +++ b/indra/llmessage/llassetstorage.cpp @@ -443,7 +443,7 @@ void LLAssetStorage::_cleanupRequests(BOOL all, S32 error) BOOL LLAssetStorage::hasLocalAsset(const LLUUID &uuid, const LLAssetType::EType type) { - return mStaticVFS->getExists(uuid, type) || mVFS->getExists(uuid, type); + return LLVFile::getExists(uuid, type); } bool LLAssetStorage::findInStaticVFSAndInvokeCallback(const LLUUID& uuid, LLAssetType::EType type, diff --git a/indra/llvfs/llvfile.cpp b/indra/llvfs/llvfile.cpp index 797b431b93..d05c419d89 100644 --- a/indra/llvfs/llvfile.cpp +++ b/indra/llvfs/llvfile.cpp @@ -124,6 +124,7 @@ const std::string assetTypeToString(LLAssetType::EType at) { LLAssetType::AT_WIDGET, "WIDGET" }, { LLAssetType::AT_PERSON, "PERSON" }, { LLAssetType::AT_MESH, "MESH" }, + { LLAssetType::AT_SETTINGS, "SETTINGS" }, { LLAssetType::AT_UNKNOWN, "UNKNOWN" } }; @@ -154,6 +155,21 @@ const std::string idToFilepath(const std::string id, LLAssetType::EType at) return filepath; } +bool LLVFile::getExists(const LLUUID &file_id, const LLAssetType::EType file_type) +{ + std::string id_str; + file_id.toString(id_str); + const std::string filename = idToFilepath(id_str, file_type); + + std::ifstream file(filename, std::ios::binary); + if (file.is_open()) + { + file.seekg(0, std::ios::end); + return file.tellg() > 0; + } + return false; +} + BOOL LLVFile::read(U8 *buffer, S32 bytes, BOOL async, F32 priority) { //CP BEGIN diff --git a/indra/llvfs/llvfile.h b/indra/llvfs/llvfile.h index 4aeb9c9630..71dd273404 100644 --- a/indra/llvfs/llvfile.h +++ b/indra/llvfs/llvfile.h @@ -58,6 +58,8 @@ public: bool isLocked(EVFSLock lock); void waitForLock(EVFSLock lock); + + static bool getExists(const LLUUID &file_id, const LLAssetType::EType file_type); static void initClass(LLVFSThread* vfsthread = NULL); static void cleanupClass(); From 3fc07dea01795b31c37dcd093ec73d190a1e188a Mon Sep 17 00:00:00 2001 From: Callum Prentice Date: Wed, 16 Sep 2020 18:53:24 -0700 Subject: [PATCH 03/85] First part of change to remove LLVFS from the Viewer. Consists of code changes to remove LLVFS and LLVFSThread classes along with the associated source files. The existing llvfs folder is renamed to llcache. Also includes changes to CMake script in many places to reflect changes. Eventually, llvfile source file and class will be renamed but that is not in this change. --- indra/CMakeLists.txt | 2 +- indra/cmake/CMakeLists.txt | 2 +- indra/cmake/LLCache.cmake | 7 + indra/cmake/LLVFS.cmake | 7 - indra/llappearance/CMakeLists.txt | 8 +- indra/llappearance/lltexlayer.cpp | 1 - indra/llaudio/CMakeLists.txt | 6 +- indra/llaudio/llaudiodecodemgr.cpp | 46 +- indra/llaudio/llaudiodecodemgr.h | 1 - indra/llaudio/llaudioengine.cpp | 15 +- indra/llaudio/llaudioengine.h | 13 +- indra/{llvfs => llcache}/CMakeLists.txt | 46 +- indra/{llvfs => llcache}/lldir.cpp | 0 indra/{llvfs => llcache}/lldir.h | 0 indra/{llvfs => llcache}/lldir_linux.cpp | 0 indra/{llvfs => llcache}/lldir_linux.h | 0 indra/{llvfs => llcache}/lldir_mac.cpp | 0 indra/{llvfs => llcache}/lldir_mac.h | 0 indra/{llvfs => llcache}/lldir_solaris.cpp | 0 indra/{llvfs => llcache}/lldir_solaris.h | 0 indra/{llvfs => llcache}/lldir_win32.cpp | 0 indra/{llvfs => llcache}/lldir_win32.h | 0 indra/{llvfs => llcache}/lldirguard.h | 0 indra/{llvfs => llcache}/lldiriterator.cpp | 0 indra/{llvfs => llcache}/lldiriterator.h | 0 indra/{llvfs => llcache}/lllfsthread.cpp | 0 indra/{llvfs => llcache}/lllfsthread.h | 0 indra/{llvfs => llcache}/llpidlock.cpp | 0 indra/{llvfs => llcache}/llpidlock.h | 0 indra/llcache/llvfile.cpp | 387 +++ indra/{llvfs => llcache}/llvfile.h | 31 +- indra/{llvfs => llcache}/tests/lldir_test.cpp | 0 .../tests/lldiriterator_test.cpp | 0 indra/llcharacter/CMakeLists.txt | 18 +- indra/llcharacter/llkeyframemotion.cpp | 19 +- indra/llcharacter/llkeyframemotion.h | 12 +- indra/llcrashlogger/CMakeLists.txt | 4 +- indra/llimage/CMakeLists.txt | 6 +- indra/llimage/llimage.cpp | 9 - indra/llinventory/CMakeLists.txt | 4 +- indra/llmessage/CMakeLists.txt | 12 +- indra/llmessage/llassetstorage.cpp | 93 +- indra/llmessage/llassetstorage.h | 26 +- indra/llmessage/llcorehttputil.cpp | 2 +- indra/llmessage/llextendedstatus.h | 14 +- indra/llmessage/lltransfersourceasset.cpp | 6 +- indra/llmessage/lltransfersourceasset.h | 2 +- indra/llmessage/lltransfertargetvfile.cpp | 6 +- indra/llmessage/llxfer_vfile.cpp | 54 +- indra/llmessage/llxfer_vfile.h | 8 +- indra/llmessage/llxfermanager.cpp | 35 +- indra/llmessage/llxfermanager.h | 12 +- indra/llrender/CMakeLists.txt | 14 +- indra/llui/CMakeLists.txt | 6 +- indra/llvfs/llvfile.cpp | 676 ----- indra/llvfs/llvfs.cpp | 2220 ----------------- indra/llvfs/llvfs.h | 183 -- indra/llvfs/llvfs_objc.h | 43 - indra/llvfs/llvfs_objc.mm | 108 - indra/llvfs/llvfsthread.cpp | 300 --- indra/llvfs/llvfsthread.h | 140 -- indra/llwindow/CMakeLists.txt | 8 +- indra/llxml/CMakeLists.txt | 6 +- indra/newview/CMakeLists.txt | 10 +- indra/newview/app_settings/settings.xml | 44 - indra/newview/llappviewer.cpp | 256 +- indra/newview/llappviewer.h | 9 +- indra/newview/llcompilequeue.cpp | 10 +- indra/newview/llcompilequeue.h | 2 +- indra/newview/llfilepicker.h | 2 +- indra/newview/llfloaterauction.cpp | 5 +- indra/newview/llfloaterbvhpreview.cpp | 2 +- indra/newview/llfloatermodelpreview.cpp | 1 - indra/newview/llfloaterpreference.h | 2 +- indra/newview/llfloaterregioninfo.cpp | 9 +- indra/newview/llfloaterregioninfo.h | 4 +- indra/newview/llfloaterreporter.cpp | 4 +- indra/newview/llfloatertos.h | 1 - indra/newview/llgesturemgr.cpp | 18 +- indra/newview/llgesturemgr.h | 11 +- indra/newview/lllandmarklist.cpp | 3 +- indra/newview/lllandmarklist.h | 1 - indra/newview/llmeshrepository.cpp | 54 +- indra/newview/llmeshrepository.h | 1 - indra/newview/lloutfitgallery.h | 1 - indra/newview/llpostcard.cpp | 1 - indra/newview/llpreviewgesture.cpp | 13 +- indra/newview/llpreviewgesture.h | 4 +- indra/newview/llpreviewnotecard.cpp | 13 +- indra/newview/llpreviewnotecard.h | 3 +- indra/newview/llpreviewscript.cpp | 14 +- indra/newview/llpreviewscript.h | 7 +- indra/newview/llsettingsvo.cpp | 8 +- indra/newview/llsettingsvo.h | 3 +- indra/newview/llsnapshotlivepreview.cpp | 3 +- indra/newview/llstartup.cpp | 16 +- indra/newview/llviewerassetstorage.cpp | 48 +- indra/newview/llviewerassetstorage.h | 6 +- indra/newview/llviewerassetupload.cpp | 18 +- indra/newview/llviewerassetupload.h | 2 +- .../newview/llviewermedia_streamingaudio.cpp | 2 - indra/newview/llviewermenufile.cpp | 1 - indra/newview/llviewermessage.cpp | 10 +- indra/newview/llviewermessage.h | 4 +- indra/newview/llviewerprecompiledheaders.h | 1 - indra/newview/llviewerstats.cpp | 2 - indra/newview/llviewerstats.h | 1 - indra/newview/llviewertexlayer.cpp | 1 - indra/newview/llviewertexture.cpp | 1 - indra/newview/llviewertexturelist.cpp | 2 - indra/newview/llviewerwearable.cpp | 1 - indra/newview/llvoavatar.cpp | 1 - indra/newview/llvovolume.h | 7 +- .../xui/en/floater_scene_load_stats.xml | 6 - .../skins/default/xui/en/floater_stats.xml | 4 - .../newview/skins/default/xui/en/strings.xml | 2 - indra/test/CMakeLists.txt | 6 +- indra/win_crash_logger/CMakeLists.txt | 6 +- 118 files changed, 747 insertions(+), 4538 deletions(-) create mode 100644 indra/cmake/LLCache.cmake delete mode 100644 indra/cmake/LLVFS.cmake rename indra/{llvfs => llcache}/CMakeLists.txt (56%) rename indra/{llvfs => llcache}/lldir.cpp (100%) rename indra/{llvfs => llcache}/lldir.h (100%) rename indra/{llvfs => llcache}/lldir_linux.cpp (100%) rename indra/{llvfs => llcache}/lldir_linux.h (100%) rename indra/{llvfs => llcache}/lldir_mac.cpp (100%) rename indra/{llvfs => llcache}/lldir_mac.h (100%) rename indra/{llvfs => llcache}/lldir_solaris.cpp (100%) rename indra/{llvfs => llcache}/lldir_solaris.h (100%) rename indra/{llvfs => llcache}/lldir_win32.cpp (100%) rename indra/{llvfs => llcache}/lldir_win32.h (100%) rename indra/{llvfs => llcache}/lldirguard.h (100%) rename indra/{llvfs => llcache}/lldiriterator.cpp (100%) rename indra/{llvfs => llcache}/lldiriterator.h (100%) rename indra/{llvfs => llcache}/lllfsthread.cpp (100%) rename indra/{llvfs => llcache}/lllfsthread.h (100%) rename indra/{llvfs => llcache}/llpidlock.cpp (100%) rename indra/{llvfs => llcache}/llpidlock.h (100%) create mode 100644 indra/llcache/llvfile.cpp rename indra/{llvfs => llcache}/llvfile.h (66%) rename indra/{llvfs => llcache}/tests/lldir_test.cpp (100%) rename indra/{llvfs => llcache}/tests/lldiriterator_test.cpp (100%) delete mode 100644 indra/llvfs/llvfile.cpp delete mode 100644 indra/llvfs/llvfs.cpp delete mode 100644 indra/llvfs/llvfs.h delete mode 100644 indra/llvfs/llvfs_objc.h delete mode 100644 indra/llvfs/llvfs_objc.mm delete mode 100644 indra/llvfs/llvfsthread.cpp delete mode 100644 indra/llvfs/llvfsthread.h diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt index 53e5d7b6a5..04279cc887 100644 --- a/indra/CMakeLists.txt +++ b/indra/CMakeLists.txt @@ -40,7 +40,7 @@ add_subdirectory(${LIBS_OPEN_PREFIX}llmath) add_subdirectory(${LIBS_OPEN_PREFIX}llmessage) add_subdirectory(${LIBS_OPEN_PREFIX}llprimitive) add_subdirectory(${LIBS_OPEN_PREFIX}llrender) -add_subdirectory(${LIBS_OPEN_PREFIX}llvfs) +add_subdirectory(${LIBS_OPEN_PREFIX}llcache) add_subdirectory(${LIBS_OPEN_PREFIX}llwindow) add_subdirectory(${LIBS_OPEN_PREFIX}llxml) diff --git a/indra/cmake/CMakeLists.txt b/indra/cmake/CMakeLists.txt index a17e37cd32..d464c8ad74 100644 --- a/indra/cmake/CMakeLists.txt +++ b/indra/cmake/CMakeLists.txt @@ -69,7 +69,7 @@ set(cmake_SOURCE_FILES LLSharedLibs.cmake LLTestCommand.cmake LLUI.cmake - LLVFS.cmake + LLCache.cmake LLWindow.cmake LLXML.cmake Linking.cmake diff --git a/indra/cmake/LLCache.cmake b/indra/cmake/LLCache.cmake new file mode 100644 index 0000000000..6a82774133 --- /dev/null +++ b/indra/cmake/LLCache.cmake @@ -0,0 +1,7 @@ +# -*- cmake -*- + +set(LLCACHE_INCLUDE_DIRS + ${LIBS_OPEN_DIR}/llcache + ) + +set(LLCACHE_LIBRARIES llcache) diff --git a/indra/cmake/LLVFS.cmake b/indra/cmake/LLVFS.cmake deleted file mode 100644 index 0fe87cdea6..0000000000 --- a/indra/cmake/LLVFS.cmake +++ /dev/null @@ -1,7 +0,0 @@ -# -*- cmake -*- - -set(LLVFS_INCLUDE_DIRS - ${LIBS_OPEN_DIR}/llvfs - ) - -set(LLVFS_LIBRARIES llvfs) diff --git a/indra/llappearance/CMakeLists.txt b/indra/llappearance/CMakeLists.txt index 20eb4678dd..ff784387dc 100644 --- a/indra/llappearance/CMakeLists.txt +++ b/indra/llappearance/CMakeLists.txt @@ -11,7 +11,7 @@ include(LLMath) include(LLMessage) include(LLCoreHttp) include(LLRender) -include(LLVFS) +include(LLCache) include(LLWindow) include(LLXML) include(Linking) @@ -23,7 +23,7 @@ include_directories( ${LLINVENTORY_INCLUDE_DIRS} ${LLMATH_INCLUDE_DIRS} ${LLRENDER_INCLUDE_DIRS} - ${LLVFS_INCLUDE_DIRS} + ${LLCACHE_INCLUDE_DIRS} ${LLWINDOW_INCLUDE_DIRS} ${LLXML_INCLUDE_DIRS} ) @@ -83,7 +83,7 @@ target_link_libraries(llappearance ${LLINVENTORY_LIBRARIES} ${LLIMAGE_LIBRARIES} ${LLRENDER_LIBRARIES} - ${LLVFS_LIBRARIES} + ${LLCACHE_LIBRARIES} ${LLMATH_LIBRARIES} ${LLXML_LIBRARIES} ${LLMATH_LIBRARIES} @@ -100,7 +100,7 @@ if (BUILD_HEADLESS) ${LLINVENTORY_LIBRARIES} ${LLIMAGE_LIBRARIES} ${LLRENDERHEADLESS_LIBRARIES} - ${LLVFS_LIBRARIES} + ${LLCACHE_LIBRARIES} ${LLMATH_LIBRARIES} ${LLXML_LIBRARIES} ${LLMATH_LIBRARIES} diff --git a/indra/llappearance/lltexlayer.cpp b/indra/llappearance/lltexlayer.cpp index c90b11ae71..2a7e5f3ddb 100644 --- a/indra/llappearance/lltexlayer.cpp +++ b/indra/llappearance/lltexlayer.cpp @@ -34,7 +34,6 @@ #include "llimagetga.h" #include "lldir.h" #include "llvfile.h" -#include "llvfs.h" #include "lltexlayerparams.h" #include "lltexturemanagerbridge.h" #include "lllocaltextureobject.h" diff --git a/indra/llaudio/CMakeLists.txt b/indra/llaudio/CMakeLists.txt index 8b628a058e..2498561029 100644 --- a/indra/llaudio/CMakeLists.txt +++ b/indra/llaudio/CMakeLists.txt @@ -9,14 +9,14 @@ include(OPENAL) include(LLCommon) include(LLMath) include(LLMessage) -include(LLVFS) +include(LLCache) include_directories( ${LLAUDIO_INCLUDE_DIRS} ${LLCOMMON_INCLUDE_DIRS} ${LLMATH_INCLUDE_DIRS} ${LLMESSAGE_INCLUDE_DIRS} - ${LLVFS_INCLUDE_DIRS} + ${LLCACHE_INCLUDE_DIRS} ${OGG_INCLUDE_DIRS} ${VORBISENC_INCLUDE_DIRS} ${VORBISFILE_INCLUDE_DIRS} @@ -82,7 +82,7 @@ target_link_libraries( ${LLCOMMON_LIBRARIES} ${LLMATH_LIBRARIES} ${LLMESSAGE_LIBRARIES} - ${LLVFS_LIBRARIES} + ${LLCACHE_LIBRARIES} ${VORBISENC_LIBRARIES} ${VORBISFILE_LIBRARIES} ${VORBIS_LIBRARIES} diff --git a/indra/llaudio/llaudiodecodemgr.cpp b/indra/llaudio/llaudiodecodemgr.cpp index e7db84f6ab..d2c4163280 100644 --- a/indra/llaudio/llaudiodecodemgr.cpp +++ b/indra/llaudio/llaudiodecodemgr.cpp @@ -90,17 +90,15 @@ protected: LLUUID mUUID; std::vector mWAVBuffer; -#if !defined(USE_WAV_VFILE) std::string mOutFilename; LLLFSThread::handle_t mFileHandle; -#endif LLVFile *mInFilep; OggVorbis_File mVF; S32 mCurrentSection; }; -size_t vfs_read(void *ptr, size_t size, size_t nmemb, void *datasource) +size_t cache_read(void *ptr, size_t size, size_t nmemb, void *datasource) { LLVFile *file = (LLVFile *)datasource; @@ -115,11 +113,11 @@ size_t vfs_read(void *ptr, size_t size, size_t nmemb, void *datasource) } } -S32 vfs_seek(void *datasource, ogg_int64_t offset, S32 whence) +S32 cache_seek(void *datasource, ogg_int64_t offset, S32 whence) { LLVFile *file = (LLVFile *)datasource; - // vfs has 31-bit files + // cache has 31-bit files if (offset > S32_MAX) { return -1; @@ -137,7 +135,7 @@ S32 vfs_seek(void *datasource, ogg_int64_t offset, S32 whence) origin = -1; break; default: - LL_ERRS("AudioEngine") << "Invalid whence argument to vfs_seek" << LL_ENDL; + LL_ERRS("AudioEngine") << "Invalid whence argument to cache_seek" << LL_ENDL; return -1; } @@ -151,14 +149,14 @@ S32 vfs_seek(void *datasource, ogg_int64_t offset, S32 whence) } } -S32 vfs_close (void *datasource) +S32 cache_close (void *datasource) { LLVFile *file = (LLVFile *)datasource; delete file; return 0; } -long vfs_tell (void *datasource) +long cache_tell (void *datasource) { LLVFile *file = (LLVFile *)datasource; return file->tell(); @@ -172,11 +170,10 @@ LLVorbisDecodeState::LLVorbisDecodeState(const LLUUID &uuid, const std::string & mUUID = uuid; mInFilep = NULL; mCurrentSection = 0; -#if !defined(USE_WAV_VFILE) mOutFilename = out_filename; mFileHandle = LLLFSThread::nullHandle(); -#endif - // No default value for mVF, it's an ogg structure? + + // No default value for mVF, it's an ogg structure? // Hey, let's zero it anyway, for predictability. memset(&mVF, 0, sizeof(mVF)); } @@ -193,15 +190,15 @@ LLVorbisDecodeState::~LLVorbisDecodeState() BOOL LLVorbisDecodeState::initDecode() { - ov_callbacks vfs_callbacks; - vfs_callbacks.read_func = vfs_read; - vfs_callbacks.seek_func = vfs_seek; - vfs_callbacks.close_func = vfs_close; - vfs_callbacks.tell_func = vfs_tell; + ov_callbacks cache_callbacks; + cache_callbacks.read_func = cache_read; + cache_callbacks.seek_func = cache_seek; + cache_callbacks.close_func = cache_close; + cache_callbacks.tell_func = cache_tell; LL_DEBUGS("AudioEngine") << "Initing decode from vfile: " << mUUID << LL_ENDL; - mInFilep = new LLVFile(gVFS, mUUID, LLAssetType::AT_SOUND); + mInFilep = new LLVFile(mUUID, LLAssetType::AT_SOUND); if (!mInFilep || !mInFilep->getSize()) { LL_WARNS("AudioEngine") << "unable to open vorbis source vfile for reading" << LL_ENDL; @@ -210,7 +207,7 @@ BOOL LLVorbisDecodeState::initDecode() return FALSE; } - S32 r = ov_open_callbacks(mInFilep, &mVF, NULL, 0, vfs_callbacks); + S32 r = ov_open_callbacks(mInFilep, &mVF, NULL, 0, cache_callbacks); if(r < 0) { LL_WARNS("AudioEngine") << r << " Input to vorbis decode does not appear to be an Ogg bitstream: " << mUUID << LL_ENDL; @@ -370,7 +367,7 @@ BOOL LLVorbisDecodeState::decodeSection() { if (!mInFilep) { - LL_WARNS("AudioEngine") << "No VFS file to decode in vorbis!" << LL_ENDL; + LL_WARNS("AudioEngine") << "No cache file to decode in vorbis!" << LL_ENDL; return TRUE; } if (mDone) @@ -420,9 +417,7 @@ BOOL LLVorbisDecodeState::finishDecode() return TRUE; // We've finished } -#if !defined(USE_WAV_VFILE) if (mFileHandle == LLLFSThread::nullHandle()) -#endif { ov_clear(&mVF); @@ -495,11 +490,9 @@ BOOL LLVorbisDecodeState::finishDecode() mValid = FALSE; return TRUE; // we've finished } -#if !defined(USE_WAV_VFILE) mBytesRead = -1; mFileHandle = LLLFSThread::sLocal->write(mOutFilename, &mWAVBuffer[0], 0, mWAVBuffer.size(), new WriteResponder(this)); -#endif } if (mFileHandle != LLLFSThread::nullHandle()) @@ -521,11 +514,6 @@ BOOL LLVorbisDecodeState::finishDecode() mDone = TRUE; -#if defined(USE_WAV_VFILE) - // write the data. - LLVFile output(gVFS, mUUID, LLAssetType::AT_SOUND_WAV); - output.write(&mWAVBuffer[0], mWAVBuffer.size()); -#endif LL_DEBUGS("AudioEngine") << "Finished decode for " << getUUID() << LL_ENDL; return TRUE; @@ -535,7 +523,7 @@ void LLVorbisDecodeState::flushBadFile() { if (mInFilep) { - LL_WARNS("AudioEngine") << "Flushing bad vorbis file from VFS for " << mUUID << LL_ENDL; + LL_WARNS("AudioEngine") << "Flushing bad vorbis file from cache for " << mUUID << LL_ENDL; mInFilep->remove(); } } diff --git a/indra/llaudio/llaudiodecodemgr.h b/indra/llaudio/llaudiodecodemgr.h index 8228e20e8c..ceaff3f2d8 100644 --- a/indra/llaudio/llaudiodecodemgr.h +++ b/indra/llaudio/llaudiodecodemgr.h @@ -33,7 +33,6 @@ #include "llassettype.h" #include "llframetimer.h" -class LLVFS; class LLVorbisDecodeState; class LLAudioDecodeMgr diff --git a/indra/llaudio/llaudioengine.cpp b/indra/llaudio/llaudioengine.cpp index 1d447f32ae..9c8bd3225b 100644 --- a/indra/llaudio/llaudioengine.cpp +++ b/indra/llaudio/llaudioengine.cpp @@ -35,7 +35,7 @@ #include "sound_ids.h" // temporary hack for min/max distances -#include "llvfs.h" +#include "llvfile.h" #include "lldir.h" #include "llaudiodecodemgr.h" #include "llassetstorage.h" @@ -684,13 +684,9 @@ bool LLAudioEngine::preloadSound(const LLUUID &uuid) return true; } - // At some point we need to have the audio/asset system check the static VFS - // before it goes off and fetches stuff from the server. - //LL_WARNS() << "Used internal preload for non-local sound" << LL_ENDL; return false; } - bool LLAudioEngine::isWindEnabled() { return mEnableWind; @@ -1018,13 +1014,12 @@ bool LLAudioEngine::hasDecodedFile(const LLUUID &uuid) bool LLAudioEngine::hasLocalFile(const LLUUID &uuid) { - // See if it's in the VFS. - bool have_local = gVFS->getExists(uuid, LLAssetType::AT_SOUND); - LL_DEBUGS("AudioEngine") << "sound uuid "< +#include "lldir.h" + +const S32 LLVFile::READ = 0x00000001; +const S32 LLVFile::WRITE = 0x00000002; +const S32 LLVFile::READ_WRITE = 0x00000003; // LLVFile::READ & LLVFile::WRITE +const S32 LLVFile::APPEND = 0x00000006; // 0x00000004 & LLVFile::WRITE + +static LLTrace::BlockTimerStatHandle FTM_VFILE_WAIT("VFile Wait"); + +LLVFile::LLVFile(const LLUUID &file_id, const LLAssetType::EType file_type, S32 mode) +{ + mFileType = file_type; + mFileID = file_id; + mPosition = 0; + mBytesRead = 0; + mReadComplete = FALSE; + mMode = mode; +} + +LLVFile::~LLVFile() +{ +} + +const std::string assetTypeToString(LLAssetType::EType at) +{ + /** + * Make use of the C++17 (or is it 14) feature that allows + * for inline initialization of an std::map<> + */ + typedef std::map asset_type_to_name_t; + asset_type_to_name_t asset_type_to_name = + { + { LLAssetType::AT_TEXTURE, "TEXTURE" }, + { LLAssetType::AT_SOUND, "SOUND" }, + { LLAssetType::AT_CALLINGCARD, "CALLINGCARD" }, + { LLAssetType::AT_LANDMARK, "LANDMARK" }, + { LLAssetType::AT_SCRIPT, "SCRIPT" }, + { LLAssetType::AT_CLOTHING, "CLOTHING" }, + { LLAssetType::AT_OBJECT, "OBJECT" }, + { LLAssetType::AT_NOTECARD, "NOTECARD" }, + { LLAssetType::AT_CATEGORY, "CATEGORY" }, + { LLAssetType::AT_LSL_TEXT, "LSL_TEXT" }, + { LLAssetType::AT_LSL_BYTECODE, "LSL_BYTECODE" }, + { LLAssetType::AT_TEXTURE_TGA, "TEXTURE_TGA" }, + { LLAssetType::AT_BODYPART, "BODYPART" }, + { LLAssetType::AT_SOUND_WAV, "SOUND_WAV" }, + { LLAssetType::AT_IMAGE_TGA, "IMAGE_TGA" }, + { LLAssetType::AT_IMAGE_JPEG, "IMAGE_JPEG" }, + { LLAssetType::AT_ANIMATION, "ANIMATION" }, + { LLAssetType::AT_GESTURE, "GESTURE" }, + { LLAssetType::AT_SIMSTATE, "SIMSTATE" }, + { LLAssetType::AT_LINK, "LINK" }, + { LLAssetType::AT_LINK_FOLDER, "LINK_FOLDER" }, + { LLAssetType::AT_MARKETPLACE_FOLDER, "MARKETPLACE_FOLDER" }, + { LLAssetType::AT_WIDGET, "WIDGET" }, + { LLAssetType::AT_PERSON, "PERSON" }, + { LLAssetType::AT_MESH, "MESH" }, + { LLAssetType::AT_SETTINGS, "SETTINGS" }, + { LLAssetType::AT_UNKNOWN, "UNKNOWN" } + }; + + asset_type_to_name_t::iterator iter = asset_type_to_name.find(at); + if (iter != asset_type_to_name.end()) + { + return iter->second; + } + + return std::string("UNKNOWN"); +} + +const std::string idToFilepath(const std::string id, LLAssetType::EType at) +{ + /** + * For the moment this is just {UUID}_{ASSET_TYPE}.txt but of + * course, will be greatly expanded upon + */ + std::ostringstream ss; + ss << "00cache_"; + ss << id; + ss << "_"; + ss << assetTypeToString(at); + ss << ".txt"; + + const std::string filepath = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, ss.str()); + + return filepath; +} + +// static +bool LLVFile::getExists(const LLUUID &file_id, const LLAssetType::EType file_type) +{ + std::string id_str; + file_id.toString(id_str); + const std::string filename = idToFilepath(id_str, file_type); + + std::ifstream file(filename, std::ios::binary); + if (file.is_open()) + { + file.seekg(0, std::ios::end); + return file.tellg() > 0; + } + return false; +} + +// static +bool LLVFile::removeFile(const LLUUID &file_id, const LLAssetType::EType file_type) +{ + std::string id_str; + file_id.toString(id_str); + const std::string filename = idToFilepath(id_str, file_type); + + std::remove(filename.c_str()); + + return true; +} + +// static +bool LLVFile::renameFile(const LLUUID &old_file_id, const LLAssetType::EType old_file_type, + const LLUUID &new_file_id, const LLAssetType::EType new_file_type) +{ + std::string old_id_str; + old_file_id.toString(old_id_str); + const std::string old_filename = idToFilepath(old_id_str, old_file_type); + + std::string new_id_str; + new_file_id.toString(new_id_str); + const std::string new_filename = idToFilepath(new_id_str, new_file_type); + + if (std::rename(old_filename.c_str(), new_filename.c_str())) + { + // We would like to return FALSE here indicating the operation + // failed but the original code does not and doing so seems to + // break a lot of things so we go with the flow... + //return FALSE; + } + + return TRUE; +} + +// static +S32 LLVFile::getFileSize(const LLUUID &file_id, const LLAssetType::EType file_type) +{ + std::string id_str; + file_id.toString(id_str); + const std::string filename = idToFilepath(id_str, file_type); + + S32 file_size = 0; + std::ifstream file(filename, std::ios::binary); + if (file.is_open()) + { + file.seekg(0, std::ios::end); + file_size = file.tellg(); + } + + return file_size; +} + +BOOL LLVFile::read(U8 *buffer, S32 bytes, BOOL async, F32 priority) +{ + BOOL success = TRUE; + + mReadComplete = FALSE; + + std::string id; + mFileID.toString(id); + const std::string filename = idToFilepath(id, mFileType); + + std::ifstream file(filename, std::ios::binary); + if (file.is_open()) + { + file.seekg(mPosition, std::ios::beg); + + file.read((char*)buffer, bytes); + + if (file) + { + mBytesRead = bytes; + } + else + { + mBytesRead = file.gcount(); + } + + file.close(); + + mPosition += mBytesRead; + if (!mBytesRead) + { + success = FALSE; + } + + mReadComplete = TRUE; + } + + return success; +} + +BOOL LLVFile::isReadComplete() +{ + if (mReadComplete) + { + return TRUE; + } + + return FALSE; +} + +S32 LLVFile::getLastBytesRead() +{ + return mBytesRead; +} + +BOOL LLVFile::eof() +{ + return mPosition >= getSize(); +} + +BOOL LLVFile::write(const U8 *buffer, S32 bytes) +{ + std::string id_str; + mFileID.toString(id_str); + const std::string filename = idToFilepath(id_str, mFileType); + + BOOL success = FALSE; + + if (mMode == APPEND) + { + std::ofstream ofs(filename, std::ios::app | std::ios::binary); + if (ofs) + { + ofs.write((const char*)buffer, bytes); + + success = TRUE; + } + } + else + { + std::ofstream ofs(filename, std::ios::binary); + if (ofs) + { + ofs.write((const char*)buffer, bytes); + + mPosition += bytes; + + success = TRUE; + } + } + + return success; +} + +//static +BOOL LLVFile::writeFile(const U8 *buffer, S32 bytes, const LLUUID &uuid, LLAssetType::EType type) +{ + LLVFile file(uuid, type, LLVFile::WRITE); + file.setMaxSize(bytes); + return file.write(buffer, bytes); +} + +BOOL LLVFile::seek(S32 offset, S32 origin) +{ + if (-1 == origin) + { + origin = mPosition; + } + + S32 new_pos = origin + offset; + + S32 size = getSize(); + + if (new_pos > size) + { + LL_WARNS() << "Attempt to seek past end of file" << LL_ENDL; + + mPosition = size; + return FALSE; + } + else if (new_pos < 0) + { + LL_WARNS() << "Attempt to seek past beginning of file" << LL_ENDL; + + mPosition = 0; + return FALSE; + } + + mPosition = new_pos; + return TRUE; +} + +S32 LLVFile::tell() const +{ + return mPosition; +} + +S32 LLVFile::getSize() +{ + return LLVFile::getFileSize(mFileID, mFileType); +} + +S32 LLVFile::getMaxSize() +{ + // offer up a huge size since we don't care what the max is + return INT_MAX; +} + +BOOL LLVFile::setMaxSize(S32 size) +{ + // we don't care what the max size is so we do nothing + // and return true to indicate all was okay + return TRUE; +} + +BOOL LLVFile::rename(const LLUUID &new_id, const LLAssetType::EType new_type) +{ + LLVFile::renameFile(mFileID, mFileType, new_id, new_type); + + mFileID = new_id; + mFileType = new_type; + + return TRUE; +} + +BOOL LLVFile::remove() +{ + LLVFile::removeFile(mFileID, mFileType); + + return TRUE; +} + +// static +void LLVFile::initClass() +{ +} + +// static +void LLVFile::cleanupClass() +{ +} + +bool LLVFile::isLocked() +{ + // I don't think we care about this test since there is no locking + // TODO: remove this function and calling sites? + return FALSE; +} + +void LLVFile::waitForLock() +{ + // TODO: remove this function and calling sites? +} diff --git a/indra/llvfs/llvfile.h b/indra/llcache/llvfile.h similarity index 66% rename from indra/llvfs/llvfile.h rename to indra/llcache/llvfile.h index 71dd273404..30130df340 100644 --- a/indra/llvfs/llvfile.h +++ b/indra/llcache/llvfile.h @@ -29,24 +29,20 @@ #include "lluuid.h" #include "llassettype.h" -#include "llvfs.h" -#include "llvfsthread.h" class LLVFile { public: - LLVFile(LLVFS *vfs, const LLUUID &file_id, const LLAssetType::EType file_type, S32 mode = LLVFile::READ); + LLVFile(const LLUUID &file_id, const LLAssetType::EType file_type, S32 mode = LLVFile::READ); ~LLVFile(); BOOL read(U8 *buffer, S32 bytes, BOOL async = FALSE, F32 priority = 128.f); /* Flawfinder: ignore */ - //CP static U8* readFile(LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type, S32* bytes_read = 0); - //CP void setReadPriority(const F32 priority); BOOL isReadComplete(); S32 getLastBytesRead(); BOOL eof(); BOOL write(const U8 *buffer, S32 bytes); - static BOOL writeFile(const U8 *buffer, S32 bytes, LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type); + static BOOL writeFile(const U8 *buffer, S32 bytes, const LLUUID &uuid, LLAssetType::EType type); BOOL seek(S32 offset, S32 origin = -1); S32 tell() const; @@ -56,20 +52,18 @@ public: BOOL rename(const LLUUID &new_id, const LLAssetType::EType new_type); BOOL remove(); - bool isLocked(EVFSLock lock); - void waitForLock(EVFSLock lock); + bool isLocked(); + void waitForLock(); - static bool getExists(const LLUUID &file_id, const LLAssetType::EType file_type); + static bool getExists(const LLUUID &file_id, const LLAssetType::EType file_type); + static bool removeFile(const LLUUID &file_id, const LLAssetType::EType file_type); + static bool renameFile(const LLUUID &old_file_id, const LLAssetType::EType old_file_type, + const LLUUID &new_file_id, const LLAssetType::EType new_file_type); + static S32 getFileSize(const LLUUID &file_id, const LLAssetType::EType file_type); - static void initClass(LLVFSThread* vfsthread = NULL); + static void initClass(); static void cleanupClass(); - //CP static LLVFSThread* getVFSThread() { return sVFSThread; } -protected: - //CP static LLVFSThread* sVFSThread; - //CP static BOOL sAllocdVFSThread; - //CP U32 threadPri() { return LLVFSThread::PRIORITY_NORMAL + llmin((U32)mPriority,(U32)0xfff); } - public: static const S32 READ; static const S32 WRITE; @@ -78,16 +72,11 @@ public: protected: LLAssetType::EType mFileType; - BOOL mReadComplete; LLUUID mFileID; S32 mPosition; S32 mMode; - //CP LLVFS *mVFS; - //CP F32 mPriority; - S32 mBytesRead; - //CP LLVFSThread::handle_t mHandle; }; #endif diff --git a/indra/llvfs/tests/lldir_test.cpp b/indra/llcache/tests/lldir_test.cpp similarity index 100% rename from indra/llvfs/tests/lldir_test.cpp rename to indra/llcache/tests/lldir_test.cpp diff --git a/indra/llvfs/tests/lldiriterator_test.cpp b/indra/llcache/tests/lldiriterator_test.cpp similarity index 100% rename from indra/llvfs/tests/lldiriterator_test.cpp rename to indra/llcache/tests/lldiriterator_test.cpp diff --git a/indra/llcharacter/CMakeLists.txt b/indra/llcharacter/CMakeLists.txt index a17a5b0aa6..e236a307c2 100644 --- a/indra/llcharacter/CMakeLists.txt +++ b/indra/llcharacter/CMakeLists.txt @@ -6,14 +6,14 @@ include(00-Common) include(LLCommon) include(LLMath) include(LLMessage) -include(LLVFS) +include(LLCache) include(LLXML) include_directories( ${LLCOMMON_INCLUDE_DIRS} ${LLMATH_INCLUDE_DIRS} ${LLMESSAGE_INCLUDE_DIRS} - ${LLVFS_INCLUDE_DIRS} + ${LLCACHE_INCLUDE_DIRS} ${LLXML_INCLUDE_DIRS} ) include_directories(SYSTEM @@ -85,18 +85,6 @@ target_link_libraries( ${LLCOMMON_LIBRARIES} ${LLMATH_LIBRARIES} ${LLMESSAGE_LIBRARIES} - ${LLVFS_LIBRARIES} + ${LLCACHE_LIBRARIES} ${LLXML_LIBRARIES} ) - - -# Add tests -#if (LL_TESTS) -# include(LLAddBuildTest) -# # UNIT TESTS -# SET(llcharacter_TEST_SOURCE_FILES -# lljoint.cpp -# ) -# LL_ADD_PROJECT_UNIT_TESTS(llcharacter "${llcharacter_TEST_SOURCE_FILES}") -#endif (LL_TESTS) - diff --git a/indra/llcharacter/llkeyframemotion.cpp b/indra/llcharacter/llkeyframemotion.cpp index cde38c8091..beca1af269 100644 --- a/indra/llcharacter/llkeyframemotion.cpp +++ b/indra/llcharacter/llkeyframemotion.cpp @@ -46,7 +46,6 @@ //----------------------------------------------------------------------------- // Static Definitions //----------------------------------------------------------------------------- -LLVFS* LLKeyframeMotion::sVFS = NULL; LLKeyframeDataCache::keyframe_data_map_t LLKeyframeDataCache::sKeyframeDataMap; //----------------------------------------------------------------------------- @@ -515,7 +514,7 @@ LLMotion::LLMotionInitStatus LLKeyframeMotion::onInitialize(LLCharacter *charact return STATUS_SUCCESS; default: // we don't know what state the asset is in yet, so keep going - // check keyframe cache first then static vfs then asset request + // check keyframe cache first then file cache then asset request break; } @@ -559,13 +558,8 @@ LLMotion::LLMotionInitStatus LLKeyframeMotion::onInitialize(LLCharacter *charact U8 *anim_data; S32 anim_file_size; - if (!sVFS) - { - LL_ERRS() << "Must call LLKeyframeMotion::setVFS() first before loading a keyframe file!" << LL_ENDL; - } - BOOL success = FALSE; - LLVFile* anim_file = new LLVFile(sVFS, mID, LLAssetType::AT_ANIMATION); + LLVFile* anim_file = new LLVFile(mID, LLAssetType::AT_ANIMATION); if (!anim_file || !anim_file->getSize()) { delete anim_file; @@ -2296,10 +2290,9 @@ void LLKeyframeMotion::setLoopOut(F32 out_point) //----------------------------------------------------------------------------- // onLoadComplete() //----------------------------------------------------------------------------- -void LLKeyframeMotion::onLoadComplete(LLVFS *vfs, - const LLUUID& asset_uuid, - LLAssetType::EType type, - void* user_data, S32 status, LLExtStat ext_status) +void LLKeyframeMotion::onLoadComplete(const LLUUID& asset_uuid, + LLAssetType::EType type, + void* user_data, S32 status, LLExtStat ext_status) { LLUUID* id = (LLUUID*)user_data; @@ -2331,7 +2324,7 @@ void LLKeyframeMotion::onLoadComplete(LLVFS *vfs, // asset already loaded return; } - LLVFile file(vfs, asset_uuid, type, LLVFile::READ); + LLVFile file(asset_uuid, type, LLVFile::READ); S32 size = file.getSize(); U8* buffer = new U8[size]; diff --git a/indra/llcharacter/llkeyframemotion.h b/indra/llcharacter/llkeyframemotion.h index 15c5c7c6c0..d640556090 100644 --- a/indra/llcharacter/llkeyframemotion.h +++ b/indra/llcharacter/llkeyframemotion.h @@ -44,7 +44,6 @@ #include "llbvhconsts.h" class LLKeyframeDataCache; -class LLVFS; class LLDataPacker; #define MIN_REQUIRED_PIXEL_AREA_KEYFRAME (40.f) @@ -141,10 +140,7 @@ public: virtual void setStopTime(F32 time); - static void setVFS(LLVFS* vfs) { sVFS = vfs; } - - static void onLoadComplete(LLVFS *vfs, - const LLUUID& asset_uuid, + static void onLoadComplete(const LLUUID& asset_uuid, LLAssetType::EType type, void* user_data, S32 status, LLExtStat ext_status); @@ -416,13 +412,7 @@ public: U32 getNumJointMotions() const { return mJointMotionArray.size(); } }; - protected: - static LLVFS* sVFS; - - //------------------------------------------------------------------------- - // Member Data - //------------------------------------------------------------------------- JointMotionList* mJointMotionList; std::vector > mJointStates; LLJoint* mPelvisp; diff --git a/indra/llcrashlogger/CMakeLists.txt b/indra/llcrashlogger/CMakeLists.txt index da23b46b7b..040a0e846c 100644 --- a/indra/llcrashlogger/CMakeLists.txt +++ b/indra/llcrashlogger/CMakeLists.txt @@ -7,7 +7,7 @@ include(LLCoreHttp) include(LLCommon) include(LLMath) include(LLMessage) -include(LLVFS) +include(LLCache) include(LLXML) include_directories( @@ -15,7 +15,7 @@ include_directories( ${LLCOMMON_INCLUDE_DIRS} ${LLMATH_INCLUDE_DIRS} ${LLMESSAGE_INCLUDE_DIRS} - ${LLVFS_INCLUDE_DIRS} + ${LLCACHE_INCLUDE_DIRS} ${LLXML_INCLUDE_DIRS} ) include_directories(SYSTEM diff --git a/indra/llimage/CMakeLists.txt b/indra/llimage/CMakeLists.txt index 293ada7548..1f6b981346 100644 --- a/indra/llimage/CMakeLists.txt +++ b/indra/llimage/CMakeLists.txt @@ -6,7 +6,7 @@ include(00-Common) include(LLCommon) include(LLImage) include(LLMath) -include(LLVFS) +include(LLCache) include(LLKDU) include(LLImageJ2COJ) include(ZLIB) @@ -17,7 +17,7 @@ include_directories( ${LLCOMMON_INCLUDE_DIRS} ${LLCOMMON_SYSTEM_INCLUDE_DIRS} ${LLMATH_INCLUDE_DIRS} - ${LLVFS_INCLUDE_DIRS} + ${LLCACHE_INCLUDE_DIRS} ${PNG_INCLUDE_DIRS} ${ZLIB_INCLUDE_DIRS} ) @@ -68,7 +68,7 @@ else (USE_KDU) endif (USE_KDU) target_link_libraries(llimage - ${LLVFS_LIBRARIES} + ${LLCACHE_LIBRARIES} ${LLMATH_LIBRARIES} ${LLCOMMON_LIBRARIES} ${JPEG_LIBRARIES} diff --git a/indra/llimage/llimage.cpp b/indra/llimage/llimage.cpp index 7a0c8cd8f5..3e7be26e59 100644 --- a/indra/llimage/llimage.cpp +++ b/indra/llimage/llimage.cpp @@ -2209,20 +2209,11 @@ bool LLImageFormatted::save(const std::string &filename) return true; } -// bool LLImageFormatted::save(LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type) -// Depricated to remove VFS dependency. -// Use: -// LLVFile::writeFile(image->getData(), image->getDataSize(), vfs, uuid, type); - -//---------------------------------------------------------------------------- - S8 LLImageFormatted::getCodec() const { return mCodec; } -//============================================================================ - static void avg4_colors4(const U8* a, const U8* b, const U8* c, const U8* d, U8* dst) { dst[0] = (U8)(((U32)(a[0]) + b[0] + c[0] + d[0])>>2); diff --git a/indra/llinventory/CMakeLists.txt b/indra/llinventory/CMakeLists.txt index e829788c91..ba9f8af390 100644 --- a/indra/llinventory/CMakeLists.txt +++ b/indra/llinventory/CMakeLists.txt @@ -7,7 +7,7 @@ include(LLCommon) include(LLCoreHttp) include(LLMath) include(LLMessage) -include(LLVFS) +include(LLCache) include(LLXML) include_directories( @@ -81,7 +81,7 @@ if (LL_TESTS) LL_ADD_PROJECT_UNIT_TESTS(llinventory "${llinventory_TEST_SOURCE_FILES}") #set(TEST_DEBUG on) - set(test_libs llinventory ${LLMESSAGE_LIBRARIES} ${LLVFS_LIBRARIES} ${LLCOREHTTP_LIBRARIES} ${LLMATH_LIBRARIES} ${LLCOMMON_LIBRARIES} ${WINDOWS_LIBRARIES}) + set(test_libs llinventory ${LLMESSAGE_LIBRARIES} ${LLCACHE_LIBRARIES} ${LLCOREHTTP_LIBRARIES} ${LLMATH_LIBRARIES} ${LLCOMMON_LIBRARIES} ${WINDOWS_LIBRARIES}) LL_ADD_INTEGRATION_TEST(inventorymisc "" "${test_libs}") LL_ADD_INTEGRATION_TEST(llparcel "" "${test_libs}") endif (LL_TESTS) diff --git a/indra/llmessage/CMakeLists.txt b/indra/llmessage/CMakeLists.txt index 2f99ca069e..66a7d3ea04 100644 --- a/indra/llmessage/CMakeLists.txt +++ b/indra/llmessage/CMakeLists.txt @@ -9,7 +9,7 @@ include(LLCommon) include(LLCoreHttp) include(LLMath) include(LLMessage) -include(LLVFS) +include(LLCache) include(LLAddBuildTest) include(Python) include(Tut) @@ -23,7 +23,7 @@ include_directories( ${LLCOREHTTP_INCLUDE_DIRS} ${LLMATH_INCLUDE_DIRS} ${LLMESSAGE_INCLUDE_DIRS} - ${LLVFS_INCLUDE_DIRS} + ${LLCACHE_INCLUDE_DIRS} ${JSONCPP_INCLUDE_DIR} ) @@ -209,7 +209,7 @@ target_link_libraries( llmessage ${CURL_LIBRARIES} ${LLCOMMON_LIBRARIES} - ${LLVFS_LIBRARIES} + ${LLCACHE_LIBRARIES} ${LLMATH_LIBRARIES} ${JSONCPP_LIBRARIES} ${OPENSSL_LIBRARIES} @@ -227,7 +227,7 @@ target_link_libraries( llmessage ${CURL_LIBRARIES} ${LLCOMMON_LIBRARIES} - ${LLVFS_LIBRARIES} + ${LLCACHE_LIBRARIES} ${LLMATH_LIBRARIES} ${JSONCPP_LIBRARIES} ${OPENSSL_LIBRARIES} @@ -257,7 +257,7 @@ if (LL_TESTS) if (LINUX) set(test_libs ${WINDOWS_LIBRARIES} - ${LLVFS_LIBRARIES} + ${LLCACHE_LIBRARIES} ${LLMATH_LIBRARIES} ${CURL_LIBRARIES} ${NGHTTP2_LIBRARIES} @@ -273,7 +273,7 @@ if (LINUX) else (LINUX) set(test_libs ${WINDOWS_LIBRARIES} - ${LLVFS_LIBRARIES} + ${LLCACHE_LIBRARIES} ${LLMATH_LIBRARIES} ${CURL_LIBRARIES} ${NGHTTP2_LIBRARIES} diff --git a/indra/llmessage/llassetstorage.cpp b/indra/llmessage/llassetstorage.cpp index 56bc1d723a..be5d00d4c9 100644 --- a/indra/llmessage/llassetstorage.cpp +++ b/indra/llmessage/llassetstorage.cpp @@ -43,7 +43,6 @@ #include "message.h" #include "llxfermanager.h" #include "llvfile.h" -#include "llvfs.h" #include "lldbstrings.h" #include "lltransfersourceasset.h" @@ -202,7 +201,7 @@ LLBaseDownloadRequest::LLBaseDownloadRequest(const LLUUID &uuid, const LLAssetTy mIsTemp(FALSE), mIsPriority(FALSE), mDataSentInFirstPacket(FALSE), - mDataIsInVFS(FALSE) + mDataIsInCache(FALSE) { // Need to guarantee that this time is up to date, we may be creating a circuit even though we haven't been // running a message system loop. @@ -266,7 +265,8 @@ LLSD LLAssetRequest::getFullDetails() const sd["is_local"] = mIsLocal; sd["is_priority"] = mIsPriority; sd["data_send_in_first_packet"] = mDataSentInFirstPacket; - sd["data_is_in_vfs"] = mDataIsInVFS; + // Note: cannot change this (easily) since it is consumed by server + sd["data_is_in_vfs"] = mDataIsInCache; return sd; } @@ -330,28 +330,23 @@ LLBaseDownloadRequest* LLEstateAssetRequest::getCopy() // TODO: rework tempfile handling? -LLAssetStorage::LLAssetStorage(LLMessageSystem *msg, LLXferManager *xfer, LLVFS *vfs, LLVFS *static_vfs, const LLHost &upstream_host) +LLAssetStorage::LLAssetStorage(LLMessageSystem *msg, LLXferManager *xfer, const LLHost &upstream_host) { - _init(msg, xfer, vfs, static_vfs, upstream_host); + _init(msg, xfer, upstream_host); } -LLAssetStorage::LLAssetStorage(LLMessageSystem *msg, LLXferManager *xfer, - LLVFS *vfs, LLVFS *static_vfs) +LLAssetStorage::LLAssetStorage(LLMessageSystem *msg, LLXferManager *xfer) { - _init(msg, xfer, vfs, static_vfs, LLHost()); + _init(msg, xfer, LLHost()); } void LLAssetStorage::_init(LLMessageSystem *msg, LLXferManager *xfer, - LLVFS *vfs, - LLVFS *static_vfs, const LLHost &upstream_host) { mShutDown = FALSE; mMessageSys = msg; mXferManager = xfer; - mVFS = vfs; - mStaticVFS = static_vfs; setUpstream(upstream_host); msg->setHandlerFuncFast(_PREHASH_AssetUploadComplete, processUploadComplete, (void **)this); @@ -430,7 +425,7 @@ void LLAssetStorage::_cleanupRequests(BOOL all, S32 error) } if (tmp->mDownCallback) { - tmp->mDownCallback(mVFS, tmp->getUUID(), tmp->getType(), tmp->mUserData, error, LLExtStat::NONE); + tmp->mDownCallback(tmp->getUUID(), tmp->getType(), tmp->mUserData, error, LLExtStat::NONE); } if (tmp->mInfoCallback) { @@ -446,7 +441,7 @@ BOOL LLAssetStorage::hasLocalAsset(const LLUUID &uuid, const LLAssetType::EType return LLVFile::getExists(uuid, type); } -bool LLAssetStorage::findInStaticVFSAndInvokeCallback(const LLUUID& uuid, LLAssetType::EType type, +bool LLAssetStorage::findInCacheAndInvokeCallback(const LLUUID& uuid, LLAssetType::EType type, LLGetAssetCallback callback, void *user_data) { if (user_data) @@ -455,17 +450,17 @@ bool LLAssetStorage::findInStaticVFSAndInvokeCallback(const LLUUID& uuid, LLAsse llassert(callback != NULL); } - BOOL exists = mStaticVFS->getExists(uuid, type); + BOOL exists = LLVFile::getExists(uuid, type); if (exists) { - LLVFile file(mStaticVFS, uuid, type); + LLVFile file(uuid, type); U32 size = file.getSize(); if (size > 0) { // we've already got the file if (callback) { - callback(mStaticVFS, uuid, type, user_data, LL_ERR_NOERR, LLExtStat::VFS_CACHED); + callback(uuid, type, user_data, LL_ERR_NOERR, LLExtStat::CACHE_CACHED); } return true; } @@ -506,7 +501,7 @@ void LLAssetStorage::getAssetData(const LLUUID uuid, if (callback) { add(sFailedDownloadCount, 1); - callback(mVFS, uuid, type, user_data, LL_ERR_ASSET_REQUEST_FAILED, LLExtStat::NONE); + callback(uuid, type, user_data, LL_ERR_ASSET_REQUEST_FAILED, LLExtStat::NONE); } return; } @@ -517,20 +512,19 @@ void LLAssetStorage::getAssetData(const LLUUID uuid, if (callback) { add(sFailedDownloadCount, 1); - callback(mVFS, uuid, type, user_data, LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE, LLExtStat::NULL_UUID); + callback(uuid, type, user_data, LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE, LLExtStat::NULL_UUID); } return; } - // Try static VFS first. - if (findInStaticVFSAndInvokeCallback(uuid,type,callback,user_data)) + if (findInCacheAndInvokeCallback(uuid,type,callback,user_data)) { - LL_DEBUGS("AssetStorage") << "ASSET_TRACE asset " << uuid << " found in static VFS" << LL_ENDL; + LL_DEBUGS("AssetStorage") << "ASSET_TRACE asset " << uuid << " found in cache" << LL_ENDL; return; } - BOOL exists = mVFS->getExists(uuid, type); - LLVFile file(mVFS, uuid, type); + BOOL exists = LLVFile::getExists(uuid, type); + LLVFile file(uuid, type); U32 size = exists ? file.getSize() : 0; if (size > 0) @@ -540,10 +534,10 @@ void LLAssetStorage::getAssetData(const LLUUID uuid, // unless there's a weird error if (callback) { - callback(mVFS, uuid, type, user_data, LL_ERR_NOERR, LLExtStat::VFS_CACHED); + callback(uuid, type, user_data, LL_ERR_NOERR, LLExtStat::CACHE_CACHED); } - LL_DEBUGS("AssetStorage") << "ASSET_TRACE asset " << uuid << " found in VFS" << LL_ENDL; + LL_DEBUGS("AssetStorage") << "ASSET_TRACE asset " << uuid << " found in cache" << LL_ENDL; } else { @@ -616,7 +610,7 @@ void LLAssetStorage::removeAndCallbackPendingDownloads(const LLUUID& file_id, LL { add(sFailedDownloadCount, 1); } - tmp->mDownCallback(gAssetStorage->mVFS, callback_id, callback_type, tmp->mUserData, result_code, ext_status); + tmp->mDownCallback(callback_id, callback_type, tmp->mUserData, result_code, ext_status); } delete tmp; } @@ -670,7 +664,7 @@ void LLAssetStorage::downloadCompleteCallback( if (LL_ERR_NOERR == result) { // we might have gotten a zero-size file - LLVFile vfile(gAssetStorage->mVFS, callback_id, callback_type); + LLVFile vfile(callback_id, callback_type); if (vfile.getSize() <= 0) { LL_WARNS("AssetStorage") << "downloadCompleteCallback has non-existent or zero-size asset " << callback_id << LL_ENDL; @@ -719,19 +713,19 @@ void LLAssetStorage::getEstateAsset( if (callback) { add(sFailedDownloadCount, 1); - callback(mVFS, asset_id, atype, user_data, LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE, LLExtStat::NULL_UUID); + callback(asset_id, atype, user_data, LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE, LLExtStat::NULL_UUID); } return; } - // Try static VFS first. - if (findInStaticVFSAndInvokeCallback(asset_id,atype,callback,user_data)) + // Try static first. + if (findInCacheAndInvokeCallback(asset_id,atype,callback,user_data)) { return; } - BOOL exists = mVFS->getExists(asset_id, atype); - LLVFile file(mVFS, asset_id, atype); + BOOL exists = LLVFile::getExists(asset_id, atype); + LLVFile file(asset_id, atype); U32 size = exists ? file.getSize() : 0; if (size > 0) @@ -741,7 +735,7 @@ void LLAssetStorage::getEstateAsset( // unless there's a weird error if (callback) { - callback(mVFS, asset_id, atype, user_data, LL_ERR_NOERR, LLExtStat::VFS_CACHED); + callback(asset_id, atype, user_data, LL_ERR_NOERR, LLExtStat::CACHE_CACHED); } } else @@ -792,7 +786,7 @@ void LLAssetStorage::getEstateAsset( if (callback) { add(sFailedDownloadCount, 1); - callback(mVFS, asset_id, atype, user_data, LL_ERR_CIRCUIT_GONE, LLExtStat::NO_UPSTREAM); + callback(asset_id, atype, user_data, LL_ERR_CIRCUIT_GONE, LLExtStat::NO_UPSTREAM); } } } @@ -824,7 +818,7 @@ void LLAssetStorage::downloadEstateAssetCompleteCallback( if (LL_ERR_NOERR == result) { // we might have gotten a zero-size file - LLVFile vfile(gAssetStorage->mVFS, req->getUUID(), req->getAType()); + LLVFile vfile(req->getUUID(), req->getAType()); if (vfile.getSize() <= 0) { LL_WARNS("AssetStorage") << "downloadCompleteCallback has non-existent or zero-size asset!" << LL_ENDL; @@ -838,7 +832,7 @@ void LLAssetStorage::downloadEstateAssetCompleteCallback( { add(sFailedDownloadCount, 1); } - req->mDownCallback(gAssetStorage->mVFS, req->getUUID(), req->getAType(), req->mUserData, result, ext_status); + req->mDownCallback(req->getUUID(), req->getAType(), req->mUserData, result, ext_status); } void LLAssetStorage::getInvItemAsset( @@ -861,14 +855,13 @@ void LLAssetStorage::getInvItemAsset( if(asset_id.notNull()) { - // Try static VFS first. - if (findInStaticVFSAndInvokeCallback( asset_id, atype, callback, user_data)) + if (findInCacheAndInvokeCallback( asset_id, atype, callback, user_data)) { return; } - exists = mVFS->getExists(asset_id, atype); - LLVFile file(mVFS, asset_id, atype); + exists = LLVFile::getExists(asset_id, atype); + LLVFile file(asset_id, atype); size = exists ? file.getSize() : 0; if(exists && size < 1) { @@ -885,7 +878,7 @@ void LLAssetStorage::getInvItemAsset( // unless there's a weird error if (callback) { - callback(mVFS, asset_id, atype, user_data, LL_ERR_NOERR, LLExtStat::VFS_CACHED); + callback(asset_id, atype, user_data, LL_ERR_NOERR, LLExtStat::CACHE_CACHED); } } else @@ -936,7 +929,7 @@ void LLAssetStorage::getInvItemAsset( if (callback) { add(sFailedDownloadCount, 1); - callback(mVFS, asset_id, atype, user_data, LL_ERR_CIRCUIT_GONE, LLExtStat::NO_UPSTREAM); + callback(asset_id, atype, user_data, LL_ERR_CIRCUIT_GONE, LLExtStat::NO_UPSTREAM); } } } @@ -968,7 +961,7 @@ void LLAssetStorage::downloadInvItemCompleteCallback( if (LL_ERR_NOERR == result) { // we might have gotten a zero-size file - LLVFile vfile(gAssetStorage->mVFS, req->getUUID(), req->getType()); + LLVFile vfile(req->getUUID(), req->getType()); if (vfile.getSize() <= 0) { LL_WARNS("AssetStorage") << "downloadCompleteCallback has non-existent or zero-size asset!" << LL_ENDL; @@ -982,7 +975,7 @@ void LLAssetStorage::downloadInvItemCompleteCallback( { add(sFailedDownloadCount, 1); } - req->mDownCallback(gAssetStorage->mVFS, req->getUUID(), req->getType(), req->mUserData, result, ext_status); + req->mDownCallback(req->getUUID(), req->getType(), req->mUserData, result, ext_status); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -1293,7 +1286,7 @@ bool LLAssetStorage::deletePendingRequestImpl(LLAssetStorage::request_list_t* re if (req->mDownCallback) { add(sFailedDownloadCount, 1); - req->mDownCallback(mVFS, req->getUUID(), req->getType(), req->mUserData, error, LLExtStat::REQUEST_DROPPED); + req->mDownCallback(req->getUUID(), req->getType(), req->mUserData, error, LLExtStat::REQUEST_DROPPED); } if (req->mInfoCallback) { @@ -1363,8 +1356,7 @@ void LLAssetStorage::getAssetData(const LLUUID uuid, { LLAssetRequest* tmp = *iter++; - //void(*const* cbptr)(LLVFS *, const LLUUID &, LLAssetType::EType, void *, S32, LLExtStat) - auto cbptr = tmp->mDownCallback.target(); + auto cbptr = tmp->mDownCallback.target(); if (type == tmp->getType() && uuid == tmp->getUUID() && @@ -1389,8 +1381,7 @@ void LLAssetStorage::getAssetData(const LLUUID uuid, } // static -void LLAssetStorage::legacyGetDataCallback(LLVFS *vfs, - const LLUUID &uuid, +void LLAssetStorage::legacyGetDataCallback(const LLUUID &uuid, LLAssetType::EType type, void *user_data, S32 status, @@ -1405,7 +1396,7 @@ void LLAssetStorage::legacyGetDataCallback(LLVFS *vfs, if ( !status && !toxic ) { - LLVFile file(vfs, uuid, type); + LLVFile file(uuid, type); std::string uuid_str; diff --git a/indra/llmessage/llassetstorage.h b/indra/llmessage/llassetstorage.h index c799d8eefc..e0f22f1160 100644 --- a/indra/llmessage/llassetstorage.h +++ b/indra/llmessage/llassetstorage.h @@ -44,7 +44,6 @@ class LLMessageSystem; class LLXferManager; class LLAssetStorage; -class LLVFS; class LLSD; // anything that takes longer than this to download will abort. @@ -60,11 +59,11 @@ const int LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE = -4; const int LL_ERR_INSUFFICIENT_PERMISSIONS = -5; const int LL_ERR_PRICE_MISMATCH = -23018; -// *TODO: these typedefs are passed into the VFS via a legacy C function pointer +// *TODO: these typedefs are passed into the cache via a legacy C function pointer // future project would be to convert these to C++ callables (std::function<>) so that // we can use bind and remove the userData parameter. // -typedef std::function LLGetAssetCallback; +typedef std::function LLGetAssetCallback; typedef std::function LLStoreAssetCallback; @@ -120,7 +119,6 @@ protected: public: LLGetAssetCallback mDownCallback; -// void(*mDownCallback)(LLVFS*, const LLUUID&, LLAssetType::EType, void *, S32, LLExtStat); void *mUserData; LLHost mHost; @@ -128,7 +126,7 @@ public: F64Seconds mTime; // Message system time BOOL mIsPriority; BOOL mDataSentInFirstPacket; - BOOL mDataIsInVFS; + BOOL mDataIsInCache; }; class LLAssetRequest : public LLBaseDownloadRequest @@ -198,9 +196,6 @@ typedef std::map toxic_asset_map_t; class LLAssetStorage { public: - // VFS member is public because static child methods need it :( - LLVFS *mVFS; - LLVFS *mStaticVFS; typedef ::LLStoreAssetCallback LLStoreAssetCallback; typedef ::LLGetAssetCallback LLGetAssetCallback; @@ -230,11 +225,9 @@ protected: toxic_asset_map_t mToxicAssetMap; // Objects in this list are known to cause problems and are not loaded public: - LLAssetStorage(LLMessageSystem *msg, LLXferManager *xfer, - LLVFS *vfs, LLVFS *static_vfs, const LLHost &upstream_host); + LLAssetStorage(LLMessageSystem *msg, LLXferManager *xfer, const LLHost &upstream_host); - LLAssetStorage(LLMessageSystem *msg, LLXferManager *xfer, - LLVFS *vfs, LLVFS *static_vfs); + LLAssetStorage(LLMessageSystem *msg, LLXferManager *xfer); virtual ~LLAssetStorage(); void setUpstream(const LLHost &upstream_host); @@ -284,7 +277,7 @@ public: void markAssetToxic( const LLUUID& uuid ); protected: - bool findInStaticVFSAndInvokeCallback(const LLUUID& uuid, LLAssetType::EType type, + bool findInCacheAndInvokeCallback(const LLUUID& uuid, LLAssetType::EType type, LLGetAssetCallback callback, void *user_data); LLSD getPendingDetailsImpl(const request_list_t* requests, @@ -375,7 +368,7 @@ public: bool user_waiting = false, F64Seconds timeout = LL_ASSET_STORAGE_TIMEOUT) = 0; - static void legacyGetDataCallback(LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType, void *user_data, S32 status, LLExtStat ext_status); + static void legacyGetDataCallback(const LLUUID &uuid, LLAssetType::EType, void *user_data, S32 status, LLExtStat ext_status); static void legacyStoreDataCallback(const LLUUID &uuid, void *user_data, S32 status, LLExtStat ext_status); // add extra methods to handle metadata @@ -385,15 +378,12 @@ protected: void _callUploadCallbacks(const LLUUID &uuid, const LLAssetType::EType asset_type, BOOL success, LLExtStat ext_status); virtual void _queueDataRequest(const LLUUID& uuid, LLAssetType::EType type, LLGetAssetCallback callback, -// void (*callback)(LLVFS *vfs, const LLUUID&, LLAssetType::EType, void *, S32, LLExtStat), void *user_data, BOOL duplicate, BOOL is_priority) = 0; private: void _init(LLMessageSystem *msg, LLXferManager *xfer, - LLVFS *vfs, - LLVFS *static_vfs, const LLHost &upstream_host); protected: @@ -408,7 +398,7 @@ protected: MR_FILE_NONEXIST = 3, // Old format store call - source file does not exist MR_NO_FILENAME = 4, // Old format store call - source filename is NULL/0-length MR_NO_UPSTREAM = 5, // Upstream provider is missing - MR_VFS_CORRUPTION = 6 // VFS is corrupt - too-large or mismatched stated/returned sizes + MR_CACHE_CORRUPTION = 6 // cache is corrupt - too-large or mismatched stated/returned sizes }; static class LLMetrics *metric_recipient; diff --git a/indra/llmessage/llcorehttputil.cpp b/indra/llmessage/llcorehttputil.cpp index 0eae6d9826..c931a89b5b 100644 --- a/indra/llmessage/llcorehttputil.cpp +++ b/indra/llmessage/llcorehttputil.cpp @@ -784,7 +784,7 @@ LLSD HttpCoroutineAdapter::postFileAndSuspend(LLCore::HttpRequest::ptr_t request // scoping for our streams so that they go away when we no longer need them. { LLCore::BufferArrayStream outs(fileData.get()); - LLVFile vfile(gVFS, assetId, assetType, LLVFile::READ); + LLVFile vfile(assetId, assetType, LLVFile::READ); S32 fileSize = vfile.getSize(); U8* fileBuffer; diff --git a/indra/llmessage/llextendedstatus.h b/indra/llmessage/llextendedstatus.h index 9923d73c1a..2a53dced80 100644 --- a/indra/llmessage/llextendedstatus.h +++ b/indra/llmessage/llextendedstatus.h @@ -1,7 +1,7 @@ /** * @file llextendedstatus.h * @date August 2007 - * @brief extended status codes for curl/vfs/resident asset storage and delivery + * @brief extended status codes for curl/resident asset storage and delivery * * $LicenseInfo:firstyear=2007&license=viewerlgpl$ * Second Life Viewer Source Code @@ -32,9 +32,9 @@ enum class LLExtStat: uint32_t { // Status provider groups - Top bits indicate which status type it is // Zero is common status code (next section) - CURL_RESULT = 1UL<<30, // serviced by curl - use 1L if we really implement the below - RES_RESULT = 2UL<<30, // serviced by resident copy - VFS_RESULT = 3UL<<30, // serviced by vfs + CURL_RESULT = 1UL<<30, // serviced by curl - use 1L if we really implement the below + RES_RESULT = 2UL<<30, // serviced by resident copy + CACHE_RESULT = 3UL<<30, // serviced by cache // Common Status Codes @@ -54,9 +54,9 @@ enum class LLExtStat: uint32_t // Memory-Resident status codes: // None at present - // VFS status codes: - VFS_CACHED = VFS_RESULT | 0x0001, - VFS_CORRUPT = VFS_RESULT | 0x0002, + // CACHE status codes: + CACHE_CACHED = CACHE_RESULT | 0x0001, + CACHE_CORRUPT = CACHE_RESULT | 0x0002, }; diff --git a/indra/llmessage/lltransfersourceasset.cpp b/indra/llmessage/lltransfersourceasset.cpp index 80ed3340c6..0156d1a5ef 100644 --- a/indra/llmessage/lltransfersourceasset.cpp +++ b/indra/llmessage/lltransfersourceasset.cpp @@ -99,7 +99,7 @@ LLTSCode LLTransferSourceAsset::dataCallback(const S32 packet_id, return LLTS_SKIP; } - LLVFile vf(gAssetStorage->mVFS, mParams.getAssetID(), mParams.getAssetType(), LLVFile::READ); + LLVFile vf(mParams.getAssetID(), mParams.getAssetType(), LLVFile::READ); if (!vf.getSize()) { @@ -171,7 +171,7 @@ BOOL LLTransferSourceAsset::unpackParams(LLDataPacker &dp) } -void LLTransferSourceAsset::responderCallback(LLVFS *vfs, const LLUUID& uuid, LLAssetType::EType type, +void LLTransferSourceAsset::responderCallback(const LLUUID& uuid, LLAssetType::EType type, void *user_data, S32 result, LLExtStat ext_status ) { LLUUID *tidp = ((LLUUID*) user_data); @@ -198,7 +198,7 @@ void LLTransferSourceAsset::responderCallback(LLVFS *vfs, const LLUUID& uuid, LL if (LL_ERR_NOERR == result) { // Everything's OK. - LLVFile vf(gAssetStorage->mVFS, uuid, type, LLVFile::READ); + LLVFile vf(uuid, type, LLVFile::READ); tsap->mSize = vf.getSize(); status = LLTS_OK; } diff --git a/indra/llmessage/lltransfersourceasset.h b/indra/llmessage/lltransfersourceasset.h index 3abda83cf8..2c798d0598 100644 --- a/indra/llmessage/lltransfersourceasset.h +++ b/indra/llmessage/lltransfersourceasset.h @@ -56,7 +56,7 @@ public: LLTransferSourceAsset(const LLUUID &request_id, const F32 priority); virtual ~LLTransferSourceAsset(); - static void responderCallback(LLVFS *vfs, const LLUUID& uuid, LLAssetType::EType type, + static void responderCallback(const LLUUID& uuid, LLAssetType::EType type, void *user_data, S32 result, LLExtStat ext_status ); protected: /*virtual*/ void initTransfer(); diff --git a/indra/llmessage/lltransfertargetvfile.cpp b/indra/llmessage/lltransfertargetvfile.cpp index b27f0881e0..f2e0232a05 100644 --- a/indra/llmessage/lltransfertargetvfile.cpp +++ b/indra/llmessage/lltransfertargetvfile.cpp @@ -138,7 +138,7 @@ LLTSCode LLTransferTargetVFile::dataCallback(const S32 packet_id, U8 *in_datap, //LL_INFOS() << "LLTransferTargetFile::dataCallback" << LL_ENDL; //LL_INFOS() << "Packet: " << packet_id << LL_ENDL; - LLVFile vf(gAssetStorage->mVFS, mTempID, mParams.getAssetType(), LLVFile::APPEND); + LLVFile vf(mTempID, mParams.getAssetType(), LLVFile::APPEND); if (mNeedsCreate) { vf.setMaxSize(mSize); @@ -176,7 +176,7 @@ void LLTransferTargetVFile::completionCallback(const LLTSCode status) case LLTS_DONE: if (!mNeedsCreate) { - LLVFile file(gAssetStorage->mVFS, mTempID, mParams.getAssetType(), LLVFile::WRITE); + LLVFile file(mTempID, mParams.getAssetType(), LLVFile::WRITE); if (!file.rename(mParams.getAssetID(), mParams.getAssetType())) { LL_ERRS() << "LLTransferTargetVFile: rename failed" << LL_ENDL; @@ -195,7 +195,7 @@ void LLTransferTargetVFile::completionCallback(const LLTSCode status) { // We're aborting this transfer, we don't want to keep this file. LL_WARNS() << "Aborting vfile transfer for " << mParams.getAssetID() << LL_ENDL; - LLVFile vf(gAssetStorage->mVFS, mTempID, mParams.getAssetType(), LLVFile::APPEND); + LLVFile vf(mTempID, mParams.getAssetType(), LLVFile::APPEND); vf.remove(); } break; diff --git a/indra/llmessage/llxfer_vfile.cpp b/indra/llmessage/llxfer_vfile.cpp index ddc24342f6..0835970cfc 100644 --- a/indra/llmessage/llxfer_vfile.cpp +++ b/indra/llmessage/llxfer_vfile.cpp @@ -31,7 +31,6 @@ #include "llerror.h" #include "llmath.h" #include "llvfile.h" -#include "llvfs.h" #include "lldir.h" // size of chunks read from/written to disk @@ -42,13 +41,13 @@ const U32 LL_MAX_XFER_FILE_BUFFER = 65536; LLXfer_VFile::LLXfer_VFile () : LLXfer(-1) { - init(NULL, LLUUID::null, LLAssetType::AT_NONE); + init(LLUUID::null, LLAssetType::AT_NONE); } -LLXfer_VFile::LLXfer_VFile (LLVFS *vfs, const LLUUID &local_id, LLAssetType::EType type) +LLXfer_VFile::LLXfer_VFile (const LLUUID &local_id, LLAssetType::EType type) : LLXfer(-1) { - init(vfs, local_id, type); + init(local_id, type); } /////////////////////////////////////////////////////////// @@ -60,10 +59,8 @@ LLXfer_VFile::~LLXfer_VFile () /////////////////////////////////////////////////////////// -void LLXfer_VFile::init (LLVFS *vfs, const LLUUID &local_id, LLAssetType::EType type) +void LLXfer_VFile::init (const LLUUID &local_id, LLAssetType::EType type) { - - mVFS = vfs; mLocalID = local_id; mType = type; @@ -82,14 +79,14 @@ void LLXfer_VFile::cleanup () if (mTempID.notNull() && mDeleteTempFile) { - if (mVFS->getExists(mTempID, mType)) + if (LLVFile::getExists(mTempID, mType)) { - LLVFile file(mVFS, mTempID, mType, LLVFile::WRITE); + LLVFile file(mTempID, mType, LLVFile::WRITE); file.remove(); } else { - LL_WARNS("Xfer") << "LLXfer_VFile::cleanup() can't open to delete VFS file " << mTempID << "." << LLAssetType::lookup(mType) + LL_WARNS("Xfer") << "LLXfer_VFile::cleanup() can't open to delete cache file " << mTempID << "." << LLAssetType::lookup(mType) << ", mRemoteID is " << mRemoteID << LL_ENDL; } } @@ -103,7 +100,6 @@ void LLXfer_VFile::cleanup () /////////////////////////////////////////////////////////// S32 LLXfer_VFile::initializeRequest(U64 xfer_id, - LLVFS* vfs, const LLUUID& local_id, const LLUUID& remote_id, LLAssetType::EType type, @@ -115,7 +111,6 @@ S32 LLXfer_VFile::initializeRequest(U64 xfer_id, mRemoteHost = remote_host; - mVFS = vfs; mLocalID = local_id; mRemoteID = remote_id; mType = type; @@ -192,13 +187,13 @@ S32 LLXfer_VFile::startSend (U64 xfer_id, const LLHost &remote_host) delete mVFile; mVFile = NULL; - if(mVFS->getExists(mLocalID, mType)) + if(LLVFile::getExists(mLocalID, mType)) { - mVFile = new LLVFile(mVFS, mLocalID, mType, LLVFile::READ); + mVFile = new LLVFile(mLocalID, mType, LLVFile::READ); if (mVFile->getSize() <= 0) { - LL_WARNS("Xfer") << "LLXfer_VFile::startSend() VFS file " << mLocalID << "." << LLAssetType::lookup(mType) + LL_WARNS("Xfer") << "LLXfer_VFile::startSend() cache file " << mLocalID << "." << LLAssetType::lookup(mType) << " has unexpected file size of " << mVFile->getSize() << LL_ENDL; delete mVFile; mVFile = NULL; @@ -214,7 +209,7 @@ S32 LLXfer_VFile::startSend (U64 xfer_id, const LLHost &remote_host) } else { - LL_WARNS("Xfer") << "LLXfer_VFile::startSend() can't read VFS file " << mLocalID << "." << LLAssetType::lookup(mType) << LL_ENDL; + LL_WARNS("Xfer") << "LLXfer_VFile::startSend() can't read cache file " << mLocalID << "." << LLAssetType::lookup(mType) << LL_ENDL; retval = LL_ERR_FILE_NOT_FOUND; } @@ -240,13 +235,13 @@ S32 LLXfer_VFile::reopenFileHandle() if (mVFile == NULL) { - if (mVFS->getExists(mLocalID, mType)) + if (LLVFile::getExists(mLocalID, mType)) { - mVFile = new LLVFile(mVFS, mLocalID, mType, LLVFile::READ); + mVFile = new LLVFile(mLocalID, mType, LLVFile::READ); } else { - LL_WARNS("Xfer") << "LLXfer_VFile::reopenFileHandle() can't read VFS file " << mLocalID << "." << LLAssetType::lookup(mType) << LL_ENDL; + LL_WARNS("Xfer") << "LLXfer_VFile::reopenFileHandle() can't read cache file " << mLocalID << "." << LLAssetType::lookup(mType) << LL_ENDL; retval = LL_ERR_FILE_NOT_FOUND; } } @@ -265,7 +260,7 @@ void LLXfer_VFile::setXferSize (S32 xfer_size) // It would be nice if LLXFers could tell which end of the pipe they were if (! mVFile) { - LLVFile file(mVFS, mTempID, mType, LLVFile::APPEND); + LLVFile file(mTempID, mType, LLVFile::APPEND); file.setMaxSize(xfer_size); } } @@ -320,7 +315,7 @@ S32 LLXfer_VFile::flush() S32 retval = 0; if (mBufferLength) { - LLVFile file(mVFS, mTempID, mType, LLVFile::APPEND); + LLVFile file(mTempID, mType, LLVFile::APPEND); file.write((U8*)mBuffer, mBufferLength); @@ -340,22 +335,15 @@ S32 LLXfer_VFile::processEOF() if (!mCallbackResult) { - if (mVFS->getExists(mTempID, mType)) + if (LLVFile::getExists(mTempID, mType)) { - LLVFile file(mVFS, mTempID, mType, LLVFile::WRITE); + LLVFile file(mTempID, mType, LLVFile::WRITE); if (!file.rename(mLocalID, mType)) { - LL_WARNS("Xfer") << "VFS rename of temp file failed: unable to rename " << mTempID << " to " << mLocalID << LL_ENDL; + LL_WARNS("Xfer") << "Cache rename of temp file failed: unable to rename " << mTempID << " to " << mLocalID << LL_ENDL; } else - { - #ifdef VFS_SPAM - // Debugging spam - LL_INFOS("Xfer") << "VFS rename of temp file done: renamed " << mTempID << " to " << mLocalID - << " LLVFile size is " << file.getSize() - << LL_ENDL; - #endif - + { // Rename worked: the original file is gone. Clear mDeleteTempFile // so we don't attempt to delete the file in cleanup() mDeleteTempFile = FALSE; @@ -363,7 +351,7 @@ S32 LLXfer_VFile::processEOF() } else { - LL_WARNS("Xfer") << "LLXfer_VFile::processEOF() can't open for renaming VFS file " << mTempID << "." << LLAssetType::lookup(mType) << LL_ENDL; + LL_WARNS("Xfer") << "LLXfer_VFile::processEOF() can't open for renaming cache file " << mTempID << "." << LLAssetType::lookup(mType) << LL_ENDL; } } diff --git a/indra/llmessage/llxfer_vfile.h b/indra/llmessage/llxfer_vfile.h index 5bf9a5cfba..d830c4be96 100644 --- a/indra/llmessage/llxfer_vfile.h +++ b/indra/llmessage/llxfer_vfile.h @@ -30,7 +30,6 @@ #include "llxfer.h" #include "llassetstorage.h" -class LLVFS; class LLVFile; class LLXfer_VFile : public LLXfer @@ -43,22 +42,19 @@ class LLXfer_VFile : public LLXfer LLVFile *mVFile; - LLVFS *mVFS; - std::string mName; BOOL mDeleteTempFile; public: LLXfer_VFile (); - LLXfer_VFile (LLVFS *vfs, const LLUUID &local_id, LLAssetType::EType type); + LLXfer_VFile (const LLUUID &local_id, LLAssetType::EType type); virtual ~LLXfer_VFile(); - virtual void init(LLVFS *vfs, const LLUUID &local_id, LLAssetType::EType type); + virtual void init(const LLUUID &local_id, LLAssetType::EType type); virtual void cleanup(); virtual S32 initializeRequest(U64 xfer_id, - LLVFS *vfs, const LLUUID &local_id, const LLUUID &remote_id, const LLAssetType::EType type, diff --git a/indra/llmessage/llxfermanager.cpp b/indra/llmessage/llxfermanager.cpp index 4cea886c8a..f9b59d7e42 100644 --- a/indra/llmessage/llxfermanager.cpp +++ b/indra/llmessage/llxfermanager.cpp @@ -56,9 +56,9 @@ const S32 LL_DEFAULT_MAX_HARD_LIMIT_SIMULTANEOUS_XFERS = 500; /////////////////////////////////////////////////////////// -LLXferManager::LLXferManager (LLVFS *vfs) +LLXferManager::LLXferManager () { - init(vfs); + init(); } /////////////////////////////////////////////////////////// @@ -70,7 +70,7 @@ LLXferManager::~LLXferManager () /////////////////////////////////////////////////////////// -void LLXferManager::init (LLVFS *vfs) +void LLXferManager::init() { cleanup(); @@ -78,8 +78,6 @@ void LLXferManager::init (LLVFS *vfs) setHardLimitOutgoingXfersPerCircuit(LL_DEFAULT_MAX_HARD_LIMIT_SIMULTANEOUS_XFERS); setMaxIncomingXfers(LL_DEFAULT_MAX_REQUEST_FIFO_XFERS); - mVFS = vfs; - // Turn on or off ack throttling mUseAckThrottling = FALSE; setAckThrottleBPS(100000); @@ -462,7 +460,7 @@ U64 LLXferManager::requestFile(const std::string& local_filename, void LLXferManager::requestVFile(const LLUUID& local_id, const LLUUID& remote_id, - LLAssetType::EType type, LLVFS* vfs, + LLAssetType::EType type, const LLHost& remote_host, void (*callback)(void**,S32,LLExtStat), void** user_data, @@ -508,7 +506,6 @@ void LLXferManager::requestVFile(const LLUUID& local_id, addToList(xfer_p, mReceiveList, is_priority); ((LLXfer_VFile *)xfer_p)->initializeRequest(getNextID(), - vfs, local_id, remote_id, type, @@ -784,33 +781,17 @@ void LLXferManager::processFileRequest (LLMessageSystem *mesgsys, void ** /*user LLXfer *xferp; if (uuid != LLUUID::null) - { // Request for an asset - use a VFS file + { // Request for an asset - use a cache file if(NULL == LLAssetType::lookup(type)) { LL_WARNS("Xfer") << "Invalid type for xfer request: " << uuid << ":" << type_s16 << " to " << mesgsys->getSender() << LL_ENDL; return; } - - if (! mVFS) - { - LL_WARNS("Xfer") << "Attempt to send VFile w/o available VFS" << LL_ENDL; - return; - } - - /* Present in fireengine, not used by viewer - if (!validateVFileForTransfer(uuid.asString())) - { - // it is up to the app sending the file to mark it for expected - // transfer before the request arrives or it will be dropped - LL_WARNS("Xfer") << "SECURITY: Unapproved VFile '" << uuid << "'" << LL_ENDL; - return; - } - */ LL_INFOS("Xfer") << "starting vfile transfer: " << uuid << "," << LLAssetType::lookup(type) << " to " << mesgsys->getSender() << LL_ENDL; - xferp = (LLXfer *)new LLXfer_VFile(mVFS, uuid, type); + xferp = (LLXfer *)new LLXfer_VFile(uuid, type); if (xferp) { mSendList.push_front(xferp); @@ -1273,9 +1254,9 @@ void LLXferManager::addToList(LLXfer* xferp, xfer_list_t & xfer_list, BOOL is_pr LLXferManager *gXferManager = NULL; -void start_xfer_manager(LLVFS *vfs) +void start_xfer_manager() { - gXferManager = new LLXferManager(vfs); + gXferManager = new LLXferManager(); } void cleanup_xfer_manager() diff --git a/indra/llmessage/llxfermanager.h b/indra/llmessage/llxfermanager.h index 45ae2ffdd3..f49209bed0 100644 --- a/indra/llmessage/llxfermanager.h +++ b/indra/llmessage/llxfermanager.h @@ -35,7 +35,6 @@ //Forward declaration to avoid circular dependencies class LLXfer; -class LLVFS; #include "llxfer.h" #include "message.h" @@ -72,9 +71,6 @@ public: class LLXferManager { - private: - LLVFS *mVFS; - protected: S32 mMaxOutgoingXfersPerCircuit; S32 mHardLimitOutgoingXfersPerCircuit; // At this limit, kill off the connection @@ -111,10 +107,10 @@ class LLXferManager std::multiset mExpectedVFileRequests; // files that are authorized to be downloaded on top of public: - LLXferManager(LLVFS *vfs); + LLXferManager(); virtual ~LLXferManager(); - virtual void init(LLVFS *vfs); + virtual void init(); virtual void cleanup(); void setUseAckThrottling(const BOOL use); @@ -166,7 +162,7 @@ class LLXferManager // vfile requesting // .. to vfile virtual void requestVFile(const LLUUID &local_id, const LLUUID& remote_id, - LLAssetType::EType type, LLVFS* vfs, + LLAssetType::EType type, const LLHost& remote_host, void (*callback)(void**,S32,LLExtStat), void** user_data, BOOL is_priority = FALSE); @@ -213,7 +209,7 @@ class LLXferManager extern LLXferManager* gXferManager; // initialization and garbage collection -void start_xfer_manager(LLVFS *vfs); +void start_xfer_manager(); void cleanup_xfer_manager(); // message system callbacks diff --git a/indra/llrender/CMakeLists.txt b/indra/llrender/CMakeLists.txt index 47e7ad915b..524e2a1729 100644 --- a/indra/llrender/CMakeLists.txt +++ b/indra/llrender/CMakeLists.txt @@ -9,10 +9,9 @@ include(LLCommon) include(LLImage) include(LLMath) include(LLRender) -include(LLVFS) include(LLWindow) include(LLXML) -include(LLVFS) +include(LLCache) include_directories( ${FREETYPE_INCLUDE_DIRS} @@ -20,10 +19,10 @@ include_directories( ${LLIMAGE_INCLUDE_DIRS} ${LLMATH_INCLUDE_DIRS} ${LLRENDER_INCLUDE_DIRS} - ${LLVFS_INCLUDE_DIRS} + ${LLCACHE_INCLUDE_DIRS} ${LLWINDOW_INCLUDE_DIRS} ${LLXML_INCLUDE_DIRS} - ${LLVFS_INCLUDE_DIRS} + ${LLCACHE_INCLUDE_DIRS} ) include_directories(SYSTEM ${LLCOMMON_SYSTEM_INCLUDE_DIRS} @@ -104,9 +103,9 @@ if (BUILD_HEADLESS) ${LLIMAGE_LIBRARIES} ${LLMATH_LIBRARIES} ${LLRENDER_HEADLESS_LIBRARIES} - ${LLVFS_LIBRARIES} + ${LLCACHE_LIBRARIES} ${LLXML_LIBRARIES} - ${LLVFS_LIBRARIES} + ${LLCACHE_LIBRARIES} ${LLWINDOW_HEADLESS_LIBRARIES} ${OPENGL_HEADLESS_LIBRARIES}) @@ -126,9 +125,8 @@ target_link_libraries(llrender ${LLCOMMON_LIBRARIES} ${LLIMAGE_LIBRARIES} ${LLMATH_LIBRARIES} - ${LLVFS_LIBRARIES} + ${LLCACHE_LIBRARIES} ${LLXML_LIBRARIES} - ${LLVFS_LIBRARIES} ${LLWINDOW_LIBRARIES} ${FREETYPE_LIBRARIES} ${OPENGL_LIBRARIES}) diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt index cce618487b..02020b19c6 100644 --- a/indra/llui/CMakeLists.txt +++ b/indra/llui/CMakeLists.txt @@ -13,7 +13,7 @@ include(LLCoreHttp) include(LLRender) include(LLWindow) include(LLCoreHttp) -include(LLVFS) +include(LLCache) include(LLXML) include_directories( @@ -25,7 +25,7 @@ include_directories( ${LLMESSAGE_INCLUDE_DIRS} ${LLRENDER_INCLUDE_DIRS} ${LLWINDOW_INCLUDE_DIRS} - ${LLVFS_INCLUDE_DIRS} + ${LLCACHE_INCLUDE_DIRS} ${LLXML_INCLUDE_DIRS} ${LIBS_PREBUILD_DIR}/include/hunspell ) @@ -283,7 +283,7 @@ target_link_libraries(llui ${LLINVENTORY_LIBRARIES} ${LLMESSAGE_LIBRARIES} ${LLCOREHTTP_LIBRARIES} - ${LLVFS_LIBRARIES} # ugh, just for LLDir + ${LLCACHE_LIBRARIES} # ugh, just for LLDir ${LLXUIXML_LIBRARIES} ${LLXML_LIBRARIES} ${LLMATH_LIBRARIES} diff --git a/indra/llvfs/llvfile.cpp b/indra/llvfs/llvfile.cpp deleted file mode 100644 index d05c419d89..0000000000 --- a/indra/llvfs/llvfile.cpp +++ /dev/null @@ -1,676 +0,0 @@ -/** - * @file llvfile.cpp - * @brief Implementation of virtual file - * - * $LicenseInfo:firstyear=2002&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "linden_common.h" - -#include "llvfile.h" - -#include "llerror.h" -#include "llthread.h" -#include "lltimer.h" -#include "llfasttimer.h" -#include "llmemory.h" -#include "llvfs.h" - -#include -#include "lldir.h" - -const S32 LLVFile::READ = 0x00000001; -const S32 LLVFile::WRITE = 0x00000002; -const S32 LLVFile::READ_WRITE = 0x00000003; // LLVFile::READ & LLVFile::WRITE -const S32 LLVFile::APPEND = 0x00000006; // 0x00000004 & LLVFile::WRITE - -static LLTrace::BlockTimerStatHandle FTM_VFILE_WAIT("VFile Wait"); - -//---------------------------------------------------------------------------- -//CP LLVFSThread* LLVFile::sVFSThread = NULL; -//CP BOOL LLVFile::sAllocdVFSThread = FALSE; -//---------------------------------------------------------------------------- - -//============================================================================ - -LLVFile::LLVFile(LLVFS *vfs, const LLUUID &file_id, const LLAssetType::EType file_type, S32 mode) -{ - mFileType = file_type; - mFileID = file_id; - mPosition = 0; - mBytesRead = 0; - mReadComplete = FALSE; - mMode = mode; - //CP mVFS = vfs; - - //CP mHandle = LLVFSThread::nullHandle(); - //CP mPriority = 128.f; - - //CP mVFS->incLock(mFileID, mFileType, VFSLOCK_OPEN); -} - -LLVFile::~LLVFile() -{ - //CP BEGIN - //if (!isReadComplete()) - //{ - // if (mHandle != LLVFSThread::nullHandle()) - // { - // if (!(mMode & LLVFile::WRITE)) - // { - // //LL_WARNS() << "Destroying LLVFile with pending async read/write, aborting..." << LL_ENDL; - // sVFSThread->setFlags(mHandle, LLVFSThread::FLAG_AUTO_COMPLETE | LLVFSThread::FLAG_ABORT); - // } - // else // WRITE - // { - // sVFSThread->setFlags(mHandle, LLVFSThread::FLAG_AUTO_COMPLETE); - // } - // } - //} - //mVFS->decLock(mFileID, mFileType, VFSLOCK_OPEN); - //CP END -} - -const std::string assetTypeToString(LLAssetType::EType at) -{ - /** - * Make use of the C++17 (or is it 14) feature that allows - * for inline initialization of an std::map<> - */ - typedef std::map asset_type_to_name_t; - asset_type_to_name_t asset_type_to_name = - { - { LLAssetType::AT_TEXTURE, "TEXTURE" }, - { LLAssetType::AT_SOUND, "SOUND" }, - { LLAssetType::AT_CALLINGCARD, "CALLINGCARD" }, - { LLAssetType::AT_LANDMARK, "LANDMARK" }, - { LLAssetType::AT_SCRIPT, "SCRIPT" }, - { LLAssetType::AT_CLOTHING, "CLOTHING" }, - { LLAssetType::AT_OBJECT, "OBJECT" }, - { LLAssetType::AT_NOTECARD, "NOTECARD" }, - { LLAssetType::AT_CATEGORY, "CATEGORY" }, - { LLAssetType::AT_LSL_TEXT, "LSL_TEXT" }, - { LLAssetType::AT_LSL_BYTECODE, "LSL_BYTECODE" }, - { LLAssetType::AT_TEXTURE_TGA, "TEXTURE_TGA" }, - { LLAssetType::AT_BODYPART, "BODYPART" }, - { LLAssetType::AT_SOUND_WAV, "SOUND_WAV" }, - { LLAssetType::AT_IMAGE_TGA, "IMAGE_TGA" }, - { LLAssetType::AT_IMAGE_JPEG, "IMAGE_JPEG" }, - { LLAssetType::AT_ANIMATION, "ANIMATION" }, - { LLAssetType::AT_GESTURE, "GESTURE" }, - { LLAssetType::AT_SIMSTATE, "SIMSTATE" }, - { LLAssetType::AT_LINK, "LINK" }, - { LLAssetType::AT_LINK_FOLDER, "LINK_FOLDER" }, - { LLAssetType::AT_MARKETPLACE_FOLDER, "MARKETPLACE_FOLDER" }, - { LLAssetType::AT_WIDGET, "WIDGET" }, - { LLAssetType::AT_PERSON, "PERSON" }, - { LLAssetType::AT_MESH, "MESH" }, - { LLAssetType::AT_SETTINGS, "SETTINGS" }, - { LLAssetType::AT_UNKNOWN, "UNKNOWN" } - }; - - asset_type_to_name_t::iterator iter = asset_type_to_name.find(at); - if (iter != asset_type_to_name.end()) - { - return iter->second; - } - - return std::string("UNKNOWN"); -} - -const std::string idToFilepath(const std::string id, LLAssetType::EType at) -{ - /** - * For the moment this is just {UUID}_{ASSET_TYPE}.txt but of - * course, will be greatly expanded upon - */ - std::ostringstream ss; - ss << "00vfs_"; - ss << id; - ss << "_"; - ss << assetTypeToString(at); - ss << ".txt"; - - const std::string filepath = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, ss.str()); - - return filepath; -} - -bool LLVFile::getExists(const LLUUID &file_id, const LLAssetType::EType file_type) -{ - std::string id_str; - file_id.toString(id_str); - const std::string filename = idToFilepath(id_str, file_type); - - std::ifstream file(filename, std::ios::binary); - if (file.is_open()) - { - file.seekg(0, std::ios::end); - return file.tellg() > 0; - } - return false; -} - -BOOL LLVFile::read(U8 *buffer, S32 bytes, BOOL async, F32 priority) -{ - //CP BEGIN - //if (! (mMode & READ)) - //{ - // LL_WARNS() << "Attempt to read from file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << LL_ENDL; - // return FALSE; - //} - - //if (mHandle != LLVFSThread::nullHandle()) - //{ - // LL_WARNS() << "Attempt to read from vfile object " << mFileID << " with pending async operation" << LL_ENDL; - // return FALSE; - //} - //mPriority = priority; - //CP END - - BOOL success = TRUE; - - mReadComplete = FALSE; - - std::string id; - mFileID.toString(id); - const std::string filename = idToFilepath(id, mFileType); - - std::ifstream file(filename, std::ios::binary); - if (file.is_open()) - { - file.seekg(mPosition, std::ios::beg); - - file.read((char*)buffer, bytes); - - if (file) - { - mBytesRead = bytes; - } - else - { - mBytesRead = file.gcount(); - } - - file.close(); - - mPosition += mBytesRead; - if (!mBytesRead) - { - success = FALSE; - } - - mReadComplete = TRUE; - } - - return success; - - // We can't do a read while there are pending async writes - //CP waitForLock(VFSLOCK_APPEND); - - //CP BEGIN - // *FIX: (?) - //if (async) - //{ - // mHandle = sVFSThread->read(mVFS, mFileID, mFileType, buffer, mPosition, bytes, threadPri()); - //} - //else - //{ - // // We can't do a read while there are pending async writes on this file - // mBytesRead = sVFSThread->readImmediate(mVFS, mFileID, mFileType, buffer, mPosition, bytes); - // mPosition += mBytesRead; - // if (! mBytesRead) - // { - // success = FALSE; - // } - //} - - //return success; - - //CP END -} - -//CP BEGIN -////static -//U8* LLVFile::readFile(LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type, S32* bytes_read) -//{ -// U8 *data; -// LLVFile file(vfs, uuid, type, LLVFile::READ); -// S32 file_size = file.getSize(); -// if (file_size == 0) -// { -// // File is empty. -// data = NULL; -// } -// else -// { -// data = (U8*) ll_aligned_malloc<16>(file_size); -// file.read(data, file_size); /* Flawfinder: ignore */ -// -// if (file.getLastBytesRead() != (S32)file_size) -// { -// ll_aligned_free<16>(data); -// data = NULL; -// file_size = 0; -// } -// } -// if (bytes_read) -// { -// *bytes_read = file_size; -// } -// return data; -//} -//CP END - -//CP BEGIN -//void LLVFile::setReadPriority(const F32 priority) -//{ -// mPriority = priority; -// if (mHandle != LLVFSThread::nullHandle()) -// { -// sVFSThread->setPriority(mHandle, threadPri()); -// } -//} -//CP END - -BOOL LLVFile::isReadComplete() -{ - if (mReadComplete) - { - return TRUE; - } - - return FALSE; - - //CP BEGIN - //BOOL res = TRUE; - //if (mHandle != LLVFSThread::nullHandle()) - //{ - // LLVFSThread::Request* req = (LLVFSThread::Request*)sVFSThread->getRequest(mHandle); - // LLVFSThread::status_t status = req->getStatus(); - // if (status == LLVFSThread::STATUS_COMPLETE) - // { - // mBytesRead = req->getBytesRead(); - // mPosition += mBytesRead; - // sVFSThread->completeRequest(mHandle); - // mHandle = LLVFSThread::nullHandle(); - // } - // else - // { - // res = FALSE; - // } - //} - //return res; - //CP END -} - -S32 LLVFile::getLastBytesRead() -{ - return mBytesRead; -} - -BOOL LLVFile::eof() -{ - return mPosition >= getSize(); -} - -BOOL LLVFile::write(const U8 *buffer, S32 bytes) -{ - std::string id_str; - mFileID.toString(id_str); - const std::string filename = idToFilepath(id_str, mFileType); - - BOOL success = FALSE; - - if (mMode == APPEND) - { - std::ofstream ofs(filename, std::ios::app | std::ios::binary); - if (ofs) - { - ofs.write((const char*)buffer, bytes); - - success = TRUE; - } - } - else - { - std::ofstream ofs(filename, std::ios::binary); - if (ofs) - { - ofs.write((const char*)buffer, bytes); - - mPosition += bytes; - - success = TRUE; - } - } - - return success; - - //CP BEGIN - //if (! (mMode & WRITE)) - //{ - // LL_WARNS() << "Attempt to write to file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << LL_ENDL; - //} - //if (mHandle != LLVFSThread::nullHandle()) - //{ - // LL_ERRS() << "Attempt to write to vfile object " << mFileID << " with pending async operation" << LL_ENDL; - // return FALSE; - //} - //BOOL success = TRUE; - // - //// *FIX: allow async writes? potential problem wit mPosition... - //if (mMode == APPEND) // all appends are async (but WRITEs are not) - //{ - // U8* writebuf = new U8[bytes]; - // memcpy(writebuf, buffer, bytes); - // S32 offset = -1; - // mHandle = sVFSThread->write(mVFS, mFileID, mFileType, - // writebuf, offset, bytes, - // LLVFSThread::FLAG_AUTO_COMPLETE | LLVFSThread::FLAG_AUTO_DELETE); - // mHandle = LLVFSThread::nullHandle(); // FLAG_AUTO_COMPLETE means we don't track this - //} - //else - //{ - // // We can't do a write while there are pending reads or writes on this file - // waitForLock(VFSLOCK_READ); - // waitForLock(VFSLOCK_APPEND); - - // S32 pos = (mMode & APPEND) == APPEND ? -1 : mPosition; - - // S32 wrote = sVFSThread->writeImmediate(mVFS, mFileID, mFileType, (U8*)buffer, pos, bytes); - - // mPosition += wrote; - // - // if (wrote < bytes) - // { - // LL_WARNS() << "Tried to write " << bytes << " bytes, actually wrote " << wrote << LL_ENDL; - - // success = FALSE; - // } - //} - //return success; - //CP END -} - -//static -BOOL LLVFile::writeFile(const U8 *buffer, S32 bytes, LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type) -{ - LLVFile file(vfs, uuid, type, LLVFile::WRITE); - file.setMaxSize(bytes); - return file.write(buffer, bytes); -} - -BOOL LLVFile::seek(S32 offset, S32 origin) -{ - //CP BEG - //if (mMode == APPEND) - //{ - // LL_WARNS() << "Attempt to seek on append-only file" << LL_ENDL; - // return FALSE; - //} - //CP END - - if (-1 == origin) - { - origin = mPosition; - } - - S32 new_pos = origin + offset; - - S32 size = getSize(); // Calls waitForLock(VFSLOCK_APPEND) - - if (new_pos > size) - { - LL_WARNS() << "Attempt to seek past end of file" << LL_ENDL; - - mPosition = size; - return FALSE; - } - else if (new_pos < 0) - { - LL_WARNS() << "Attempt to seek past beginning of file" << LL_ENDL; - - mPosition = 0; - return FALSE; - } - - mPosition = new_pos; - return TRUE; -} - -S32 LLVFile::tell() const -{ - return mPosition; -} - -S32 LLVFile::getSize() -{ - std::string id_str; - mFileID.toString(id_str); - const std::string filename = idToFilepath(id_str, mFileType); - - S32 file_size = 0; - std::ifstream file(filename, std::ios::binary); - if (file.is_open()) - { - file.seekg(0, std::ios::end); - file_size = file.tellg(); - } - return file_size; - - //CP BEG - //waitForLock(VFSLOCK_APPEND); - //S32 size = mVFS->getSize(mFileID, mFileType); - - //return size; - //CP END -} - -S32 LLVFile::getMaxSize() -{ - // offer up a huge size since we don't care what the max is - return INT_MAX; - - //CP BEG - //S32 size = mVFS->getMaxSize(mFileID, mFileType); - - //return size; - //CP END -} - -BOOL LLVFile::setMaxSize(S32 size) -{ - // we don't care what the max size is so we do nothing - // and return true to indicate all was okay - return TRUE; - - //CP BEG - //if (! (mMode & WRITE)) - //{ - // LL_WARNS() << "Attempt to change size of file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << LL_ENDL; - - // return FALSE; - //} - - //if (!mVFS->checkAvailable(size)) - //{ - // //LL_RECORD_BLOCK_TIME(FTM_VFILE_WAIT); - // S32 count = 0; - // while (sVFSThread->getPending() > 1000) - // { - // if (count % 100 == 0) - // { - // LL_INFOS() << "VFS catching up... Pending: " << sVFSThread->getPending() << LL_ENDL; - // } - // if (sVFSThread->isPaused()) - // { - // sVFSThread->update(0); - // } - // ms_sleep(10); - // } - //} - //return mVFS->setMaxSize(mFileID, mFileType, size); - //CP END -} - -BOOL LLVFile::rename(const LLUUID &new_id, const LLAssetType::EType new_type) -{ - std::string old_id_str; - mFileID.toString(old_id_str); - const std::string old_filename = idToFilepath(old_id_str, mFileType); - - std::string new_id_str; - new_id.toString(new_id_str); - const std::string new_filename = idToFilepath(new_id_str, new_type); - - if (std::rename(old_filename.c_str(), new_filename.c_str())) - { - // We would like to return FALSE here indicating the operation - // failed but the original code does not and doing so seems to - // break a lot of things so we go with the flow... - //return FALSE; - } - mFileID = new_id; - mFileType = new_type; - - return TRUE; - //CP BEG - //if (! (mMode & WRITE)) - //{ - // LL_WARNS() << "Attempt to rename file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << LL_ENDL; - - // return FALSE; - //} - - //if (mHandle != LLVFSThread::nullHandle()) - //{ - // LL_WARNS() << "Renaming file with pending async read" << LL_ENDL; - //} - - //waitForLock(VFSLOCK_READ); - //waitForLock(VFSLOCK_APPEND); - - //// we need to release / replace our own lock - //// since the renamed file will inherit locks from the new name - //mVFS->decLock(mFileID, mFileType, VFSLOCK_OPEN); - //mVFS->renameFile(mFileID, mFileType, new_id, new_type); - //mVFS->incLock(new_id, new_type, VFSLOCK_OPEN); - // - //mFileID = new_id; - //mFileType = new_type; - - //return TRUE; - //CP END -} - -BOOL LLVFile::remove() -{ - std::string id_str; - mFileID.toString(id_str); - const std::string filename = idToFilepath(id_str, mFileType); - - std::remove(filename.c_str()); - // TODO: check if file was not removed and return false - maybe we don't care? - - return TRUE; - - //CP BEG - // LL_INFOS() << "Removing file " << mFileID << LL_ENDL; - //if (! (mMode & WRITE)) - //{ - // // Leaving paranoia warning just because this should be a very infrequent - // // operation. - // LL_WARNS() << "Remove file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << LL_ENDL; - //} - - //if (mHandle != LLVFSThread::nullHandle()) - //{ - // LL_WARNS() << "Removing file with pending async read" << LL_ENDL; - //} - // - //// why not seek back to the beginning of the file too? - //mPosition = 0; - - //waitForLock(VFSLOCK_READ); - //waitForLock(VFSLOCK_APPEND); - //mVFS->removeFile(mFileID, mFileType); - - //return TRUE; - //CP END -} - -// static -void LLVFile::initClass(LLVFSThread* vfsthread) -{ - //CP BEG - //if (!vfsthread) - //{ - // if (LLVFSThread::sLocal != NULL) - // { - // vfsthread = LLVFSThread::sLocal; - // } - // else - // { - // vfsthread = new LLVFSThread(); - // sAllocdVFSThread = TRUE; - // } - //} - //sVFSThread = vfsthread; - //CP END -} - -// static -void LLVFile::cleanupClass() -{ - //CP BEG - //if (sAllocdVFSThread) - //{ - // delete sVFSThread; - //} - //sVFSThread = NULL; - //CP END -} - -bool LLVFile::isLocked(EVFSLock lock) -{ - // I don't think we care about this test since there is no locking - return FALSE; - - //CP return mVFS->isLocked(mFileID, mFileType, lock) ? true : false; -} - -void LLVFile::waitForLock(EVFSLock lock) -{ - //CP BEG - ////LL_RECORD_BLOCK_TIME(FTM_VFILE_WAIT); - //// spin until the lock clears - //while (isLocked(lock)) - //{ - // if (sVFSThread->isPaused()) - // { - // sVFSThread->update(0); - // } - // ms_sleep(1); - //} - //CP END -} diff --git a/indra/llvfs/llvfs.cpp b/indra/llvfs/llvfs.cpp deleted file mode 100644 index 617056d94d..0000000000 --- a/indra/llvfs/llvfs.cpp +++ /dev/null @@ -1,2220 +0,0 @@ -/** - * @file llvfs.cpp - * @brief Implementation of virtual file system - * - * $LicenseInfo:firstyear=2002&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "linden_common.h" - -#include "llvfs.h" - -#include -#include -#include -#if LL_WINDOWS -#include -#elif LL_SOLARIS -#include -#include -#include -#else -#include -#endif - -#include "llstl.h" -#include "lltimer.h" - -const S32 FILE_BLOCK_MASK = 0x000003FF; // 1024-byte blocks -const S32 VFS_CLEANUP_SIZE = 5242880; // how much space we free up in a single stroke -const S32 BLOCK_LENGTH_INVALID = -1; // mLength for invalid LLVFSFileBlocks - -LLVFS *gVFS = NULL; - -// internal class definitions -class LLVFSBlock -{ -public: - LLVFSBlock() - { - mLocation = 0; - mLength = 0; - } - - LLVFSBlock(U32 loc, S32 size) - { - mLocation = loc; - mLength = size; - } - - static bool locationSortPredicate( - const LLVFSBlock* lhs, - const LLVFSBlock* rhs) - { - return lhs->mLocation < rhs->mLocation; - } - -public: - U32 mLocation; - S32 mLength; // allocated block size -}; - -LLVFSFileSpecifier::LLVFSFileSpecifier() -: mFileID(), - mFileType( LLAssetType::AT_NONE ) -{ -} - -LLVFSFileSpecifier::LLVFSFileSpecifier(const LLUUID &file_id, const LLAssetType::EType file_type) -{ - mFileID = file_id; - mFileType = file_type; -} - -bool LLVFSFileSpecifier::operator<(const LLVFSFileSpecifier &rhs) const -{ - return (mFileID == rhs.mFileID) - ? mFileType < rhs.mFileType - : mFileID < rhs.mFileID; -} - -bool LLVFSFileSpecifier::operator==(const LLVFSFileSpecifier &rhs) const -{ - return (mFileID == rhs.mFileID && - mFileType == rhs.mFileType); -} - - -class LLVFSFileBlock : public LLVFSBlock, public LLVFSFileSpecifier -{ -public: - LLVFSFileBlock() : LLVFSBlock(), LLVFSFileSpecifier() - { - init(); - } - - LLVFSFileBlock(const LLUUID &file_id, LLAssetType::EType file_type, U32 loc = 0, S32 size = 0) - : LLVFSBlock(loc, size), LLVFSFileSpecifier( file_id, file_type ) - { - init(); - } - - void init() - { - mSize = 0; - mIndexLocation = -1; - mAccessTime = (U32)time(NULL); - - for (S32 i = 0; i < (S32)VFSLOCK_COUNT; i++) - { - mLocks[(EVFSLock)i] = 0; - } - } - - #ifdef LL_LITTLE_ENDIAN - inline void swizzleCopy(void *dst, void *src, int size) { memcpy(dst, src, size); /* Flawfinder: ignore */} - - #else - - inline U32 swizzle32(U32 x) - { - return(((x >> 24) & 0x000000FF) | ((x >> 8) & 0x0000FF00) | ((x << 8) & 0x00FF0000) |((x << 24) & 0xFF000000)); - } - - inline U16 swizzle16(U16 x) - { - return( ((x >> 8) & 0x000000FF) | ((x << 8) & 0x0000FF00) ); - } - - inline void swizzleCopy(void *dst, void *src, int size) - { - if(size == 4) - { - ((U32*)dst)[0] = swizzle32(((U32*)src)[0]); - } - else if(size == 2) - { - ((U16*)dst)[0] = swizzle16(((U16*)src)[0]); - } - else - { - // Perhaps this should assert... - memcpy(dst, src, size); /* Flawfinder: ignore */ - } - } - - #endif - - void serialize(U8 *buffer) - { - swizzleCopy(buffer, &mLocation, 4); - buffer += 4; - swizzleCopy(buffer, &mLength, 4); - buffer +=4; - swizzleCopy(buffer, &mAccessTime, 4); - buffer +=4; - memcpy(buffer, &mFileID.mData, 16); /* Flawfinder: ignore */ - buffer += 16; - S16 temp_type = mFileType; - swizzleCopy(buffer, &temp_type, 2); - buffer += 2; - swizzleCopy(buffer, &mSize, 4); - } - - void deserialize(U8 *buffer, const S32 index_loc) - { - mIndexLocation = index_loc; - - swizzleCopy(&mLocation, buffer, 4); - buffer += 4; - swizzleCopy(&mLength, buffer, 4); - buffer += 4; - swizzleCopy(&mAccessTime, buffer, 4); - buffer += 4; - memcpy(&mFileID.mData, buffer, 16); - buffer += 16; - S16 temp_type; - swizzleCopy(&temp_type, buffer, 2); - mFileType = (LLAssetType::EType)temp_type; - buffer += 2; - swizzleCopy(&mSize, buffer, 4); - } - - static BOOL insertLRU(LLVFSFileBlock* const& first, - LLVFSFileBlock* const& second) - { - return (first->mAccessTime == second->mAccessTime) - ? *first < *second - : first->mAccessTime < second->mAccessTime; - } - -public: - S32 mSize; - S32 mIndexLocation; // location of index entry - U32 mAccessTime; - BOOL mLocks[VFSLOCK_COUNT]; // number of outstanding locks of each type - - static const S32 SERIAL_SIZE; -}; - -// Helper structure for doing lru w/ stl... is there a simpler way? -struct LLVFSFileBlock_less -{ - bool operator()(LLVFSFileBlock* const& lhs, LLVFSFileBlock* const& rhs) const - { - return (LLVFSFileBlock::insertLRU(lhs, rhs)) ? true : false; - } -}; - - -const S32 LLVFSFileBlock::SERIAL_SIZE = 34; - - -LLVFS::LLVFS(const std::string& index_filename, const std::string& data_filename, const BOOL read_only, const U32 presize, const BOOL remove_after_crash) -: mRemoveAfterCrash(remove_after_crash), - mDataFP(NULL), - mIndexFP(NULL) -{ - mDataMutex = new LLMutex(); - - S32 i; - for (i = 0; i < VFSLOCK_COUNT; i++) - { - mLockCounts[i] = 0; - } - mValid = VFSVALID_OK; - mReadOnly = read_only; - mIndexFilename = index_filename; - mDataFilename = data_filename; - - const char *file_mode = mReadOnly ? "rb" : "r+b"; - - LL_INFOS("VFS") << "Attempting to open VFS index file " << mIndexFilename << LL_ENDL; - LL_INFOS("VFS") << "Attempting to open VFS data file " << mDataFilename << LL_ENDL; - - mDataFP = openAndLock(mDataFilename, file_mode, mReadOnly); - if (!mDataFP) - { - if (mReadOnly) - { - LL_WARNS("VFS") << "Can't find " << mDataFilename << " to open read-only VFS" << LL_ENDL; - mValid = VFSVALID_BAD_CANNOT_OPEN_READONLY; - return; - } - - mDataFP = openAndLock(mDataFilename, "w+b", FALSE); - if (mDataFP) - { - // Since we're creating this data file, assume any index file is bogus - // remove the index, since this vfs is now blank - LLFile::remove(mIndexFilename); - } - else - { - LL_WARNS("VFS") << "Couldn't open vfs data file " - << mDataFilename << LL_ENDL; - mValid = VFSVALID_BAD_CANNOT_CREATE; - return; - } - - if (presize) - { - presizeDataFile(presize); - } - } - - // Did we leave this file open for writing last time? - // If so, close it and start over. - if (!mReadOnly && mRemoveAfterCrash) - { - llstat marker_info; - std::string marker = mDataFilename + ".open"; - if (!LLFile::stat(marker, &marker_info)) - { - // marker exists, kill the lock and the VFS files - unlockAndClose(mDataFP); - mDataFP = NULL; - - LL_WARNS("VFS") << "VFS: File left open on last run, removing old VFS file " << mDataFilename << LL_ENDL; - LLFile::remove(mIndexFilename); - LLFile::remove(mDataFilename); - LLFile::remove(marker); - - mDataFP = openAndLock(mDataFilename, "w+b", FALSE); - if (!mDataFP) - { - LL_WARNS("VFS") << "Can't open VFS data file in crash recovery" << LL_ENDL; - mValid = VFSVALID_BAD_CANNOT_CREATE; - return; - } - - if (presize) - { - presizeDataFile(presize); - } - } - } - - // determine the real file size - fseek(mDataFP, 0, SEEK_END); - U32 data_size = ftell(mDataFP); - - // read the index file - // make sure there's at least one file in it too - // if not, we'll treat this as a new vfs - llstat fbuf; - if (! LLFile::stat(mIndexFilename, &fbuf) && - fbuf.st_size >= LLVFSFileBlock::SERIAL_SIZE && - (mIndexFP = openAndLock(mIndexFilename, file_mode, mReadOnly)) // Yes, this is an assignment and not '==' - ) - { - std::vector buffer(fbuf.st_size); - size_t buf_offset = 0; - size_t nread = fread(&buffer[0], 1, fbuf.st_size, mIndexFP); - - std::vector files_by_loc; - - while (buf_offset < nread) - { - LLVFSFileBlock *block = new LLVFSFileBlock(); - - block->deserialize(&buffer[buf_offset], (S32)buf_offset); - - // Do sanity check on this block. - // Note that this skips zero size blocks, which helps VFS - // to heal after some errors. JC - if (block->mLength > 0 && - (U32)block->mLength <= data_size && - block->mLocation < data_size && - block->mSize > 0 && - block->mSize <= block->mLength && - block->mFileType >= LLAssetType::AT_NONE && - block->mFileType < LLAssetType::AT_COUNT) - { - mFileBlocks.insert(fileblock_map::value_type(*block, block)); - files_by_loc.push_back(block); - } - else - if (block->mLength && block->mSize > 0) - { - // this is corrupt, not empty - LL_WARNS("VFS") << "VFS corruption: " << block->mFileID << " (" << block->mFileType << ") at index " << block->mIndexLocation << " DS: " << data_size << LL_ENDL; - LL_WARNS("VFS") << "Length: " << block->mLength << "\tLocation: " << block->mLocation << "\tSize: " << block->mSize << LL_ENDL; - LL_WARNS("VFS") << "File has bad data - VFS removed" << LL_ENDL; - - delete block; - - unlockAndClose( mIndexFP ); - mIndexFP = NULL; - LLFile::remove( mIndexFilename ); - - unlockAndClose( mDataFP ); - mDataFP = NULL; - LLFile::remove( mDataFilename ); - - LL_WARNS("VFS") << "Deleted corrupt VFS files " - << mDataFilename - << " and " - << mIndexFilename - << LL_ENDL; - - mValid = VFSVALID_BAD_CORRUPT; - return; - } - else - { - // this is a null or bad entry, skip it - mIndexHoles.push_back(buf_offset); - - delete block; - } - - buf_offset += LLVFSFileBlock::SERIAL_SIZE; - } - - std::sort( - files_by_loc.begin(), - files_by_loc.end(), - LLVFSFileBlock::locationSortPredicate); - - // There are 3 cases that have to be considered. - // 1. No blocks - // 2. One block. - // 3. Two or more blocks. - if (!files_by_loc.empty()) - { - // cur walks through the list. - std::vector::iterator cur = files_by_loc.begin(); - std::vector::iterator end = files_by_loc.end(); - LLVFSFileBlock* last_file_block = *cur; - - // Check to see if there is an empty space before the first file. - if (last_file_block->mLocation > 0) - { - // If so, create a free block. - addFreeBlock(new LLVFSBlock(0, last_file_block->mLocation)); - } - - // Walk through the 2nd+ block. If there is a free space - // between cur_file_block and last_file_block, add it to - // the free space collection. This block will not need to - // run in the case there is only one entry in the VFS. - ++cur; - while( cur != end ) - { - LLVFSFileBlock* cur_file_block = *cur; - - // Dupe check on the block - if (cur_file_block->mLocation == last_file_block->mLocation - && cur_file_block->mLength == last_file_block->mLength) - { - LL_WARNS("VFS") << "VFS: removing duplicate entry" - << " at " << cur_file_block->mLocation - << " length " << cur_file_block->mLength - << " size " << cur_file_block->mSize - << " ID " << cur_file_block->mFileID - << " type " << cur_file_block->mFileType - << LL_ENDL; - - // Duplicate entries. Nuke them both for safety. - mFileBlocks.erase(*cur_file_block); // remove ID/type entry - if (cur_file_block->mLength > 0) - { - // convert to hole - addFreeBlock( - new LLVFSBlock( - cur_file_block->mLocation, - cur_file_block->mLength)); - } - lockData(); // needed for sync() - sync(cur_file_block, TRUE); // remove first on disk - sync(last_file_block, TRUE); // remove last on disk - unlockData(); // needed for sync() - last_file_block = cur_file_block; - ++cur; - continue; - } - - // Figure out where the last block ended. - S32 loc = last_file_block->mLocation+last_file_block->mLength; - - // Figure out how much space there is between where - // the last block ended and this block begins. - S32 length = cur_file_block->mLocation - loc; - - // Check for more errors... Seeing if the current - // entry and the last entry make sense together. - if (length < 0 || loc < 0 || (U32)loc > data_size) - { - // Invalid VFS - unlockAndClose( mIndexFP ); - mIndexFP = NULL; - LLFile::remove( mIndexFilename ); - - unlockAndClose( mDataFP ); - mDataFP = NULL; - LLFile::remove( mDataFilename ); - - LL_WARNS("VFS") << "VFS: overlapping entries" - << " at " << cur_file_block->mLocation - << " length " << cur_file_block->mLength - << " ID " << cur_file_block->mFileID - << " type " << cur_file_block->mFileType - << LL_ENDL; - - LL_WARNS("VFS") << "Deleted corrupt VFS files " - << mDataFilename - << " and " - << mIndexFilename - << LL_ENDL; - - mValid = VFSVALID_BAD_CORRUPT; - return; - } - - // we don't want to add empty blocks to the list... - if (length > 0) - { - addFreeBlock(new LLVFSBlock(loc, length)); - } - last_file_block = cur_file_block; - ++cur; - } - - // also note any empty space at the end - U32 loc = last_file_block->mLocation + last_file_block->mLength; - if (loc < data_size) - { - addFreeBlock(new LLVFSBlock(loc, data_size - loc)); - } - } - else // There where no blocks in the file. - { - addFreeBlock(new LLVFSBlock(0, data_size)); - } - } - else // Pre-existing index file wasn't opened - { - if (mReadOnly) - { - LL_WARNS("VFS") << "Can't find " << mIndexFilename << " to open read-only VFS" << LL_ENDL; - mValid = VFSVALID_BAD_CANNOT_OPEN_READONLY; - return; - } - - - mIndexFP = openAndLock(mIndexFilename, "w+b", FALSE); - if (!mIndexFP) - { - LL_WARNS("VFS") << "Couldn't open an index file for the VFS, probably a sharing violation!" << LL_ENDL; - - unlockAndClose( mDataFP ); - mDataFP = NULL; - LLFile::remove( mDataFilename ); - - mValid = VFSVALID_BAD_CANNOT_CREATE; - return; - } - - // no index file, start from scratch w/ 1GB allocation - LLVFSBlock *first_block = new LLVFSBlock(0, data_size ? data_size : 0x40000000); - addFreeBlock(first_block); - } - - // Open marker file to look for bad shutdowns - if (!mReadOnly && mRemoveAfterCrash) - { - std::string marker = mDataFilename + ".open"; - LLFILE* marker_fp = LLFile::fopen(marker, "w"); /* Flawfinder: ignore */ - if (marker_fp) - { - fclose(marker_fp); - marker_fp = NULL; - } - } - - LL_INFOS("VFS") << "Using VFS index file " << mIndexFilename << LL_ENDL; - LL_INFOS("VFS") << "Using VFS data file " << mDataFilename << LL_ENDL; - - mValid = VFSVALID_OK; -} - -LLVFS::~LLVFS() -{ - if (mDataMutex->isLocked()) - { - LL_ERRS("VFS") << "LLVFS destroyed with mutex locked" << LL_ENDL; - } - - unlockAndClose(mIndexFP); - mIndexFP = NULL; - - fileblock_map::const_iterator it; - for (it = mFileBlocks.begin(); it != mFileBlocks.end(); ++it) - { - delete (*it).second; - } - mFileBlocks.clear(); - - mFreeBlocksByLength.clear(); - - for_each(mFreeBlocksByLocation.begin(), mFreeBlocksByLocation.end(), DeletePairedPointer()); - mFreeBlocksByLocation.clear(); - - unlockAndClose(mDataFP); - mDataFP = NULL; - - // Remove marker file - if (!mReadOnly && mRemoveAfterCrash) - { - std::string marker = mDataFilename + ".open"; - LLFile::remove(marker); - } - - delete mDataMutex; -} - - -// Use this function normally to create LLVFS files. -// Will append digits to the end of the filename with multiple re-trys -// static -LLVFS * LLVFS::createLLVFS(const std::string& index_filename, - const std::string& data_filename, - const BOOL read_only, - const U32 presize, - const BOOL remove_after_crash) -{ - LLVFS * new_vfs = new LLVFS(index_filename, data_filename, read_only, presize, remove_after_crash); - - if( !new_vfs->isValid() ) - { // First name failed, retry with new names - std::string retry_vfs_index_name; - std::string retry_vfs_data_name; - S32 count = 0; - while (!new_vfs->isValid() && - count < 256) - { // Append '.' to end of filenames - retry_vfs_index_name = index_filename + llformat(".%u",count); - retry_vfs_data_name = data_filename + llformat(".%u", count); - - delete new_vfs; // Delete bad VFS and try again - new_vfs = new LLVFS(retry_vfs_index_name, retry_vfs_data_name, read_only, presize, remove_after_crash); - - count++; - } - } - - if( !new_vfs->isValid() ) - { - delete new_vfs; // Delete bad VFS - new_vfs = NULL; // Total failure - } - - return new_vfs; -} - - - -void LLVFS::presizeDataFile(const U32 size) -{ - if (!mDataFP) - { - LL_ERRS() << "LLVFS::presizeDataFile() with no data file open" << LL_ENDL; - return; - } - - // we're creating this file for the first time, size it - fseek(mDataFP, size-1, SEEK_SET); - S32 tmp = 0; - tmp = (S32)fwrite(&tmp, 1, 1, mDataFP); - // fflush(mDataFP); - - // also remove any index, since this vfs is now blank - LLFile::remove(mIndexFilename); - - if (tmp) - { - LL_INFOS() << "Pre-sized VFS data file to " << ftell(mDataFP) << " bytes" << LL_ENDL; - } - else - { - LL_WARNS() << "Failed to pre-size VFS data file" << LL_ENDL; - } -} - -BOOL LLVFS::getExists(const LLUUID &file_id, const LLAssetType::EType file_type) -{ - LLVFSFileBlock *block = NULL; - - if (!isValid()) - { - LL_ERRS() << "Attempting to use invalid VFS!" << LL_ENDL; - } - - lockData(); - - LLVFSFileSpecifier spec(file_id, file_type); - fileblock_map::iterator it = mFileBlocks.find(spec); - if (it != mFileBlocks.end()) - { - block = (*it).second; - block->mAccessTime = (U32)time(NULL); - } - - BOOL res = (block && block->mLength > 0) ? TRUE : FALSE; - - unlockData(); - - return res; -} - -S32 LLVFS::getSize(const LLUUID &file_id, const LLAssetType::EType file_type) -{ - S32 size = 0; - - if (!isValid()) - { - LL_ERRS() << "Attempting to use invalid VFS!" << LL_ENDL; - - } - - lockData(); - - LLVFSFileSpecifier spec(file_id, file_type); - fileblock_map::iterator it = mFileBlocks.find(spec); - if (it != mFileBlocks.end()) - { - LLVFSFileBlock *block = (*it).second; - - block->mAccessTime = (U32)time(NULL); - size = block->mSize; - } - - unlockData(); - - return size; -} - -S32 LLVFS::getMaxSize(const LLUUID &file_id, const LLAssetType::EType file_type) -{ - S32 size = 0; - - if (!isValid()) - { - LL_ERRS() << "Attempting to use invalid VFS!" << LL_ENDL; - } - - lockData(); - - LLVFSFileSpecifier spec(file_id, file_type); - fileblock_map::iterator it = mFileBlocks.find(spec); - if (it != mFileBlocks.end()) - { - LLVFSFileBlock *block = (*it).second; - - block->mAccessTime = (U32)time(NULL); - size = block->mLength; - } - - unlockData(); - - return size; -} - -BOOL LLVFS::checkAvailable(S32 max_size) -{ - lockData(); - - blocks_length_map_t::iterator iter = mFreeBlocksByLength.lower_bound(max_size); // first entry >= size - const BOOL res(iter == mFreeBlocksByLength.end() ? FALSE : TRUE); - - unlockData(); - - return res; -} - -BOOL LLVFS::setMaxSize(const LLUUID &file_id, const LLAssetType::EType file_type, S32 max_size) -{ - if (!isValid()) - { - LL_ERRS() << "Attempting to use invalid VFS!" << LL_ENDL; - } - if (mReadOnly) - { - LL_ERRS() << "Attempt to write to read-only VFS" << LL_ENDL; - } - if (max_size <= 0) - { - LL_WARNS() << "VFS: Attempt to assign size " << max_size << " to vfile " << file_id << LL_ENDL; - return FALSE; - } - - lockData(); - - LLVFSFileSpecifier spec(file_id, file_type); - LLVFSFileBlock *block = NULL; - fileblock_map::iterator it = mFileBlocks.find(spec); - if (it != mFileBlocks.end()) - { - block = (*it).second; - } - - // round all sizes upward to KB increments - // SJB: Need to not round for the new texture-pipeline code so we know the correct - // max file size. Need to investigate the potential problems with this... - if (file_type != LLAssetType::AT_TEXTURE) - { - if (max_size & FILE_BLOCK_MASK) - { - max_size += FILE_BLOCK_MASK; - max_size &= ~FILE_BLOCK_MASK; - } - } - - if (block && block->mLength > 0) - { - block->mAccessTime = (U32)time(NULL); - - if (max_size == block->mLength) - { - unlockData(); - return TRUE; - } - else if (max_size < block->mLength) - { - // this file is shrinking - LLVFSBlock *free_block = new LLVFSBlock(block->mLocation + max_size, block->mLength - max_size); - - addFreeBlock(free_block); - - block->mLength = max_size; - - if (block->mLength < block->mSize) - { - // JC: Was a warning, but Ian says it's bad. - LL_ERRS() << "Truncating virtual file " << file_id << " to " << block->mLength << " bytes" << LL_ENDL; - block->mSize = block->mLength; - } - - sync(block); - //mergeFreeBlocks(); - - unlockData(); - return TRUE; - } - else if (max_size > block->mLength) - { - // this file is growing - // first check for an adjacent free block to grow into - S32 size_increase = max_size - block->mLength; - - // Find the first free block with and addres > block->mLocation - LLVFSBlock *free_block; - blocks_location_map_t::iterator iter = mFreeBlocksByLocation.upper_bound(block->mLocation); - if (iter != mFreeBlocksByLocation.end()) - { - free_block = iter->second; - - if (free_block->mLocation == block->mLocation + block->mLength && - free_block->mLength >= size_increase) - { - // this free block is at the end of the file and is large enough - - // Must call useFreeSpace before sync(), as sync() - // unlocks data structures. - useFreeSpace(free_block, size_increase); - block->mLength += size_increase; - sync(block); - - unlockData(); - return TRUE; - } - } - - // no adjecent free block, find one in the list - free_block = findFreeBlock(max_size, block); - - if (free_block) - { - // Save location where data is going, useFreeSpace will move free_block->mLocation; - U32 new_data_location = free_block->mLocation; - - //mark the free block as used so it does not - //interfere with other operations such as addFreeBlock - useFreeSpace(free_block, max_size); // useFreeSpace takes ownership (and may delete) free_block - - if (block->mLength > 0) - { - // create a new free block where this file used to be - LLVFSBlock *new_free_block = new LLVFSBlock(block->mLocation, block->mLength); - - addFreeBlock(new_free_block); - - if (block->mSize > 0) - { - // move the file into the new block - std::vector buffer(block->mSize); - fseek(mDataFP, block->mLocation, SEEK_SET); - if (fread(&buffer[0], block->mSize, 1, mDataFP) == 1) - { - fseek(mDataFP, new_data_location, SEEK_SET); - if (fwrite(&buffer[0], block->mSize, 1, mDataFP) != 1) - { - LL_WARNS() << "Short write" << LL_ENDL; - } - } else { - LL_WARNS() << "Short read" << LL_ENDL; - } - } - } - - block->mLocation = new_data_location; - - block->mLength = max_size; - - - sync(block); - - unlockData(); - return TRUE; - } - else - { - LL_WARNS() << "VFS: No space (" << max_size << ") to resize existing vfile " << file_id << LL_ENDL; - //dumpMap(); - unlockData(); - dumpStatistics(); - return FALSE; - } - } - } - else - { - // find a free block in the list - LLVFSBlock *free_block = findFreeBlock(max_size); - - if (free_block) - { - if (block) - { - block->mLocation = free_block->mLocation; - block->mLength = max_size; - } - else - { - // this file doesn't exist, create it - block = new LLVFSFileBlock(file_id, file_type, free_block->mLocation, max_size); - mFileBlocks.insert(fileblock_map::value_type(spec, block)); - } - - // Must call useFreeSpace before sync(), as sync() - // unlocks data structures. - useFreeSpace(free_block, max_size); - block->mAccessTime = (U32)time(NULL); - - sync(block); - } - else - { - LL_WARNS() << "VFS: No space (" << max_size << ") for new virtual file " << file_id << LL_ENDL; - //dumpMap(); - unlockData(); - dumpStatistics(); - return FALSE; - } - } - unlockData(); - return TRUE; -} - - -// WARNING: HERE BE DRAGONS! -// rename is the weirdest VFS op, because the file moves but the locks don't! -void LLVFS::renameFile(const LLUUID &file_id, const LLAssetType::EType file_type, - const LLUUID &new_id, const LLAssetType::EType &new_type) -{ - if (!isValid()) - { - LL_ERRS() << "Attempting to use invalid VFS!" << LL_ENDL; - } - if (mReadOnly) - { - LL_ERRS() << "Attempt to write to read-only VFS" << LL_ENDL; - } - - lockData(); - - LLVFSFileSpecifier new_spec(new_id, new_type); - LLVFSFileSpecifier old_spec(file_id, file_type); - - fileblock_map::iterator it = mFileBlocks.find(old_spec); - if (it != mFileBlocks.end()) - { - LLVFSFileBlock *src_block = (*it).second; - - // this will purge the data but leave the file block in place, w/ locks, if any - // WAS: removeFile(new_id, new_type); NOW uses removeFileBlock() to avoid mutex lock recursion - fileblock_map::iterator new_it = mFileBlocks.find(new_spec); - if (new_it != mFileBlocks.end()) - { - LLVFSFileBlock *new_block = (*new_it).second; - removeFileBlock(new_block); - } - - // if there's something in the target location, remove it but inherit its locks - it = mFileBlocks.find(new_spec); - if (it != mFileBlocks.end()) - { - LLVFSFileBlock *dest_block = (*it).second; - - for (S32 i = 0; i < (S32)VFSLOCK_COUNT; i++) - { - if(dest_block->mLocks[i]) - { - LL_ERRS() << "Renaming VFS block to a locked file." << LL_ENDL; - } - dest_block->mLocks[i] = src_block->mLocks[i]; - } - - mFileBlocks.erase(new_spec); - delete dest_block; - } - - src_block->mFileID = new_id; - src_block->mFileType = new_type; - src_block->mAccessTime = (U32)time(NULL); - - mFileBlocks.erase(old_spec); - mFileBlocks.insert(fileblock_map::value_type(new_spec, src_block)); - - sync(src_block); - } - else - { - LL_WARNS() << "VFS: Attempt to rename nonexistent vfile " << file_id << ":" << file_type << LL_ENDL; - } - unlockData(); -} - -// mDataMutex must be LOCKED before calling this -void LLVFS::removeFileBlock(LLVFSFileBlock *fileblock) -{ - // convert this into an unsaved, dummy fileblock to preserve locks - // a more rubust solution would store the locks in a seperate data structure - sync(fileblock, TRUE); - - if (fileblock->mLength > 0) - { - // turn this file into an empty block - LLVFSBlock *free_block = new LLVFSBlock(fileblock->mLocation, fileblock->mLength); - - addFreeBlock(free_block); - } - - fileblock->mLocation = 0; - fileblock->mSize = 0; - fileblock->mLength = BLOCK_LENGTH_INVALID; - fileblock->mIndexLocation = -1; - - //mergeFreeBlocks(); -} - -void LLVFS::removeFile(const LLUUID &file_id, const LLAssetType::EType file_type) -{ - if (!isValid()) - { - LL_ERRS() << "Attempting to use invalid VFS!" << LL_ENDL; - } - if (mReadOnly) - { - LL_ERRS() << "Attempt to write to read-only VFS" << LL_ENDL; - } - - lockData(); - - LLVFSFileSpecifier spec(file_id, file_type); - fileblock_map::iterator it = mFileBlocks.find(spec); - if (it != mFileBlocks.end()) - { - LLVFSFileBlock *block = (*it).second; - removeFileBlock(block); - } - else - { - LL_WARNS() << "VFS: attempting to remove nonexistent file " << file_id << " type " << file_type << LL_ENDL; - } - - unlockData(); -} - - -S32 LLVFS::getData(const LLUUID &file_id, const LLAssetType::EType file_type, U8 *buffer, S32 location, S32 length) -{ - S32 bytesread = 0; - - if (!isValid()) - { - LL_ERRS() << "Attempting to use invalid VFS!" << LL_ENDL; - } - llassert(location >= 0); - llassert(length >= 0); - - BOOL do_read = FALSE; - - lockData(); - - LLVFSFileSpecifier spec(file_id, file_type); - fileblock_map::iterator it = mFileBlocks.find(spec); - if (it != mFileBlocks.end()) - { - LLVFSFileBlock *block = (*it).second; - - block->mAccessTime = (U32)time(NULL); - - if (location > block->mSize) - { - LL_WARNS() << "VFS: Attempt to read location " << location << " in file " << file_id << " of length " << block->mSize << LL_ENDL; - } - else - { - if (length > block->mSize - location) - { - length = block->mSize - location; - } - location += block->mLocation; - do_read = TRUE; - } - } - - if (do_read) - { - fseek(mDataFP, location, SEEK_SET); - bytesread = (S32)fread(buffer, 1, length, mDataFP); - } - - unlockData(); - - return bytesread; -} - -S32 LLVFS::storeData(const LLUUID &file_id, const LLAssetType::EType file_type, const U8 *buffer, S32 location, S32 length) -{ - if (!isValid()) - { - LL_ERRS() << "Attempting to use invalid VFS!" << LL_ENDL; - } - if (mReadOnly) - { - LL_ERRS() << "Attempt to write to read-only VFS" << LL_ENDL; - } - - llassert(length > 0); - - lockData(); - - LLVFSFileSpecifier spec(file_id, file_type); - fileblock_map::iterator it = mFileBlocks.find(spec); - if (it != mFileBlocks.end()) - { - LLVFSFileBlock *block = (*it).second; - - S32 in_loc = location; - if (location == -1) - { - location = block->mSize; - } - llassert(location >= 0); - - block->mAccessTime = (U32)time(NULL); - - if (block->mLength == BLOCK_LENGTH_INVALID) - { - // Block was removed, ignore write - LL_WARNS() << "VFS: Attempt to write to invalid block" - << " in file " << file_id - << " location: " << in_loc - << " bytes: " << length - << LL_ENDL; - unlockData(); - return length; - } - else if (location > block->mLength) - { - LL_WARNS() << "VFS: Attempt to write to location " << location - << " in file " << file_id - << " type " << S32(file_type) - << " of size " << block->mSize - << " block length " << block->mLength - << LL_ENDL; - unlockData(); - return length; - } - else - { - if (length > block->mLength - location ) - { - LL_WARNS() << "VFS: Truncating write to virtual file " << file_id << " type " << S32(file_type) << LL_ENDL; - length = block->mLength - location; - } - U32 file_location = location + block->mLocation; - - fseek(mDataFP, file_location, SEEK_SET); - S32 write_len = (S32)fwrite(buffer, 1, length, mDataFP); - if (write_len != length) - { - LL_WARNS() << llformat("VFS Write Error: %d != %d",write_len,length) << LL_ENDL; - } - // fflush(mDataFP); - - if (location + length > block->mSize) - { - block->mSize = location + write_len; - sync(block); - } - unlockData(); - - return write_len; - } - } - else - { - unlockData(); - return 0; - } -} - -void LLVFS::incLock(const LLUUID &file_id, const LLAssetType::EType file_type, EVFSLock lock) -{ - lockData(); - - LLVFSFileSpecifier spec(file_id, file_type); - LLVFSFileBlock *block; - - fileblock_map::iterator it = mFileBlocks.find(spec); - if (it != mFileBlocks.end()) - { - block = (*it).second; - } - else - { - // Create a dummy block which isn't saved - block = new LLVFSFileBlock(file_id, file_type, 0, BLOCK_LENGTH_INVALID); - block->mAccessTime = (U32)time(NULL); - mFileBlocks.insert(fileblock_map::value_type(spec, block)); - } - - block->mLocks[lock]++; - mLockCounts[lock]++; - - unlockData(); -} - -void LLVFS::decLock(const LLUUID &file_id, const LLAssetType::EType file_type, EVFSLock lock) -{ - lockData(); - - LLVFSFileSpecifier spec(file_id, file_type); - fileblock_map::iterator it = mFileBlocks.find(spec); - if (it != mFileBlocks.end()) - { - LLVFSFileBlock *block = (*it).second; - - if (block->mLocks[lock] > 0) - { - block->mLocks[lock]--; - } - else - { - LL_WARNS() << "VFS: Decrementing zero-value lock " << lock << LL_ENDL; - } - mLockCounts[lock]--; - } - - unlockData(); -} - -BOOL LLVFS::isLocked(const LLUUID &file_id, const LLAssetType::EType file_type, EVFSLock lock) -{ - lockData(); - - BOOL res = FALSE; - - LLVFSFileSpecifier spec(file_id, file_type); - fileblock_map::iterator it = mFileBlocks.find(spec); - if (it != mFileBlocks.end()) - { - LLVFSFileBlock *block = (*it).second; - res = (block->mLocks[lock] > 0); - } - - unlockData(); - - return res; -} - -//============================================================================ -// protected -//============================================================================ - -void LLVFS::eraseBlockLength(LLVFSBlock *block) -{ - // find the corresponding map entry in the length map and erase it - S32 length = block->mLength; - blocks_length_map_t::iterator iter = mFreeBlocksByLength.lower_bound(length); - blocks_length_map_t::iterator end = mFreeBlocksByLength.end(); - bool found_block = false; - while(iter != end) - { - LLVFSBlock *tblock = iter->second; - llassert(tblock->mLength == length); // there had -better- be an entry with our length! - if (tblock == block) - { - mFreeBlocksByLength.erase(iter); - found_block = true; - break; - } - ++iter; - } - if(!found_block) - { - LL_ERRS() << "eraseBlock could not find block" << LL_ENDL; - } -} - - -// Remove block from both free lists (by location and by length). -void LLVFS::eraseBlock(LLVFSBlock *block) -{ - eraseBlockLength(block); - // find the corresponding map entry in the location map and erase it - U32 location = block->mLocation; - llverify(mFreeBlocksByLocation.erase(location) == 1); // we should only have one entry per location. -} - - -// Add the region specified by block location and length to the free lists. -// Also incrementally defragment by merging with previous and next free blocks. -void LLVFS::addFreeBlock(LLVFSBlock *block) -{ -#if LL_DEBUG - size_t dbgcount = mFreeBlocksByLocation.count(block->mLocation); - if(dbgcount > 0) - { - LL_ERRS() << "addFreeBlock called with block already in list" << LL_ENDL; - } -#endif - - // Get a pointer to the next free block (by location). - blocks_location_map_t::iterator next_free_it = mFreeBlocksByLocation.lower_bound(block->mLocation); - - // We can merge with previous if it ends at our requested location. - LLVFSBlock* prev_block = NULL; - bool merge_prev = false; - if (next_free_it != mFreeBlocksByLocation.begin()) - { - blocks_location_map_t::iterator prev_free_it = next_free_it; - --prev_free_it; - prev_block = prev_free_it->second; - merge_prev = (prev_block->mLocation + prev_block->mLength == block->mLocation); - } - - // We can merge with next if our block ends at the next block's location. - LLVFSBlock* next_block = NULL; - bool merge_next = false; - if (next_free_it != mFreeBlocksByLocation.end()) - { - next_block = next_free_it->second; - merge_next = (block->mLocation + block->mLength == next_block->mLocation); - } - - if (merge_prev && merge_next) - { - // LL_INFOS() << "VFS merge BOTH" << LL_ENDL; - // Previous block is changing length (a lot), so only need to update length map. - // Next block is going away completely. JC - eraseBlockLength(prev_block); - eraseBlock(next_block); - prev_block->mLength += block->mLength + next_block->mLength; - mFreeBlocksByLength.insert(blocks_length_map_t::value_type(prev_block->mLength, prev_block)); - delete block; - block = NULL; - delete next_block; - next_block = NULL; - } - else if (merge_prev) - { - // LL_INFOS() << "VFS merge previous" << LL_ENDL; - // Previous block is maintaining location, only changing length, - // therefore only need to update the length map. JC - eraseBlockLength(prev_block); - prev_block->mLength += block->mLength; - mFreeBlocksByLength.insert(blocks_length_map_t::value_type(prev_block->mLength, prev_block)); // multimap insert - delete block; - block = NULL; - } - else if (merge_next) - { - // LL_INFOS() << "VFS merge next" << LL_ENDL; - // Next block is changing both location and length, - // so both free lists must update. JC - eraseBlock(next_block); - next_block->mLocation = block->mLocation; - next_block->mLength += block->mLength; - // Don't hint here, next_free_it iterator may be invalid. - mFreeBlocksByLocation.insert(blocks_location_map_t::value_type(next_block->mLocation, next_block)); // multimap insert - mFreeBlocksByLength.insert(blocks_length_map_t::value_type(next_block->mLength, next_block)); // multimap insert - delete block; - block = NULL; - } - else - { - // Can't merge with other free blocks. - // Hint that insert should go near next_free_it. - mFreeBlocksByLocation.insert(next_free_it, blocks_location_map_t::value_type(block->mLocation, block)); // multimap insert - mFreeBlocksByLength.insert(blocks_length_map_t::value_type(block->mLength, block)); // multimap insert - } -} - -// Superceeded by new addFreeBlock which does incremental free space merging. -// Incremental is faster overall. -//void LLVFS::mergeFreeBlocks() -//{ -// if (!isValid()) -// { -// LL_ERRS() << "Attempting to use invalid VFS!" << LL_ENDL; -// } -// // TODO: could we optimize this with hints from the calling code? -// blocks_location_map_t::iterator iter = mFreeBlocksByLocation.begin(); -// blocks_location_map_t::iterator end = mFreeBlocksByLocation.end(); -// LLVFSBlock *first_block = iter->second; -// while(iter != end) -// { -// blocks_location_map_t::iterator first_iter = iter; // save for if we do a merge -// if (++iter == end) -// break; -// LLVFSBlock *second_block = iter->second; -// if (first_block->mLocation + first_block->mLength == second_block->mLocation) -// { -// // remove the first block from the length map -// eraseBlockLength(first_block); -// // merge first_block with second_block, since they're adjacent -// first_block->mLength += second_block->mLength; -// // add the first block to the length map (with the new size) -// mFreeBlocksByLength.insert(blocks_length_map_t::value_type(first_block->mLength, first_block)); // multimap insert -// -// // erase and delete the second block -// eraseBlock(second_block); -// delete second_block; -// -// // reset iterator -// iter = first_iter; // haven't changed first_block, so corresponding iterator is still valid -// end = mFreeBlocksByLocation.end(); -// } -// first_block = second_block; -// } -//} - -// length bytes from free_block are going to be used (so they are no longer free) -void LLVFS::useFreeSpace(LLVFSBlock *free_block, S32 length) -{ - if (free_block->mLength == length) - { - eraseBlock(free_block); - delete free_block; - } - else - { - eraseBlock(free_block); - - free_block->mLocation += length; - free_block->mLength -= length; - - addFreeBlock(free_block); - } -} - -// NOTE! mDataMutex must be LOCKED before calling this -// sync this index entry out to the index file -// we need to do this constantly to avoid corruption on viewer crash -void LLVFS::sync(LLVFSFileBlock *block, BOOL remove) -{ - if (!isValid()) - { - LL_ERRS() << "Attempting to use invalid VFS!" << LL_ENDL; - } - if (mReadOnly) - { - LL_WARNS() << "Attempt to sync read-only VFS" << LL_ENDL; - return; - } - if (block->mLength == BLOCK_LENGTH_INVALID) - { - // This is a dummy file, don't save - return; - } - if (block->mLength == 0) - { - LL_ERRS() << "VFS syncing zero-length block" << LL_ENDL; - } - - BOOL set_index_to_end = FALSE; - long seek_pos = block->mIndexLocation; - - if (-1 == seek_pos) - { - if (!mIndexHoles.empty()) - { - seek_pos = mIndexHoles.front(); - mIndexHoles.pop_front(); - } - else - { - set_index_to_end = TRUE; - } - } - - if (set_index_to_end) - { - // Need fseek/ftell to update the seek_pos and hence data - // structures, so can't unlockData() before this. - fseek(mIndexFP, 0, SEEK_END); - seek_pos = ftell(mIndexFP); - } - - block->mIndexLocation = seek_pos; - if (remove) - { - mIndexHoles.push_back(seek_pos); - } - - U8 buffer[LLVFSFileBlock::SERIAL_SIZE]; - if (remove) - { - memset(buffer, 0, LLVFSFileBlock::SERIAL_SIZE); - } - else - { - block->serialize(buffer); - } - - // If set_index_to_end, file pointer is already at seek_pos - // and we don't need to do anything. Only seek if not at end. - if (!set_index_to_end) - { - fseek(mIndexFP, seek_pos, SEEK_SET); - } - - if (fwrite(buffer, LLVFSFileBlock::SERIAL_SIZE, 1, mIndexFP) != 1) - { - LL_WARNS() << "Short write" << LL_ENDL; - } - - // *NOTE: Why was this commented out? - // fflush(mIndexFP); - - return; -} - -// mDataMutex must be LOCKED before calling this -// Can initiate LRU-based file removal to make space. -// The immune file block will not be removed. -LLVFSBlock *LLVFS::findFreeBlock(S32 size, LLVFSFileBlock *immune) -{ - if (!isValid()) - { - LL_ERRS() << "Attempting to use invalid VFS!" << LL_ENDL; - } - - LLVFSBlock *block = NULL; - BOOL have_lru_list = FALSE; - - typedef std::set lru_set; - lru_set lru_list; - - LLTimer timer; - - while (! block) - { - // look for a suitable free block - blocks_length_map_t::iterator iter = mFreeBlocksByLength.lower_bound(size); // first entry >= size - if (iter != mFreeBlocksByLength.end()) - block = iter->second; - - // no large enough free blocks, time to clean out some junk - if (! block) - { - // create a list of files sorted by usage time - // this is far faster than sorting a linked list - if (! have_lru_list) - { - for (fileblock_map::iterator it = mFileBlocks.begin(); it != mFileBlocks.end(); ++it) - { - LLVFSFileBlock *tmp = (*it).second; - - if (tmp != immune && - tmp->mLength > 0 && - ! tmp->mLocks[VFSLOCK_READ] && - ! tmp->mLocks[VFSLOCK_APPEND] && - ! tmp->mLocks[VFSLOCK_OPEN]) - { - lru_list.insert(tmp); - } - } - - have_lru_list = TRUE; - } - - if (lru_list.size() == 0) - { - // No more files to delete, and still not enough room! - LL_WARNS() << "VFS: Can't make " << size << " bytes of free space in VFS, giving up" << LL_ENDL; - break; - } - - // is the oldest file big enough? (Should be about half the time) - lru_set::iterator it = lru_list.begin(); - LLVFSFileBlock *file_block = *it; - if (file_block->mLength >= size && file_block != immune) - { - // ditch this file and look again for a free block - should find it - // TODO: it'll be faster just to assign the free block and break - LL_INFOS() << "LRU: Removing " << file_block->mFileID << ":" << file_block->mFileType << LL_ENDL; - lru_list.erase(it); - removeFileBlock(file_block); - file_block = NULL; - continue; - } - - - LL_INFOS() << "VFS: LRU: Aggressive: " << (S32)lru_list.size() << " files remain" << LL_ENDL; - dumpLockCounts(); - - // Now it's time to aggressively make more space - // Delete the oldest 5MB of the vfs or enough to hold the file, which ever is larger - // This may yield too much free space, but we'll use it up soon enough - U32 cleanup_target = (size > VFS_CLEANUP_SIZE) ? size : VFS_CLEANUP_SIZE; - U32 cleaned_up = 0; - for (it = lru_list.begin(); - it != lru_list.end() && cleaned_up < cleanup_target; - ) - { - file_block = *it; - - // TODO: it would be great to be able to batch all these sync() calls - // LL_INFOS() << "LRU2: Removing " << file_block->mFileID << ":" << file_block->mFileType << " last accessed" << file_block->mAccessTime << LL_ENDL; - - cleaned_up += file_block->mLength; - lru_list.erase(it++); - removeFileBlock(file_block); - file_block = NULL; - } - //mergeFreeBlocks(); - } - } - - F32 time = timer.getElapsedTimeF32(); - if (time > 0.5f) - { - LL_WARNS() << "VFS: Spent " << time << " seconds in findFreeBlock!" << LL_ENDL; - } - - return block; -} - -//============================================================================ -// public -//============================================================================ - -void LLVFS::pokeFiles() -{ - if (!isValid()) - { - LL_ERRS() << "Attempting to use invalid VFS!" << LL_ENDL; - } - U32 word; - - // only write data if we actually read 4 bytes - // otherwise we're writing garbage and screwing up the file - fseek(mDataFP, 0, SEEK_SET); - if (fread(&word, sizeof(word), 1, mDataFP) == 1) - { - fseek(mDataFP, 0, SEEK_SET); - if (fwrite(&word, sizeof(word), 1, mDataFP) != 1) - { - LL_WARNS() << "Could not write to data file" << LL_ENDL; - } - fflush(mDataFP); - } - - fseek(mIndexFP, 0, SEEK_SET); - if (fread(&word, sizeof(word), 1, mIndexFP) == 1) - { - fseek(mIndexFP, 0, SEEK_SET); - if (fwrite(&word, sizeof(word), 1, mIndexFP) != 1) - { - LL_WARNS() << "Could not write to index file" << LL_ENDL; - } - fflush(mIndexFP); - } -} - - -void LLVFS::dumpMap() -{ - LL_INFOS() << "Files:" << LL_ENDL; - for (fileblock_map::iterator it = mFileBlocks.begin(); it != mFileBlocks.end(); ++it) - { - LLVFSFileBlock *file_block = (*it).second; - LL_INFOS() << "Location: " << file_block->mLocation << "\tLength: " << file_block->mLength << "\t" << file_block->mFileID << "\t" << file_block->mFileType << LL_ENDL; - } - - LL_INFOS() << "Free Blocks:" << LL_ENDL; - for (blocks_location_map_t::iterator iter = mFreeBlocksByLocation.begin(), - end = mFreeBlocksByLocation.end(); - iter != end; iter++) - { - LLVFSBlock *free_block = iter->second; - LL_INFOS() << "Location: " << free_block->mLocation << "\tLength: " << free_block->mLength << LL_ENDL; - } -} - -// verify that the index file contents match the in-memory file structure -// Very slow, do not call routinely. JC -void LLVFS::audit() -{ - // Lock the mutex through this whole function. - LLMutexLock lock_data(mDataMutex); - - fflush(mIndexFP); - - fseek(mIndexFP, 0, SEEK_END); - size_t index_size = ftell(mIndexFP); - fseek(mIndexFP, 0, SEEK_SET); - - BOOL vfs_corrupt = FALSE; - - // since we take the address of element 0, we need to have at least one element. - std::vector buffer(llmax(index_size,1U)); - - if (fread(&buffer[0], 1, index_size, mIndexFP) != index_size) - { - LL_WARNS() << "Index truncated" << LL_ENDL; - vfs_corrupt = TRUE; - } - - size_t buf_offset = 0; - - std::map found_files; - U32 cur_time = (U32)time(NULL); - - std::vector audit_blocks; - while (!vfs_corrupt && buf_offset < index_size) - { - LLVFSFileBlock *block = new LLVFSFileBlock(); - audit_blocks.push_back(block); - - block->deserialize(&buffer[buf_offset], (S32)buf_offset); - buf_offset += block->SERIAL_SIZE; - - // do sanity check on this block - if (block->mLength >= 0 && - block->mSize >= 0 && - block->mSize <= block->mLength && - block->mFileType >= LLAssetType::AT_NONE && - block->mFileType < LLAssetType::AT_COUNT && - block->mAccessTime <= cur_time && - block->mFileID != LLUUID::null) - { - if (mFileBlocks.find(*block) == mFileBlocks.end()) - { - LL_WARNS() << "VFile " << block->mFileID << ":" << block->mFileType << " on disk, not in memory, loc " << block->mIndexLocation << LL_ENDL; - } - else if (found_files.find(*block) != found_files.end()) - { - std::map::iterator it; - it = found_files.find(*block); - LLVFSFileBlock* dupe = it->second; - // try to keep data from being lost - unlockAndClose(mIndexFP); - mIndexFP = NULL; - unlockAndClose(mDataFP); - mDataFP = NULL; - LL_WARNS() << "VFS: Original block index " << block->mIndexLocation - << " location " << block->mLocation - << " length " << block->mLength - << " size " << block->mSize - << " id " << block->mFileID - << " type " << block->mFileType - << LL_ENDL; - LL_WARNS() << "VFS: Duplicate block index " << dupe->mIndexLocation - << " location " << dupe->mLocation - << " length " << dupe->mLength - << " size " << dupe->mSize - << " id " << dupe->mFileID - << " type " << dupe->mFileType - << LL_ENDL; - LL_WARNS() << "VFS: Index size " << index_size << LL_ENDL; - LL_WARNS() << "VFS: INDEX CORRUPT" << LL_ENDL; - vfs_corrupt = TRUE; - break; - } - else - { - found_files[*block] = block; - } - } - else - { - if (block->mLength) - { - LL_WARNS() << "VFile " << block->mFileID << ":" << block->mFileType << " corrupt on disk" << LL_ENDL; - } - // else this is just a hole - } - } - - if (!vfs_corrupt) - { - for (fileblock_map::iterator it = mFileBlocks.begin(); it != mFileBlocks.end(); ++it) - { - LLVFSFileBlock* block = (*it).second; - - if (block->mSize > 0) - { - if (! found_files.count(*block)) - { - LL_WARNS() << "VFile " << block->mFileID << ":" << block->mFileType << " in memory, not on disk, loc " << block->mIndexLocation<< LL_ENDL; - fseek(mIndexFP, block->mIndexLocation, SEEK_SET); - U8 buf[LLVFSFileBlock::SERIAL_SIZE]; - if (fread(buf, LLVFSFileBlock::SERIAL_SIZE, 1, mIndexFP) != 1) - { - LL_WARNS() << "VFile " << block->mFileID - << " gave short read" << LL_ENDL; - } - - LLVFSFileBlock disk_block; - disk_block.deserialize(buf, block->mIndexLocation); - - LL_WARNS() << "Instead found " << disk_block.mFileID << ":" << block->mFileType << LL_ENDL; - } - else - { - block = found_files.find(*block)->second; - found_files.erase(*block); - } - } - } - - for (std::map::iterator iter = found_files.begin(); - iter != found_files.end(); iter++) - { - LLVFSFileBlock* block = iter->second; - LL_WARNS() << "VFile " << block->mFileID << ":" << block->mFileType << " szie:" << block->mSize << " leftover" << LL_ENDL; - } - - LL_INFOS() << "VFS: audit OK" << LL_ENDL; - // mutex released by LLMutexLock() destructor. - } - - for_each(audit_blocks.begin(), audit_blocks.end(), DeletePointer()); - audit_blocks.clear(); -} - - -// quick check for uninitialized blocks -// Slow, do not call in release. -void LLVFS::checkMem() -{ - lockData(); - - for (fileblock_map::iterator it = mFileBlocks.begin(); it != mFileBlocks.end(); ++it) - { - LLVFSFileBlock *block = (*it).second; - llassert(block->mFileType >= LLAssetType::AT_NONE && - block->mFileType < LLAssetType::AT_COUNT && - block->mFileID != LLUUID::null); - - for (std::deque::iterator iter = mIndexHoles.begin(); - iter != mIndexHoles.end(); ++iter) - { - S32 index_loc = *iter; - if (index_loc == block->mIndexLocation) - { - LL_WARNS() << "VFile block " << block->mFileID << ":" << block->mFileType << " is marked as a hole" << LL_ENDL; - } - } - } - - LL_INFOS() << "VFS: mem check OK" << LL_ENDL; - - unlockData(); -} - -void LLVFS::dumpLockCounts() -{ - S32 i; - for (i = 0; i < VFSLOCK_COUNT; i++) - { - LL_INFOS() << "LockType: " << i << ": " << mLockCounts[i] << LL_ENDL; - } -} - -void LLVFS::dumpStatistics() -{ - lockData(); - - // Investigate file blocks. - std::map size_counts; - std::map location_counts; - std::map > filetype_counts; - - S32 max_file_size = 0; - S32 total_file_size = 0; - S32 invalid_file_count = 0; - for (fileblock_map::iterator it = mFileBlocks.begin(); it != mFileBlocks.end(); ++it) - { - LLVFSFileBlock *file_block = (*it).second; - if (file_block->mLength == BLOCK_LENGTH_INVALID) - { - invalid_file_count++; - } - else if (file_block->mLength <= 0) - { - LL_INFOS() << "Bad file block at: " << file_block->mLocation << "\tLength: " << file_block->mLength << "\t" << file_block->mFileID << "\t" << file_block->mFileType << LL_ENDL; - size_counts[file_block->mLength]++; - location_counts[file_block->mLocation]++; - } - else - { - total_file_size += file_block->mLength; - } - - if (file_block->mLength > max_file_size) - { - max_file_size = file_block->mLength; - } - - filetype_counts[file_block->mFileType].first++; - filetype_counts[file_block->mFileType].second += file_block->mLength; - } - - for (std::map::iterator it = size_counts.begin(); it != size_counts.end(); ++it) - { - S32 size = it->first; - S32 size_count = it->second; - LL_INFOS() << "Bad files size " << size << " count " << size_count << LL_ENDL; - } - for (std::map::iterator it = location_counts.begin(); it != location_counts.end(); ++it) - { - U32 location = it->first; - S32 location_count = it->second; - LL_INFOS() << "Bad files location " << location << " count " << location_count << LL_ENDL; - } - - // Investigate free list. - S32 max_free_size = 0; - S32 total_free_size = 0; - std::map free_length_counts; - for (blocks_location_map_t::iterator iter = mFreeBlocksByLocation.begin(), - end = mFreeBlocksByLocation.end(); - iter != end; iter++) - { - LLVFSBlock *free_block = iter->second; - if (free_block->mLength <= 0) - { - LL_INFOS() << "Bad free block at: " << free_block->mLocation << "\tLength: " << free_block->mLength << LL_ENDL; - } - else - { - LL_INFOS() << "Block: " << free_block->mLocation - << "\tLength: " << free_block->mLength - << "\tEnd: " << free_block->mLocation + free_block->mLength - << LL_ENDL; - total_free_size += free_block->mLength; - } - - if (free_block->mLength > max_free_size) - { - max_free_size = free_block->mLength; - } - - free_length_counts[free_block->mLength]++; - } - - // Dump histogram of free block sizes - for (std::map::iterator it = free_length_counts.begin(); it != free_length_counts.end(); ++it) - { - LL_INFOS() << "Free length " << it->first << " count " << it->second << LL_ENDL; - } - - LL_INFOS() << "Invalid blocks: " << invalid_file_count << LL_ENDL; - LL_INFOS() << "File blocks: " << mFileBlocks.size() << LL_ENDL; - - S32 length_list_count = (S32)mFreeBlocksByLength.size(); - S32 location_list_count = (S32)mFreeBlocksByLocation.size(); - if (length_list_count == location_list_count) - { - LL_INFOS() << "Free list lengths match, free blocks: " << location_list_count << LL_ENDL; - } - else - { - LL_WARNS() << "Free list lengths do not match!" << LL_ENDL; - LL_WARNS() << "By length: " << length_list_count << LL_ENDL; - LL_WARNS() << "By location: " << location_list_count << LL_ENDL; - } - LL_INFOS() << "Max file: " << max_file_size/1024 << "K" << LL_ENDL; - LL_INFOS() << "Max free: " << max_free_size/1024 << "K" << LL_ENDL; - LL_INFOS() << "Total file size: " << total_file_size/1024 << "K" << LL_ENDL; - LL_INFOS() << "Total free size: " << total_free_size/1024 << "K" << LL_ENDL; - LL_INFOS() << "Sum: " << (total_file_size + total_free_size) << " bytes" << LL_ENDL; - LL_INFOS() << llformat("%.0f%% full",((F32)(total_file_size)/(F32)(total_file_size+total_free_size))*100.f) << LL_ENDL; - - LL_INFOS() << " " << LL_ENDL; - for (std::map >::iterator iter = filetype_counts.begin(); - iter != filetype_counts.end(); ++iter) - { - LL_INFOS() << "Type: " << LLAssetType::getDesc(iter->first) - << " Count: " << iter->second.first - << " Bytes: " << (iter->second.second>>20) << " MB" << LL_ENDL; - } - - // Look for potential merges - { - blocks_location_map_t::iterator iter = mFreeBlocksByLocation.begin(); - blocks_location_map_t::iterator end = mFreeBlocksByLocation.end(); - LLVFSBlock *first_block = iter->second; - while(iter != end) - { - if (++iter == end) - break; - LLVFSBlock *second_block = iter->second; - if (first_block->mLocation + first_block->mLength == second_block->mLocation) - { - LL_INFOS() << "Potential merge at " << first_block->mLocation << LL_ENDL; - } - first_block = second_block; - } - } - unlockData(); -} - -// Debug Only! -std::string get_extension(LLAssetType::EType type) -{ - std::string extension; - switch(type) - { - case LLAssetType::AT_TEXTURE: - extension = ".jp2"; // formerly ".j2c" - break; - case LLAssetType::AT_SOUND: - extension = ".ogg"; - break; - case LLAssetType::AT_SOUND_WAV: - extension = ".wav"; - break; - case LLAssetType::AT_TEXTURE_TGA: - extension = ".tga"; - break; - case LLAssetType::AT_ANIMATION: - extension = ".lla"; - break; - case LLAssetType::AT_MESH: - extension = ".slm"; - break; - default: - // Just use the asset server filename extension in most cases - extension += "."; - extension += LLAssetType::lookup(type); - break; - } - return extension; -} - -void LLVFS::listFiles() -{ - lockData(); - - for (fileblock_map::iterator it = mFileBlocks.begin(); it != mFileBlocks.end(); ++it) - { - LLVFSFileSpecifier file_spec = it->first; - LLVFSFileBlock *file_block = it->second; - S32 length = file_block->mLength; - S32 size = file_block->mSize; - if (length != BLOCK_LENGTH_INVALID && size > 0) - { - LLUUID id = file_spec.mFileID; - std::string extension = get_extension(file_spec.mFileType); - LL_INFOS() << " File: " << id - << " Type: " << LLAssetType::getDesc(file_spec.mFileType) - << " Size: " << size - << LL_ENDL; - } - } - - unlockData(); -} - -#include "llapr.h" -void LLVFS::dumpFiles() -{ - lockData(); - - S32 files_extracted = 0; - for (fileblock_map::iterator it = mFileBlocks.begin(); it != mFileBlocks.end(); ++it) - { - LLVFSFileSpecifier file_spec = it->first; - LLVFSFileBlock *file_block = it->second; - S32 length = file_block->mLength; - S32 size = file_block->mSize; - if (length != BLOCK_LENGTH_INVALID && size > 0) - { - LLUUID id = file_spec.mFileID; - LLAssetType::EType type = file_spec.mFileType; - std::vector buffer(size); - - unlockData(); - getData(id, type, &buffer[0], 0, size); - lockData(); - - std::string extension = get_extension(type); - std::string filename = id.asString() + extension; - LL_INFOS() << " Writing " << filename << LL_ENDL; - - LLAPRFile outfile; - outfile.open(filename, LL_APR_WB); - outfile.write(&buffer[0], size); - outfile.close(); - - files_extracted++; - } - } - - unlockData(); - - LL_INFOS() << "Extracted " << files_extracted << " files out of " << mFileBlocks.size() << LL_ENDL; -} - -time_t LLVFS::creationTime() -{ - llstat data_file_stat; - int errors = LLFile::stat(mDataFilename, &data_file_stat); - if (0 == errors) - { - time_t creation_time = data_file_stat.st_ctime; -#if LL_DARWIN - creation_time = data_file_stat.st_birthtime; -#endif - return creation_time; - } - return 0; -} - -//============================================================================ -// protected -//============================================================================ - -// static -LLFILE *LLVFS::openAndLock(const std::string& filename, const char* mode, BOOL read_lock) -{ -#if LL_WINDOWS - - return LLFile::_fsopen(filename, mode, (read_lock ? _SH_DENYWR : _SH_DENYRW)); - -#else - - LLFILE *fp; - int fd; - - // first test the lock in a non-destructive way -#if LL_SOLARIS - struct flock fl; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - fl.l_len = 1; -#else // !LL_SOLARIS - if (strchr(mode, 'w') != NULL) - { - fp = LLFile::fopen(filename, "rb"); /* Flawfinder: ignore */ - if (fp) - { - fd = fileno(fp); - if (flock(fd, (read_lock ? LOCK_SH : LOCK_EX) | LOCK_NB) == -1) - { - fclose(fp); - return NULL; - } - - fclose(fp); - } - } -#endif // !LL_SOLARIS - - // now actually open the file for use - fp = LLFile::fopen(filename, mode); /* Flawfinder: ignore */ - if (fp) - { - fd = fileno(fp); -#if LL_SOLARIS - fl.l_type = read_lock ? F_RDLCK : F_WRLCK; - if (fcntl(fd, F_SETLK, &fl) == -1) -#else - if (flock(fd, (read_lock ? LOCK_SH : LOCK_EX) | LOCK_NB) == -1) -#endif - { - fclose(fp); - fp = NULL; - } - } - - return fp; - -#endif -} - -// static -void LLVFS::unlockAndClose(LLFILE *fp) -{ - if (fp) - { - // IW: we don't actually want to unlock on linux - // this is because a forked process can kill the parent's lock - // with an explicit unlock - // however, fclose() will implicitly remove the lock - // but only once both parent and child have closed the file - /* - #if !LL_WINDOWS - int fd = fileno(fp); - flock(fd, LOCK_UN); - #endif - */ -#if LL_SOLARIS - struct flock fl; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - fl.l_len = 1; - fl.l_type = F_UNLCK; - fcntl(fileno(fp), F_SETLK, &fl); -#endif - fclose(fp); - } -} diff --git a/indra/llvfs/llvfs.h b/indra/llvfs/llvfs.h deleted file mode 100644 index 42feafe20b..0000000000 --- a/indra/llvfs/llvfs.h +++ /dev/null @@ -1,183 +0,0 @@ -/** - * @file llvfs.h - * @brief Definition of virtual file system - * - * $LicenseInfo:firstyear=2002&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_LLVFS_H -#define LL_LLVFS_H - -#include -#include "lluuid.h" -#include "llassettype.h" -#include "llthread.h" -#include "llmutex.h" - -enum EVFSValid -{ - VFSVALID_UNKNOWN = 0, - VFSVALID_OK = 1, - VFSVALID_BAD_CORRUPT = 2, - VFSVALID_BAD_CANNOT_OPEN_READONLY = 3, - VFSVALID_BAD_CANNOT_CREATE = 4 -}; - -// Lock types for open vfiles, pending async reads, and pending async appends -// (There are no async normal writes, currently) -enum EVFSLock -{ - VFSLOCK_OPEN = 0, - VFSLOCK_READ = 1, - VFSLOCK_APPEND = 2, - - VFSLOCK_COUNT = 3 -}; - -// internal classes -class LLVFSBlock; -class LLVFSFileBlock; -class LLVFSFileSpecifier -{ -public: - LLVFSFileSpecifier(); - LLVFSFileSpecifier(const LLUUID &file_id, const LLAssetType::EType file_type); - bool operator<(const LLVFSFileSpecifier &rhs) const; - bool operator==(const LLVFSFileSpecifier &rhs) const; - -public: - LLUUID mFileID; - LLAssetType::EType mFileType; -}; - -class LLVFS -{ -private: - // Use createLLVFS() to open a VFS file - // Pass 0 to not presize - LLVFS(const std::string& index_filename, - const std::string& data_filename, - const BOOL read_only, - const U32 presize, - const BOOL remove_after_crash); -public: - ~LLVFS(); - - // Use this function normally to create LLVFS files - // Pass 0 to not presize - static LLVFS * createLLVFS(const std::string& index_filename, - const std::string& data_filename, - const BOOL read_only, - const U32 presize, - const BOOL remove_after_crash); - - BOOL isValid() const { return (VFSVALID_OK == mValid); } - EVFSValid getValidState() const { return mValid; } - - // ---------- The following fucntions lock/unlock mDataMutex ---------- - BOOL getExists(const LLUUID &file_id, const LLAssetType::EType file_type); - S32 getSize(const LLUUID &file_id, const LLAssetType::EType file_type); - - BOOL checkAvailable(S32 max_size); - - S32 getMaxSize(const LLUUID &file_id, const LLAssetType::EType file_type); - BOOL setMaxSize(const LLUUID &file_id, const LLAssetType::EType file_type, S32 max_size); - - void renameFile(const LLUUID &file_id, const LLAssetType::EType file_type, - const LLUUID &new_id, const LLAssetType::EType &new_type); - void removeFile(const LLUUID &file_id, const LLAssetType::EType file_type); - - S32 getData(const LLUUID &file_id, const LLAssetType::EType file_type, U8 *buffer, S32 location, S32 length); - S32 storeData(const LLUUID &file_id, const LLAssetType::EType file_type, const U8 *buffer, S32 location, S32 length); - - void incLock(const LLUUID &file_id, const LLAssetType::EType file_type, EVFSLock lock); - void decLock(const LLUUID &file_id, const LLAssetType::EType file_type, EVFSLock lock); - BOOL isLocked(const LLUUID &file_id, const LLAssetType::EType file_type, EVFSLock lock); - // ---------------------------------------------------------------- - - // Used to trigger evil WinXP behavior of "preloading" entire file into memory. - void pokeFiles(); - - // Verify that the index file contents match the in-memory file structure - // Very slow, do not call routinely. JC - void audit(); - // Check for uninitialized blocks. Slow, do not call in release. JC - void checkMem(); - // for debugging, prints a map of the vfs - void dumpMap(); - void dumpLockCounts(); - void dumpStatistics(); - void listFiles(); - void dumpFiles(); - time_t creationTime(); - -protected: - void removeFileBlock(LLVFSFileBlock *fileblock); - - void eraseBlockLength(LLVFSBlock *block); - void eraseBlock(LLVFSBlock *block); - void addFreeBlock(LLVFSBlock *block); - //void mergeFreeBlocks(); - void useFreeSpace(LLVFSBlock *free_block, S32 length); - void sync(LLVFSFileBlock *block, BOOL remove = FALSE); - void presizeDataFile(const U32 size); - - static LLFILE *openAndLock(const std::string& filename, const char* mode, BOOL read_lock); - static void unlockAndClose(FILE *fp); - - // Can initiate LRU-based file removal to make space. - // The immune file block will not be removed. - LLVFSBlock *findFreeBlock(S32 size, LLVFSFileBlock *immune = NULL); - - // lock/unlock data mutex (mDataMutex) - void lockData() { mDataMutex->lock(); } - void unlockData() { mDataMutex->unlock(); } - -protected: - LLMutex* mDataMutex; - - typedef std::map fileblock_map; - fileblock_map mFileBlocks; - - typedef std::multimap blocks_length_map_t; - blocks_length_map_t mFreeBlocksByLength; - typedef std::multimap blocks_location_map_t; - blocks_location_map_t mFreeBlocksByLocation; - - LLFILE *mDataFP; - LLFILE *mIndexFP; - - std::deque mIndexHoles; - - std::string mIndexFilename; - std::string mDataFilename; - BOOL mReadOnly; - - EVFSValid mValid; - - S32 mLockCounts[VFSLOCK_COUNT]; - BOOL mRemoveAfterCrash; -}; - -extern LLVFS *gVFS; - -#endif diff --git a/indra/llvfs/llvfs_objc.h b/indra/llvfs/llvfs_objc.h deleted file mode 100644 index 56cdbebfc5..0000000000 --- a/indra/llvfs/llvfs_objc.h +++ /dev/null @@ -1,43 +0,0 @@ -/** - * @file llvfs_objc.h - * @brief Definition of directory utilities class for Mac OS X - * - * $LicenseInfo:firstyear=2000&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#if !LL_DARWIN -#error This header must not be included when compiling for any target other than Mac OS. Consider including lldir.h instead. -#endif // !LL_DARWIN - -#ifndef LL_LLVFS_OBJC_H -#define LL_LLVFS_OBJC_H - -#include - -std::string* getSystemTempFolder(); -std::string* getSystemCacheFolder(); -std::string* getSystemApplicationSupportFolder(); -std::string* getSystemResourceFolder(); -std::string* getSystemExecutableFolder(); - - -#endif // LL_LLVFS_OBJC_H diff --git a/indra/llvfs/llvfs_objc.mm b/indra/llvfs/llvfs_objc.mm deleted file mode 100644 index 282ea41339..0000000000 --- a/indra/llvfs/llvfs_objc.mm +++ /dev/null @@ -1,108 +0,0 @@ -/** - * @file llvfs_objc.cpp - * @brief Cocoa implementation of directory utilities for Mac OS X - * - * $LicenseInfo:firstyear=2002&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ -#if LL_DARWIN - -//WARNING: This file CANNOT use standard linden includes due to conflicts between definitions of BOOL - -#include "llvfs_objc.h" -#import - -std::string* getSystemTempFolder() -{ - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - NSString * tempDir = NSTemporaryDirectory(); - if (tempDir == nil) - tempDir = @"/tmp"; - std::string *result = ( new std::string([tempDir UTF8String]) ); - [pool release]; - - return result; -} - -//findSystemDirectory scoped exclusively to this file. -std::string* findSystemDirectory(NSSearchPathDirectory searchPathDirectory, - NSSearchPathDomainMask domainMask) -{ - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - - std::string *result = nil; - NSString *path = nil; - - // Search for the path - NSArray* paths = NSSearchPathForDirectoriesInDomains(searchPathDirectory, - domainMask, - YES); - if ([paths count]) - { - path = [paths objectAtIndex:0]; - //HACK: Always attempt to create directory, ignore errors. - NSError *error = nil; - - [[NSFileManager defaultManager] createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:&error]; - - - result = new std::string([path UTF8String]); - } - [pool release]; - return result; -} - -std::string* getSystemExecutableFolder() -{ - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - - NSString *bundlePath = [[NSBundle mainBundle] executablePath]; - std::string *result = (new std::string([bundlePath UTF8String])); - [pool release]; - - return result; -} - -std::string* getSystemResourceFolder() -{ - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - - NSString *bundlePath = [[NSBundle mainBundle] resourcePath]; - std::string *result = (new std::string([bundlePath UTF8String])); - [pool release]; - - return result; -} - -std::string* getSystemCacheFolder() -{ - return findSystemDirectory (NSCachesDirectory, - NSUserDomainMask); -} - -std::string* getSystemApplicationSupportFolder() -{ - return findSystemDirectory (NSApplicationSupportDirectory, - NSUserDomainMask); - -} - -#endif // LL_DARWIN diff --git a/indra/llvfs/llvfsthread.cpp b/indra/llvfs/llvfsthread.cpp deleted file mode 100644 index 8cd85929e2..0000000000 --- a/indra/llvfs/llvfsthread.cpp +++ /dev/null @@ -1,300 +0,0 @@ -/** - * @file llvfsthread.cpp - * @brief LLVFSThread implementation - * - * $LicenseInfo:firstyear=2001&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "linden_common.h" -#include "llvfsthread.h" -#include "llstl.h" - -//============================================================================ - -/*static*/ std::string LLVFSThread::sDataPath = ""; - -/*static*/ LLVFSThread* LLVFSThread::sLocal = NULL; - -//============================================================================ -// Run on MAIN thread -//static -void LLVFSThread::initClass(bool local_is_threaded) -{ - llassert(sLocal == NULL); - sLocal = new LLVFSThread(local_is_threaded); -} - -//static -S32 LLVFSThread::updateClass(U32 ms_elapsed) -{ - sLocal->update((F32)ms_elapsed); - return sLocal->getPending(); -} - -//static -void LLVFSThread::cleanupClass() -{ - sLocal->setQuitting(); - while (sLocal->getPending()) - { - sLocal->update(0); - } - delete sLocal; - sLocal = 0; -} - -//---------------------------------------------------------------------------- - -LLVFSThread::LLVFSThread(bool threaded) : - LLQueuedThread("VFS", threaded) -{ -} - -LLVFSThread::~LLVFSThread() -{ - // ~LLQueuedThread() will be called here -} - -//---------------------------------------------------------------------------- - -LLVFSThread::handle_t LLVFSThread::read(LLVFS* vfs, const LLUUID &file_id, const LLAssetType::EType file_type, - U8* buffer, S32 offset, S32 numbytes, U32 priority, U32 flags) -{ - handle_t handle = generateHandle(); - - priority = llmax(priority, (U32)PRIORITY_LOW); // All reads are at least PRIORITY_LOW - Request* req = new Request(handle, priority, flags, FILE_READ, vfs, file_id, file_type, - buffer, offset, numbytes); - - bool res = addRequest(req); - if (!res) - { - LL_ERRS() << "LLVFSThread::read called after LLVFSThread::cleanupClass()" << LL_ENDL; - req->deleteRequest(); - handle = nullHandle(); - } - - return handle; -} - -S32 LLVFSThread::readImmediate(LLVFS* vfs, const LLUUID &file_id, const LLAssetType::EType file_type, - U8* buffer, S32 offset, S32 numbytes) -{ - handle_t handle = generateHandle(); - - Request* req = new Request(handle, PRIORITY_IMMEDIATE, 0, FILE_READ, vfs, file_id, file_type, - buffer, offset, numbytes); - - S32 res = addRequest(req) ? 1 : 0; - if (res == 0) - { - LL_ERRS() << "LLVFSThread::read called after LLVFSThread::cleanupClass()" << LL_ENDL; - req->deleteRequest(); - } - else - { - llverify(waitForResult(handle, false) == true); - res = req->getBytesRead(); - completeRequest(handle); - } - return res; -} - -LLVFSThread::handle_t LLVFSThread::write(LLVFS* vfs, const LLUUID &file_id, const LLAssetType::EType file_type, - U8* buffer, S32 offset, S32 numbytes, U32 flags) -{ - handle_t handle = generateHandle(); - - Request* req = new Request(handle, 0, flags, FILE_WRITE, vfs, file_id, file_type, - buffer, offset, numbytes); - - bool res = addRequest(req); - if (!res) - { - LL_ERRS() << "LLVFSThread::read called after LLVFSThread::cleanupClass()" << LL_ENDL; - req->deleteRequest(); - handle = nullHandle(); - } - - return handle; -} - -S32 LLVFSThread::writeImmediate(LLVFS* vfs, const LLUUID &file_id, const LLAssetType::EType file_type, - U8* buffer, S32 offset, S32 numbytes) -{ - handle_t handle = generateHandle(); - - Request* req = new Request(handle, PRIORITY_IMMEDIATE, 0, FILE_WRITE, vfs, file_id, file_type, - buffer, offset, numbytes); - - S32 res = addRequest(req) ? 1 : 0; - if (res == 0) - { - LL_ERRS() << "LLVFSThread::read called after LLVFSThread::cleanupClass()" << LL_ENDL; - req->deleteRequest(); - } - else - { - llverify(waitForResult(handle, false) == true); - res = req->getBytesRead(); - completeRequest(handle); - } - return res; -} - - -// LLVFSThread::handle_t LLVFSThread::rename(LLVFS* vfs, const LLUUID &file_id, const LLAssetType::EType file_type, -// const LLUUID &new_id, const LLAssetType::EType new_type, U32 flags) -// { -// handle_t handle = generateHandle(); - -// LLUUID* new_idp = new LLUUID(new_id); // deleted with Request -// // new_type is passed as "numbytes" -// Request* req = new Request(handle, 0, flags, FILE_RENAME, vfs, file_id, file_type, -// (U8*)new_idp, 0, (S32)new_type); - -// bool res = addRequest(req); -// if (!res) -// { -// LL_ERRS() << "LLVFSThread::read called after LLVFSThread::cleanupClass()" << LL_ENDL; -// req->deleteRequest(); -// handle = nullHandle(); -// } - -// return handle; -// } - -//============================================================================ - -LLVFSThread::Request::Request(handle_t handle, U32 priority, U32 flags, - operation_t op, LLVFS* vfs, - const LLUUID &file_id, const LLAssetType::EType file_type, - U8* buffer, S32 offset, S32 numbytes) : - QueuedRequest(handle, priority, flags), - mOperation(op), - mVFS(vfs), - mFileID(file_id), - mFileType(file_type), - mBuffer(buffer), - mOffset(offset), - mBytes(numbytes), - mBytesRead(0) -{ - llassert(mBuffer); - - if (numbytes <= 0 && mOperation != FILE_RENAME) - { - LL_WARNS() << "LLVFSThread: Request with numbytes = " << numbytes - << " operation = " << op - << " offset " << offset - << " file_type " << file_type << LL_ENDL; - } - if (mOperation == FILE_WRITE) - { - S32 blocksize = mVFS->getMaxSize(mFileID, mFileType); - if (blocksize < 0) - { - LL_WARNS() << "VFS write to temporary block (shouldn't happen)" << LL_ENDL; - } - mVFS->incLock(mFileID, mFileType, VFSLOCK_APPEND); - } - else if (mOperation == FILE_RENAME) - { - mVFS->incLock(mFileID, mFileType, VFSLOCK_APPEND); - } - else // if (mOperation == FILE_READ) - { - mVFS->incLock(mFileID, mFileType, VFSLOCK_READ); - } -} - -// dec locks as soon as a request finishes -void LLVFSThread::Request::finishRequest(bool completed) -{ - if (mOperation == FILE_WRITE) - { - mVFS->decLock(mFileID, mFileType, VFSLOCK_APPEND); - } - else if (mOperation == FILE_RENAME) - { - mVFS->decLock(mFileID, mFileType, VFSLOCK_APPEND); - } - else // if (mOperation == FILE_READ) - { - mVFS->decLock(mFileID, mFileType, VFSLOCK_READ); - } -} - -void LLVFSThread::Request::deleteRequest() -{ - if (getStatus() == STATUS_QUEUED) - { - LL_ERRS() << "Attempt to delete a queued LLVFSThread::Request!" << LL_ENDL; - } - if (mOperation == FILE_WRITE) - { - if (mFlags & FLAG_AUTO_DELETE) - { - delete [] mBuffer; - } - } - else if (mOperation == FILE_RENAME) - { - LLUUID* new_idp = (LLUUID*)mBuffer; - delete new_idp; - } - LLQueuedThread::QueuedRequest::deleteRequest(); -} - -bool LLVFSThread::Request::processRequest() -{ - bool complete = false; - if (mOperation == FILE_READ) - { - llassert(mOffset >= 0); - mBytesRead = mVFS->getData(mFileID, mFileType, mBuffer, mOffset, mBytes); - complete = true; - //LL_INFOS() << llformat("LLVFSThread::READ '%s': %d bytes arg:%d",getFilename(),mBytesRead) << LL_ENDL; - } - else if (mOperation == FILE_WRITE) - { - mBytesRead = mVFS->storeData(mFileID, mFileType, mBuffer, mOffset, mBytes); - complete = true; - //LL_INFOS() << llformat("LLVFSThread::WRITE '%s': %d bytes arg:%d",getFilename(),mBytesRead) << LL_ENDL; - } - else if (mOperation == FILE_RENAME) - { - LLUUID* new_idp = (LLUUID*)mBuffer; - LLAssetType::EType new_type = (LLAssetType::EType)mBytes; - mVFS->renameFile(mFileID, mFileType, *new_idp, new_type); - mFileID = *new_idp; - complete = true; - //LL_INFOS() << llformat("LLVFSThread::RENAME '%s': %d bytes arg:%d",getFilename(),mBytesRead) << LL_ENDL; - } - else - { - LL_ERRS() << llformat("LLVFSThread::unknown operation: %d", mOperation) << LL_ENDL; - } - return complete; -} - -//============================================================================ diff --git a/indra/llvfs/llvfsthread.h b/indra/llvfs/llvfsthread.h deleted file mode 100644 index 7814de4a2d..0000000000 --- a/indra/llvfs/llvfsthread.h +++ /dev/null @@ -1,140 +0,0 @@ -/** - * @file llvfsthread.h - * @brief LLVFSThread definition - * - * $LicenseInfo:firstyear=2001&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_LLVFSTHREAD_H -#define LL_LLVFSTHREAD_H - -#include -#include -#include -#include - -#include "llqueuedthread.h" - -#include "llvfs.h" - -//============================================================================ - -class LLVFSThread : public LLQueuedThread -{ - //------------------------------------------------------------------------ -public: - enum operation_t { - FILE_READ, - FILE_WRITE, - FILE_RENAME - }; - - //------------------------------------------------------------------------ -public: - - class Request : public QueuedRequest - { - protected: - ~Request() {}; // use deleteRequest() - - public: - Request(handle_t handle, U32 priority, U32 flags, - operation_t op, LLVFS* vfs, - const LLUUID &file_id, const LLAssetType::EType file_type, - U8* buffer, S32 offset, S32 numbytes); - - S32 getBytesRead() - { - return mBytesRead; - } - S32 getOperation() - { - return mOperation; - } - U8* getBuffer() - { - return mBuffer; - } - LLVFS* getVFS() - { - return mVFS; - } - std::string getFilename() - { - std::string tstring; - mFileID.toString(tstring); - return tstring; - } - - /*virtual*/ bool processRequest(); - /*virtual*/ void finishRequest(bool completed); - /*virtual*/ void deleteRequest(); - - private: - operation_t mOperation; - - LLVFS* mVFS; - LLUUID mFileID; - LLAssetType::EType mFileType; - - U8* mBuffer; // dest for reads, source for writes, new UUID for rename - S32 mOffset; // offset into file, -1 = append (WRITE only) - S32 mBytes; // bytes to read from file, -1 = all (new mFileType for rename) - S32 mBytesRead; // bytes read from file - }; - - //------------------------------------------------------------------------ -public: - static std::string sDataPath; - static LLVFSThread* sLocal; // Default worker thread - -public: - LLVFSThread(bool threaded = TRUE); - ~LLVFSThread(); - - // Return a Request handle - handle_t read(LLVFS* vfs, const LLUUID &file_id, const LLAssetType::EType file_type, /* Flawfinder: ignore */ - U8* buffer, S32 offset, S32 numbytes, U32 pri=PRIORITY_NORMAL, U32 flags = 0); - handle_t write(LLVFS* vfs, const LLUUID &file_id, const LLAssetType::EType file_type, - U8* buffer, S32 offset, S32 numbytes, U32 flags); - // SJB: rename seems to have issues, especially when threaded -// handle_t rename(LLVFS* vfs, const LLUUID &file_id, const LLAssetType::EType file_type, -// const LLUUID &new_id, const LLAssetType::EType new_type, U32 flags); - // Return number of bytes read - S32 readImmediate(LLVFS* vfs, const LLUUID &file_id, const LLAssetType::EType file_type, - U8* buffer, S32 offset, S32 numbytes); - S32 writeImmediate(LLVFS* vfs, const LLUUID &file_id, const LLAssetType::EType file_type, - U8* buffer, S32 offset, S32 numbytes); - - /*virtual*/ bool processRequest(QueuedRequest* req); - -public: - static void initClass(bool local_is_threaded = TRUE); // Setup sLocal - static S32 updateClass(U32 ms_elapsed); - static void cleanupClass(); // Delete sLocal - static void setDataPath(const std::string& path) { sDataPath = path; } -}; - -//============================================================================ - - -#endif // LL_LLVFSTHREAD_H diff --git a/indra/llwindow/CMakeLists.txt b/indra/llwindow/CMakeLists.txt index 8bfb23ed64..5603c2f322 100644 --- a/indra/llwindow/CMakeLists.txt +++ b/indra/llwindow/CMakeLists.txt @@ -16,7 +16,7 @@ include(LLCommon) include(LLImage) include(LLMath) include(LLRender) -include(LLVFS) +include(LLCache) include(LLWindow) include(LLXML) include(UI) @@ -26,7 +26,7 @@ include_directories( ${LLIMAGE_INCLUDE_DIRS} ${LLMATH_INCLUDE_DIRS} ${LLRENDER_INCLUDE_DIRS} - ${LLVFS_INCLUDE_DIRS} + ${LLCACHE_INCLUDE_DIRS} ${LLWINDOW_INCLUDE_DIRS} ${LLXML_INCLUDE_DIRS} ) @@ -72,7 +72,7 @@ if (LINUX) ${LLIMAGE_LIBRARIES} ${LLMATH_LIBRARIES} ${LLRENDER_LIBRARIES} - ${LLVFS_LIBRARIES} + ${LLCACHE_LIBRARIES} ${LLWINDOW_LIBRARIES} ${LLXML_LIBRARIES} ${UI_LIBRARIES} # for GTK @@ -95,7 +95,7 @@ if (LINUX) ${LLIMAGE_LIBRARIES} ${LLMATH_LIBRARIES} ${LLRENDER_HEADLESS_LIBRARIES} - ${LLVFS_LIBRARIES} + ${LLCACHE_LIBRARIES} ${LLWINDOW_HEADLESS_LIBRARIES} ${LLXML_LIBRARIES} fontconfig # For FCInit and other FC* functions. diff --git a/indra/llxml/CMakeLists.txt b/indra/llxml/CMakeLists.txt index 013a422d35..e2c6026496 100644 --- a/indra/llxml/CMakeLists.txt +++ b/indra/llxml/CMakeLists.txt @@ -5,13 +5,13 @@ project(llxml) include(00-Common) include(LLCommon) include(LLMath) -include(LLVFS) +include(LLCache) include(LLXML) include_directories( ${LLCOMMON_INCLUDE_DIRS} ${LLMATH_INCLUDE_DIRS} - ${LLVFS_INCLUDE_DIRS} + ${LLCACHE_INCLUDE_DIRS} ) include_directories( ${LLCOMMON_SYSTEM_INCLUDE_DIRS} @@ -42,7 +42,7 @@ add_library (llxml ${llxml_SOURCE_FILES}) # Libraries on which this library depends, needed for Linux builds # Sort by high-level to low-level target_link_libraries( llxml - ${LLVFS_LIBRARIES} + ${LLCACHE_LIBRARIES} ${LLMATH_LIBRARIES} ${LLCOMMON_LIBRARIES} ${EXPAT_LIBRARIES} diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 88667bdc11..ccf7ad675f 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -39,7 +39,7 @@ include(LLPlugin) include(LLPrimitive) include(LLRender) include(LLUI) -include(LLVFS) +include(LLCache) include(LLWindow) include(LLXML) include(NDOF) @@ -84,7 +84,7 @@ include_directories( ${LLPRIMITIVE_INCLUDE_DIRS} ${LLRENDER_INCLUDE_DIRS} ${LLUI_INCLUDE_DIRS} - ${LLVFS_INCLUDE_DIRS} + ${LLCACHE_INCLUDE_DIRS} ${LLWINDOW_INCLUDE_DIRS} ${LLXML_INCLUDE_DIRS} ${LLLOGIN_INCLUDE_DIRS} @@ -1995,7 +1995,7 @@ target_link_libraries(${VIEWER_BINARY_NAME} ${LLRENDER_LIBRARIES} ${FREETYPE_LIBRARIES} ${LLUI_LIBRARIES} - ${LLVFS_LIBRARIES} + ${LLCACHE_LIBRARIES} ${LLWINDOW_LIBRARIES} ${LLXML_LIBRARIES} ${LLMATH_LIBRARIES} @@ -2467,7 +2467,7 @@ if (LL_TESTS) set(test_libs ${LLMESSAGE_LIBRARIES} ${WINDOWS_LIBRARIES} - ${LLVFS_LIBRARIES} + ${LLCACHE_LIBRARIES} ${LLMATH_LIBRARIES} ${LLCOMMON_LIBRARIES} ${GOOGLEMOCK_LIBRARIES} @@ -2482,7 +2482,7 @@ if (LL_TESTS) set(test_libs ${WINDOWS_LIBRARIES} - ${LLVFS_LIBRARIES} + ${LLCACHE_LIBRARIES} ${LLMATH_LIBRARIES} ${LLCOMMON_LIBRARIES} ${LLMESSAGE_LIBRARIES} diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index a00aa86d78..592418d50d 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -2835,17 +2835,6 @@ Value -1 - DebugStatModeVFSPendingOps - - Comment - Mode of stat in Statistics floater - Persist - 1 - Type - S32 - Value - -1 - DebugStatModeTimeDialation Comment @@ -3638,17 +3627,6 @@ Value 4 - DumpVFSCaches - - Comment - Dump VFS caches on startup. - Persist - 1 - Type - Boolean - Value - 0 - DynamicCameraStrength Comment @@ -14250,28 +14228,6 @@ Value - VFSOldSize - - Comment - [DO NOT MODIFY] Controls resizing of local file cache - Persist - 1 - Type - U32 - Value - 0 - - VFSSalt - - Comment - [DO NOT MODIFY] Controls local file caching behavior - Persist - 1 - Type - U32 - Value - 1 - VelocityInterpolate Comment diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 75574df00e..b9e3a44786 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -115,7 +115,6 @@ #include "llurlaction.h" #include "llurlentry.h" #include "llvfile.h" -#include "llvfsthread.h" #include "llvolumemgr.h" #include "llxfermanager.h" #include "llphysicsextensions.h" @@ -339,9 +338,6 @@ bool gUseWireframe = FALSE; //use for remember deferred mode in wireframe switch bool gInitialDeferredModeForWireframe = FALSE; -// VFS globals - see llappviewer.h -LLVFS* gStaticVFS = NULL; - LLMemoryInfo gSysMemory; U64Bytes gMemoryAllocated(0); // updated in display_stats() in llviewerdisplay.cpp @@ -430,12 +426,6 @@ void init_default_trans_args() default_trans_args.insert("create_account_url"); } -//---------------------------------------------------------------------------- -// File scope definitons -const char *VFS_DATA_FILE_BASE = "data.db2.x."; -const char *VFS_INDEX_FILE_BASE = "index.db2.x."; - - struct SettingsFile : public LLInitParam::Block { Mandatory name; @@ -967,10 +957,6 @@ bool LLAppViewer::init() // *Note: this is where gViewerStats used to be created. - // - // Initialize the VFS, and gracefully handle initialization errors - // - if (!initCache()) { LL_WARNS("InitInfo") << "Failed to init cache" << LL_ENDL; @@ -1326,7 +1312,6 @@ static LLTrace::BlockTimerStatHandle FTM_TEXTURE_CACHE("Texture Cache"); static LLTrace::BlockTimerStatHandle FTM_DECODE("Image Decode"); static LLTrace::BlockTimerStatHandle FTM_FETCH("Image Fetch"); -static LLTrace::BlockTimerStatHandle FTM_VFS("VFS Thread"); static LLTrace::BlockTimerStatHandle FTM_LFS("LFS Thread"); static LLTrace::BlockTimerStatHandle FTM_PAUSE_THREADS("Pause Threads"); static LLTrace::BlockTimerStatHandle FTM_IDLE("Idle"); @@ -1558,10 +1543,6 @@ bool LLAppViewer::doFrame() work_pending += updateTextureThreads(max_time); - { - LL_RECORD_BLOCK_TIME(FTM_VFS); - io_pending += LLVFSThread::updateClass(1); - } { LL_RECORD_BLOCK_TIME(FTM_LFS); io_pending += LLLFSThread::updateClass(1); @@ -1569,7 +1550,7 @@ bool LLAppViewer::doFrame() if (io_pending > 1000) { - ms_sleep(llmin(io_pending/100,100)); // give the vfs some time to catch up + ms_sleep(llmin(io_pending/100,100)); // give the lfs some time to catch up } total_work_pending += work_pending ; @@ -1586,7 +1567,6 @@ bool LLAppViewer::doFrame() } if(!total_io_pending) //pause file threads if nothing to process. { - LLVFSThread::sLocal->pause(); LLLFSThread::sLocal->pause(); } @@ -1648,12 +1628,11 @@ S32 LLAppViewer::updateTextureThreads(F32 max_time) return work_pending; } -void LLAppViewer::flushVFSIO() +void LLAppViewer::flushLFSIO() { while (1) { - S32 pending = LLVFSThread::updateClass(0); - pending += LLLFSThread::updateClass(0); + S32 pending = LLLFSThread::updateClass(0); if (!pending) { break; @@ -1741,7 +1720,7 @@ bool LLAppViewer::cleanup() LLKeyframeDataCache::clear(); - // End TransferManager before deleting systems it depends on (Audio, VFS, AssetStorage) + // End TransferManager before deleting systems it depends on (Audio, AssetStorage) #if 0 // this seems to get us stuck in an infinite loop... gTransferManager.cleanup(); #endif @@ -1808,8 +1787,8 @@ bool LLAppViewer::cleanup() LL_INFOS() << "Cache files removed" << LL_ENDL; - // Wait for any pending VFS IO - flushVFSIO(); + // Wait for any pending LFS IO + flushLFSIO(); LL_INFOS() << "Shutting down Views" << LL_ENDL; // Destroy the UI @@ -1895,13 +1874,7 @@ bool LLAppViewer::cleanup() SUBSYSTEM_CLEANUP(LLWorldMapView); SUBSYSTEM_CLEANUP(LLFolderViewItem); - // - // Shut down the VFS's AFTER the decode manager cleans up (since it cleans up vfiles). - // Also after viewerwindow is deleted, since it may have image pointers (which have vfiles) - // Also after shutting down the messaging system since it has VFS dependencies - - // - LL_INFOS() << "Cleaning up VFS" << LL_ENDL; + LL_INFOS() << "Cleaning up VFILE" << LL_ENDL; SUBSYSTEM_CLEANUP(LLVFile); LL_INFOS() << "Saving Data" << LL_ENDL; @@ -1990,7 +1963,6 @@ bool LLAppViewer::cleanup() pending += LLAppViewer::getTextureCache()->update(1); // unpauses the worker thread pending += LLAppViewer::getImageDecodeThread()->update(1); // unpauses the image thread pending += LLAppViewer::getTextureFetch()->update(1); // unpauses the texture fetch thread - pending += LLVFSThread::updateClass(0); pending += LLLFSThread::updateClass(0); F64 idle_time = idleTimer.getElapsedTimeF64(); if(!pending) @@ -2066,26 +2038,10 @@ bool LLAppViewer::cleanup() LLUIImageList::getInstance()->cleanUp(); // This should eventually be done in LLAppViewer - SUBSYSTEM_CLEANUP(LLVFSThread); SUBSYSTEM_CLEANUP(LLLFSThread); -#ifndef LL_RELEASE_FOR_DOWNLOAD - LL_INFOS() << "Auditing VFS" << LL_ENDL; - if(gVFS) - { - gVFS->audit(); - } -#endif - LL_INFOS() << "Misc Cleanup" << LL_ENDL; - // For safety, the LLVFS has to be deleted *after* LLVFSThread. This should be cleaned up. - // (LLVFS doesn't know about LLVFSThread so can't kill pending requests) -Steve - delete gStaticVFS; - gStaticVFS = NULL; - delete gVFS; - gVFS = NULL; - gSavedSettings.cleanup(); LLUIColorTable::instance().clear(); @@ -2170,7 +2126,6 @@ bool LLAppViewer::initThreads() LLImage::initParamSingleton(gSavedSettings.getBOOL("TextureNewByteRange"),gSavedSettings.getS32("TextureReverseByteRange")); - LLVFSThread::initClass(enable_threads && false); LLLFSThread::initClass(enable_threads && false); // Image decoding @@ -3171,10 +3126,6 @@ LLSD LLAppViewer::getViewerInfo() const info["GPU_SHADERS"] = gSavedSettings.getBOOL("RenderDeferred") ? "Enabled" : "Disabled"; info["TEXTURE_MEMORY"] = gSavedSettings.getS32("TextureMemory"); - LLSD substitution; - substitution["datetime"] = (S32)(gVFS ? gVFS->creationTime() : 0); - info["VFS_TIME"] = LLTrans::getString("AboutTime", substitution); - #if LL_DARWIN info["HIDPI"] = gHiDPISupport; #endif @@ -3915,7 +3866,7 @@ void LLAppViewer::forceQuit() void LLAppViewer::fastQuit(S32 error_code) { // finish pending transfers - flushVFSIO(); + flushLFSIO(); // let sim know we're logging out sendLogoutRequest(); // flush network buffers by shutting down messaging system @@ -4101,39 +4052,6 @@ void LLAppViewer::migrateCacheDirectory() #endif // LL_WINDOWS || LL_DARWIN } -void dumpVFSCaches() -{ - LL_INFOS() << "======= Static VFS ========" << LL_ENDL; - gStaticVFS->listFiles(); -#if LL_WINDOWS - LL_INFOS() << "======= Dumping static VFS to StaticVFSDump ========" << LL_ENDL; - WCHAR w_str[MAX_PATH]; - GetCurrentDirectory(MAX_PATH, w_str); - S32 res = LLFile::mkdir("StaticVFSDump"); - if (res == -1) - { - LL_WARNS() << "Couldn't create dir StaticVFSDump" << LL_ENDL; - } - SetCurrentDirectory(utf8str_to_utf16str("StaticVFSDump").c_str()); - gStaticVFS->dumpFiles(); - SetCurrentDirectory(w_str); -#endif - - LL_INFOS() << "========= Dynamic VFS ====" << LL_ENDL; - gVFS->listFiles(); -#if LL_WINDOWS - LL_INFOS() << "========= Dumping dynamic VFS to VFSDump ====" << LL_ENDL; - res = LLFile::mkdir("VFSDump"); - if (res == -1) - { - LL_WARNS() << "Couldn't create dir VFSDump" << LL_ENDL; - } - SetCurrentDirectory(utf8str_to_utf16str("VFSDump").c_str()); - gVFS->dumpFiles(); - SetCurrentDirectory(w_str); -#endif -} - //static U32 LLAppViewer::getTextureCacheVersion() { @@ -4219,172 +4137,20 @@ bool LLAppViewer::initCache() const S32 MB = 1024 * 1024; const S64 MIN_CACHE_SIZE = 256 * MB; const S64 MAX_CACHE_SIZE = 9984ll * MB; - const S64 MAX_VFS_SIZE = 1024 * MB; // 1 GB S64 cache_size = (S64)(gSavedSettings.getU32("CacheSize")) * MB; cache_size = llclamp(cache_size, MIN_CACHE_SIZE, MAX_CACHE_SIZE); - S64 vfs_size = llmin((S64)((cache_size * 2) / 10), MAX_VFS_SIZE); - S64 texture_cache_size = cache_size - vfs_size; + S64 texture_cache_size = cache_size; S64 extra = LLAppViewer::getTextureCache()->initCache(LL_PATH_CACHE, texture_cache_size, texture_cache_mismatch); texture_cache_size -= extra; - LLVOCache::getInstance()->initCache(LL_PATH_CACHE, gSavedSettings.getU32("CacheNumberOfRegionsForObjects"), getObjectCacheVersion()); - LLSplashScreen::update(LLTrans::getString("StartupInitializingVFS")); + LLVFile::initClass(); - // Init the VFS - vfs_size = llmin(vfs_size + extra, MAX_VFS_SIZE); - vfs_size = (vfs_size / MB) * MB; // make sure it is MB aligned - U32 vfs_size_u32 = (U32)vfs_size; - U32 old_vfs_size = gSavedSettings.getU32("VFSOldSize") * MB; - bool resize_vfs = (vfs_size_u32 != old_vfs_size); - if (resize_vfs) - { - gSavedSettings.setU32("VFSOldSize", vfs_size_u32 / MB); - } - LL_INFOS("AppCache") << "VFS CACHE SIZE: " << vfs_size / (1024*1024) << " MB" << LL_ENDL; - - // This has to happen BEFORE starting the vfs - // time_t ltime; - srand(time(NULL)); // Flawfinder: ignore - U32 old_salt = gSavedSettings.getU32("VFSSalt"); - U32 new_salt; - std::string old_vfs_data_file; - std::string old_vfs_index_file; - std::string new_vfs_data_file; - std::string new_vfs_index_file; - std::string static_vfs_index_file; - std::string static_vfs_data_file; - - if (gSavedSettings.getBOOL("AllowMultipleViewers")) - { - // don't mess with renaming the VFS in this case - new_salt = old_salt; - } - else - { - do - { - new_salt = rand(); - } while(new_salt == old_salt); - } - - old_vfs_data_file = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, VFS_DATA_FILE_BASE) + llformat("%u", old_salt); - - // make sure this file exists - llstat s; - S32 stat_result = LLFile::stat(old_vfs_data_file, &s); - if (stat_result) - { - // doesn't exist, look for a data file - std::string mask; - mask = VFS_DATA_FILE_BASE; - mask += "*"; - - std::string dir; - dir = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, ""); - - std::string found_file; - LLDirIterator iter(dir, mask); - if (iter.next(found_file)) - { - old_vfs_data_file = gDirUtilp->add(dir, found_file); - - S32 start_pos = found_file.find_last_of('.'); - if (start_pos > 0) - { - sscanf(found_file.substr(start_pos+1).c_str(), "%d", &old_salt); - } - LL_DEBUGS("AppCache") << "Default vfs data file not present, found: " << old_vfs_data_file << " Old salt: " << old_salt << LL_ENDL; - } - } - - old_vfs_index_file = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, VFS_INDEX_FILE_BASE) + llformat("%u", old_salt); - - stat_result = LLFile::stat(old_vfs_index_file, &s); - if (stat_result) - { - // We've got a bad/missing index file, nukem! - LL_WARNS("AppCache") << "Bad or missing vfx index file " << old_vfs_index_file << LL_ENDL; - LL_WARNS("AppCache") << "Removing old vfs data file " << old_vfs_data_file << LL_ENDL; - LLFile::remove(old_vfs_data_file); - LLFile::remove(old_vfs_index_file); - - // Just in case, nuke any other old cache files in the directory. - std::string dir; - dir = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, ""); - - std::string mask; - mask = VFS_DATA_FILE_BASE; - mask += "*"; - - gDirUtilp->deleteFilesInDir(dir, mask); - - mask = VFS_INDEX_FILE_BASE; - mask += "*"; - - gDirUtilp->deleteFilesInDir(dir, mask); - } - - new_vfs_data_file = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, VFS_DATA_FILE_BASE) + llformat("%u", new_salt); - new_vfs_index_file = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, VFS_INDEX_FILE_BASE) + llformat("%u", new_salt); - - static_vfs_data_file = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "static_data.db2"); - static_vfs_index_file = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "static_index.db2"); - - if (resize_vfs) - { - LL_DEBUGS("AppCache") << "Removing old vfs and re-sizing" << LL_ENDL; - - LLFile::remove(old_vfs_data_file); - LLFile::remove(old_vfs_index_file); - } - else if (old_salt != new_salt) - { - // move the vfs files to a new name before opening - LL_DEBUGS("AppCache") << "Renaming " << old_vfs_data_file << " to " << new_vfs_data_file << LL_ENDL; - LL_DEBUGS("AppCache") << "Renaming " << old_vfs_index_file << " to " << new_vfs_index_file << LL_ENDL; - LLFile::rename(old_vfs_data_file, new_vfs_data_file); - LLFile::rename(old_vfs_index_file, new_vfs_index_file); - } - - // Startup the VFS... - gSavedSettings.setU32("VFSSalt", new_salt); - - // Don't remove VFS after viewer crashes. If user has corrupt data, they can reinstall. JC - gVFS = LLVFS::createLLVFS(new_vfs_index_file, new_vfs_data_file, false, vfs_size_u32, false); - if (!gVFS) - { - return false; - } - - gStaticVFS = LLVFS::createLLVFS(static_vfs_index_file, static_vfs_data_file, true, 0, false); - if (!gStaticVFS) - { - return false; - } - - BOOL success = gVFS->isValid() && gStaticVFS->isValid(); - if (!success) - { - return false; - } - else - { - LLVFile::initClass(); - -#ifndef LL_RELEASE_FOR_DOWNLOAD - if (gSavedSettings.getBOOL("DumpVFSCaches")) - { - dumpVFSCaches(); - } -#endif - - return true; - } + return true; } void LLAppViewer::addOnIdleCallback(const boost::function& cb) diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h index e8b3464c6e..e4803e9081 100644 --- a/indra/newview/llappviewer.h +++ b/indra/newview/llappviewer.h @@ -1,4 +1,5 @@ /** + * @mainpage * @mainpage * * This is the sources for the Second Life Viewer; @@ -82,7 +83,7 @@ public: virtual bool frame(); // Override for application body logic // Application control - void flushVFSIO(); // waits for vfs transfers to complete + void flushLFSIO(); // waits for lfs transfers to complete void forceQuit(); // Puts the viewer into 'shutting down without error' mode. void fastQuit(S32 error_code = 0); // Shuts down the viewer immediately after sending a logout message void requestQuit(); // Request a quit. A kinder, gentler quit. @@ -377,12 +378,6 @@ extern BOOL gRestoreGL; extern bool gUseWireframe; extern bool gInitialDeferredModeForWireframe; -// VFS globals - gVFS is for general use -// gStaticVFS is read-only and is shipped w/ the viewer -// it has pre-cache data like the UI .TGAs -class LLVFS; -extern LLVFS *gStaticVFS; - extern LLMemoryInfo gSysMemory; extern U64Bytes gMemoryAllocated; diff --git a/indra/newview/llcompilequeue.cpp b/indra/newview/llcompilequeue.cpp index 76e16f5a1f..96a2f6796d 100644 --- a/indra/newview/llcompilequeue.cpp +++ b/indra/newview/llcompilequeue.cpp @@ -116,7 +116,7 @@ namespace } // *NOTE$: A minor specialization of LLScriptAssetUpload, it does not require a buffer -// (and does not save a buffer to the vFS) and it finds the compile queue window and +// (and does not save a buffer to the cache) and it finds the compile queue window and // displays a compiling message. class LLQueuedScriptAssetUpload : public LLScriptAssetUpload { @@ -134,8 +134,8 @@ public: virtual LLSD prepareUpload() { /* *NOTE$: The parent class (LLScriptAssetUpload will attempt to save - * the script buffer into to the VFS. Since the resource is already in - * the VFS we don't want to do that. Just put a compiling message in + * the script buffer into to the cache. Since the resource is already in + * the cache we don't want to do that. Just put a compiling message in * the window and move on */ LLFloaterCompileQueue* queue = LLFloaterReg::findTypedInstance("compile_queue", LLSD(mQueueId)); @@ -283,11 +283,11 @@ void LLFloaterCompileQueue::handleHTTPResponse(std::string pumpName, const LLSD LLEventPumps::instance().post(pumpName, expresult); } -// *TODO: handleSCriptRetrieval is passed into the VFS via a legacy C function pointer +// *TODO: handleSCriptRetrieval is passed into the cache via a legacy C function pointer // future project would be to convert these to C++ callables (std::function<>) so that // we can use bind and remove the userData parameter. // -void LLFloaterCompileQueue::handleScriptRetrieval(LLVFS *vfs, const LLUUID& assetId, +void LLFloaterCompileQueue::handleScriptRetrieval(const LLUUID& assetId, LLAssetType::EType type, void* userData, S32 status, LLExtStat extStatus) { LLSD result(LLSD::emptyMap()); diff --git a/indra/newview/llcompilequeue.h b/indra/newview/llcompilequeue.h index 1b3d8f83a0..f0e104b2da 100644 --- a/indra/newview/llcompilequeue.h +++ b/indra/newview/llcompilequeue.h @@ -138,7 +138,7 @@ protected: //bool checkAssetId(const LLUUID &assetId); static void handleHTTPResponse(std::string pumpName, const LLSD &expresult); - static void handleScriptRetrieval(LLVFS *vfs, const LLUUID& assetId, LLAssetType::EType type, void* userData, S32 status, LLExtStat extStatus); + static void handleScriptRetrieval(const LLUUID& assetId, LLAssetType::EType type, void* userData, S32 status, LLExtStat extStatus); private: static void processExperienceIdResults(LLSD result, LLUUID parent); diff --git a/indra/newview/llfilepicker.h b/indra/newview/llfilepicker.h index 2fc496a144..04ba4416d7 100644 --- a/indra/newview/llfilepicker.h +++ b/indra/newview/llfilepicker.h @@ -136,7 +136,7 @@ public: S32 getFileCount() const { return (S32)mFiles.size(); } - // See llvfs/lldir.h : getBaseFileName and getDirName to extract base or directory names + // see lldir.h : getBaseFileName and getDirName to extract base or directory names // clear any lists of buffers or whatever, and make sure the file // picker isn't locked. diff --git a/indra/newview/llfloaterauction.cpp b/indra/newview/llfloaterauction.cpp index 957b2e1e8e..bbb6409111 100644 --- a/indra/newview/llfloaterauction.cpp +++ b/indra/newview/llfloaterauction.cpp @@ -33,7 +33,6 @@ #include "llimagetga.h" #include "llparcel.h" #include "llvfile.h" -#include "llvfs.h" #include "llwindow.h" #include "message.h" @@ -202,7 +201,7 @@ void LLFloaterAuction::onClickSnapshot(void* data) LLPointer tga = new LLImageTGA; tga->encode(raw); - LLVFile::writeFile(tga->getData(), tga->getDataSize(), gVFS, self->mImageID, LLAssetType::AT_IMAGE_TGA); + LLVFile::writeFile(tga->getData(), tga->getDataSize(), self->mImageID, LLAssetType::AT_IMAGE_TGA); raw->biasedScaleToPowerOfTwo(LLViewerTexture::MAX_IMAGE_SIZE_DEFAULT); @@ -210,7 +209,7 @@ void LLFloaterAuction::onClickSnapshot(void* data) LLPointer j2c = new LLImageJ2C; j2c->encode(raw, 0.0f); - LLVFile::writeFile(j2c->getData(), j2c->getDataSize(), gVFS, self->mImageID, LLAssetType::AT_TEXTURE); + LLVFile::writeFile(j2c->getData(), j2c->getDataSize(), self->mImageID, LLAssetType::AT_TEXTURE); self->mImage = LLViewerTextureManager::getLocalTexture((LLImageRaw*)raw, FALSE); gGL.getTexUnit(0)->bind(self->mImage); diff --git a/indra/newview/llfloaterbvhpreview.cpp b/indra/newview/llfloaterbvhpreview.cpp index 131d9b077b..88ea3d74fb 100644 --- a/indra/newview/llfloaterbvhpreview.cpp +++ b/indra/newview/llfloaterbvhpreview.cpp @@ -997,7 +997,7 @@ void LLFloaterBvhPreview::onBtnOK(void* userdata) LLDataPackerBinaryBuffer dp(buffer, file_size); if (motionp->serialize(dp)) { - LLVFile file(gVFS, motionp->getID(), LLAssetType::AT_ANIMATION, LLVFile::APPEND); + LLVFile file(motionp->getID(), LLAssetType::AT_ANIMATION, LLVFile::APPEND); S32 size = dp.getCurrentSize(); file.setMaxSize(size); diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp index bc44e37c5a..bc35975cc6 100644 --- a/indra/newview/llfloatermodelpreview.cpp +++ b/indra/newview/llfloatermodelpreview.cpp @@ -78,7 +78,6 @@ #include "lltoggleablemenu.h" #include "lltrans.h" #include "llvfile.h" -#include "llvfs.h" #include "llcallbacklist.h" #include "llviewerobjectlist.h" #include "llanimationstates.h" diff --git a/indra/newview/llfloaterpreference.h b/indra/newview/llfloaterpreference.h index 526214a617..f99d0e6150 100644 --- a/indra/newview/llfloaterpreference.h +++ b/indra/newview/llfloaterpreference.h @@ -107,7 +107,7 @@ protected: void onBtnOK(const LLSD& userdata); void onBtnCancel(const LLSD& userdata); - void onClickClearCache(); // Clear viewer texture cache, vfs, and VO cache on next startup + void onClickClearCache(); // Clear viewer texture cache, file cache on next startup void onClickBrowserClearCache(); // Clear web history and caches as well as viewer caches above void onLanguageChange(); void onNotificationsChange(const std::string& OptionName); diff --git a/indra/newview/llfloaterregioninfo.cpp b/indra/newview/llfloaterregioninfo.cpp index ec1909d02a..4b67a0f605 100644 --- a/indra/newview/llfloaterregioninfo.cpp +++ b/indra/newview/llfloaterregioninfo.cpp @@ -2229,10 +2229,9 @@ void LLPanelEstateCovenant::loadInvItem(LLInventoryItem *itemp) } // static -void LLPanelEstateCovenant::onLoadComplete(LLVFS *vfs, - const LLUUID& asset_uuid, - LLAssetType::EType type, - void* user_data, S32 status, LLExtStat ext_status) +void LLPanelEstateCovenant::onLoadComplete(const LLUUID& asset_uuid, + LLAssetType::EType type, + void* user_data, S32 status, LLExtStat ext_status) { LL_INFOS() << "LLPanelEstateCovenant::onLoadComplete()" << LL_ENDL; LLPanelEstateCovenant* panelp = (LLPanelEstateCovenant*)user_data; @@ -2240,7 +2239,7 @@ void LLPanelEstateCovenant::onLoadComplete(LLVFS *vfs, { if(0 == status) { - LLVFile file(vfs, asset_uuid, type, LLVFile::READ); + LLVFile file(asset_uuid, type, LLVFile::READ); S32 file_length = file.getSize(); diff --git a/indra/newview/llfloaterregioninfo.h b/indra/newview/llfloaterregioninfo.h index 75d0c3ea5c..c34dbb62e8 100644 --- a/indra/newview/llfloaterregioninfo.h +++ b/indra/newview/llfloaterregioninfo.h @@ -55,7 +55,6 @@ class LLRadioGroup; class LLSliderCtrl; class LLSpinCtrl; class LLTextBox; -class LLVFS; class LLPanelRegionGeneralInfo; class LLPanelRegionDebugInfo; @@ -357,8 +356,7 @@ public: static bool confirmResetCovenantCallback(const LLSD& notification, const LLSD& response); void sendChangeCovenantID(const LLUUID &asset_id); void loadInvItem(LLInventoryItem *itemp); - static void onLoadComplete(LLVFS *vfs, - const LLUUID& asset_uuid, + static void onLoadComplete(const LLUUID& asset_uuid, LLAssetType::EType type, void* user_data, S32 status, LLExtStat ext_status); diff --git a/indra/newview/llfloaterreporter.cpp b/indra/newview/llfloaterreporter.cpp index 702d612343..09178166e9 100644 --- a/indra/newview/llfloaterreporter.cpp +++ b/indra/newview/llfloaterreporter.cpp @@ -45,7 +45,6 @@ #include "llstring.h" #include "llsys.h" #include "llvfile.h" -#include "llvfs.h" #include "mean_collision_data.h" #include "message.h" #include "v3math.h" @@ -899,10 +898,9 @@ void LLFloaterReporter::takeScreenshot(bool use_prev_screenshot) mResourceDatap->mAssetInfo.setName("screenshot_name"); mResourceDatap->mAssetInfo.setDescription("screenshot_descr"); - // store in VFS + // store in cache LLVFile::writeFile(upload_data->getData(), upload_data->getDataSize(), - gVFS, mResourceDatap->mAssetInfo.mUuid, mResourceDatap->mAssetInfo.mType); diff --git a/indra/newview/llfloatertos.h b/indra/newview/llfloatertos.h index 85033acf4d..7c2f0705b7 100644 --- a/indra/newview/llfloatertos.h +++ b/indra/newview/llfloatertos.h @@ -36,7 +36,6 @@ class LLButton; class LLRadioGroup; -class LLVFS; class LLTextEditor; class LLUUID; diff --git a/indra/newview/llgesturemgr.cpp b/indra/newview/llgesturemgr.cpp index 950a6cfaef..119e0d21b2 100644 --- a/indra/newview/llgesturemgr.cpp +++ b/indra/newview/llgesturemgr.cpp @@ -548,7 +548,7 @@ void LLGestureMgr::playGesture(LLMultiGesture* gesture) LLGestureStepAnimation* anim_step = (LLGestureStepAnimation*)step; const LLUUID& anim_id = anim_step->mAnimAssetID; - // Don't request the animation if this step stops it or if it is already in Static VFS + // Don't request the animation if this step stops it or if it is already in the cache if (!(anim_id.isNull() || anim_step->mFlags & ANIM_FLAG_STOP || gAssetStorage->hasLocalAsset(anim_id, LLAssetType::AT_ANIMATION))) @@ -1038,10 +1038,9 @@ void LLGestureMgr::runStep(LLMultiGesture* gesture, LLGestureStep* step) // static -void LLGestureMgr::onLoadComplete(LLVFS *vfs, - const LLUUID& asset_uuid, - LLAssetType::EType type, - void* user_data, S32 status, LLExtStat ext_status) +void LLGestureMgr::onLoadComplete(const LLUUID& asset_uuid, + LLAssetType::EType type, + void* user_data, S32 status, LLExtStat ext_status) { LLLoadInfo* info = (LLLoadInfo*)user_data; @@ -1056,7 +1055,7 @@ void LLGestureMgr::onLoadComplete(LLVFS *vfs, if (0 == status) { - LLVFile file(vfs, asset_uuid, type, LLVFile::READ); + LLVFile file(asset_uuid, type, LLVFile::READ); S32 size = file.getSize(); std::vector buffer(size+1); @@ -1159,8 +1158,7 @@ void LLGestureMgr::onLoadComplete(LLVFS *vfs, } // static -void LLGestureMgr::onAssetLoadComplete(LLVFS *vfs, - const LLUUID& asset_uuid, +void LLGestureMgr::onAssetLoadComplete(const LLUUID& asset_uuid, LLAssetType::EType type, void* user_data, S32 status, LLExtStat ext_status) { @@ -1172,7 +1170,7 @@ void LLGestureMgr::onAssetLoadComplete(LLVFS *vfs, { case LLAssetType::AT_ANIMATION: { - LLKeyframeMotion::onLoadComplete(vfs, asset_uuid, type, user_data, status, ext_status); + LLKeyframeMotion::onLoadComplete(asset_uuid, type, user_data, status, ext_status); self.mLoadingAssets.erase(asset_uuid); @@ -1180,7 +1178,7 @@ void LLGestureMgr::onAssetLoadComplete(LLVFS *vfs, } case LLAssetType::AT_SOUND: { - LLAudioEngine::assetCallback(vfs, asset_uuid, type, user_data, status, ext_status); + LLAudioEngine::assetCallback(asset_uuid, type, user_data, status, ext_status); self.mLoadingAssets.erase(asset_uuid); diff --git a/indra/newview/llgesturemgr.h b/indra/newview/llgesturemgr.h index 402bdf6039..91ab445273 100644 --- a/indra/newview/llgesturemgr.h +++ b/indra/newview/llgesturemgr.h @@ -40,7 +40,6 @@ class LLMultiGesture; class LLGestureListener; class LLGestureStep; class LLUUID; -class LLVFS; class LLGestureManagerObserver { @@ -154,15 +153,13 @@ protected: void done(); // Used by loadGesture - static void onLoadComplete(LLVFS *vfs, - const LLUUID& asset_uuid, - LLAssetType::EType type, - void* user_data, S32 status, LLExtStat ext_status); + static void onLoadComplete(const LLUUID& asset_uuid, + LLAssetType::EType type, + void* user_data, S32 status, LLExtStat ext_status); // Used by playGesture to load an asset file // required to play a gesture step - static void onAssetLoadComplete(LLVFS *vfs, - const LLUUID& asset_uuid, + static void onAssetLoadComplete(const LLUUID& asset_uuid, LLAssetType::EType type, void* user_data, S32 status, LLExtStat ext_status); diff --git a/indra/newview/lllandmarklist.cpp b/indra/newview/lllandmarklist.cpp index c58540914e..247ebf7719 100644 --- a/indra/newview/lllandmarklist.cpp +++ b/indra/newview/lllandmarklist.cpp @@ -97,7 +97,6 @@ LLLandmark* LLLandmarkList::getAsset(const LLUUID& asset_uuid, loaded_callback_t // static void LLLandmarkList::processGetAssetReply( - LLVFS *vfs, const LLUUID& uuid, LLAssetType::EType type, void* user_data, @@ -106,7 +105,7 @@ void LLLandmarkList::processGetAssetReply( { if( status == 0 ) { - LLVFile file(vfs, uuid, type); + LLVFile file(uuid, type); S32 file_length = file.getSize(); std::vector buffer(file_length + 1); diff --git a/indra/newview/lllandmarklist.h b/indra/newview/lllandmarklist.h index 3356f866ce..750cb898e1 100644 --- a/indra/newview/lllandmarklist.h +++ b/indra/newview/lllandmarklist.h @@ -52,7 +52,6 @@ public: BOOL assetExists(const LLUUID& asset_uuid); LLLandmark* getAsset(const LLUUID& asset_uuid, loaded_callback_t cb = NULL); static void processGetAssetReply( - LLVFS *vfs, const LLUUID& uuid, LLAssetType::EType type, void* user_data, diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index c5ced425f6..55c64508d2 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -294,8 +294,6 @@ // * Header parse failures come without much explanation. Elaborate. // * Work queue for uploads? Any need for this or is the current scheme good // enough? -// * Various temp buffers used in VFS I/O might be allocated once or even -// statically. Look for some wins here. // * Move data structures holding mesh data used by main thread into main- // thread-only access so that no locking is needed. May require duplication // of some data so that worker thread has a minimal data set to guide @@ -1336,8 +1334,8 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id) if (version <= MAX_MESH_VERSION && offset >= 0 && size > 0) { - //check VFS for mesh skin info - LLVFile file(gVFS, mesh_id, LLAssetType::AT_MESH); + //check cache for mesh skin info + LLVFile file(mesh_id, LLAssetType::AT_MESH); if (file.getSize() >= offset+size) { U8* buffer = new(std::nothrow) U8[size]; @@ -1370,7 +1368,7 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id) delete[] buffer; } - //reading from VFS failed for whatever reason, fetch from sim + //reading from cache failed for whatever reason, fetch from sim std::string http_url; constructUrl(mesh_id, &http_url); @@ -1432,8 +1430,8 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id) if (version <= MAX_MESH_VERSION && offset >= 0 && size > 0) { - //check VFS for mesh skin info - LLVFile file(gVFS, mesh_id, LLAssetType::AT_MESH); + //check cache for mesh skin info + LLVFile file(mesh_id, LLAssetType::AT_MESH); if (file.getSize() >= offset+size) { U8* buffer = new(std::nothrow) U8[size]; @@ -1467,7 +1465,7 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id) delete[] buffer; } - //reading from VFS failed for whatever reason, fetch from sim + //reading from cache failed for whatever reason, fetch from sim std::string http_url; constructUrl(mesh_id, &http_url); @@ -1529,8 +1527,8 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id) if (version <= MAX_MESH_VERSION && offset >= 0 && size > 0) { - //check VFS for mesh physics shape info - LLVFile file(gVFS, mesh_id, LLAssetType::AT_MESH); + //check cache for mesh physics shape info + LLVFile file(mesh_id, LLAssetType::AT_MESH); if (file.getSize() >= offset+size) { LLMeshRepository::sCacheBytesRead += size; @@ -1563,7 +1561,7 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id) delete[] buffer; } - //reading from VFS failed for whatever reason, fetch from sim + //reading from cache failed for whatever reason, fetch from sim std::string http_url; constructUrl(mesh_id, &http_url); @@ -1634,8 +1632,8 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, bool c ++LLMeshRepository::sMeshRequestCount; { - //look for mesh in asset in vfs - LLVFile file(gVFS, mesh_params.getSculptID(), LLAssetType::AT_MESH); + //look for mesh in asset in cache + LLVFile file(mesh_params.getSculptID(), LLAssetType::AT_MESH); S32 size = file.getSize(); @@ -1649,7 +1647,7 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, bool c file.read(buffer, bytes); if (headerReceived(mesh_params, buffer, bytes)) { - // Found mesh in VFS cache + // Found mesh in cache return true; } } @@ -1713,8 +1711,8 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, if (version <= MAX_MESH_VERSION && offset >= 0 && size > 0) { - //check VFS for mesh asset - LLVFile file(gVFS, mesh_id, LLAssetType::AT_MESH); + //check cache for mesh asset + LLVFile file(mesh_id, LLAssetType::AT_MESH); if (file.getSize() >= offset+size) { U8* buffer = new(std::nothrow) U8[size]; @@ -1749,7 +1747,7 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, delete[] buffer; } - //reading from VFS failed for whatever reason, fetch from sim + //reading from cache failed for whatever reason, fetch from sim std::string http_url; constructUrl(mesh_id, &http_url); @@ -3162,7 +3160,7 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* b } else if (data && data_size > 0) { - // header was successfully retrieved from sim and parsed, cache in vfs + // header was successfully retrieved from sim and parsed and is in cache S32 header_bytes = 0; LLSD header; @@ -3199,10 +3197,10 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* b // It's possible for the remote asset to have more data than is needed for the local cache - // only allocate as much space in the VFS as is needed for the local cache + // only allocate as much space in the cache as is needed for the local cache data_size = llmin(data_size, bytes); - LLVFile file(gVFS, mesh_id, LLAssetType::AT_MESH, LLVFile::WRITE); + LLVFile file(mesh_id, LLAssetType::AT_MESH, LLVFile::WRITE); if (file.getMaxSize() >= bytes || file.setMaxSize(bytes)) { LLMeshRepository::sCacheBytesWritten += data_size; @@ -3273,8 +3271,8 @@ void LLMeshLODHandler::processData(LLCore::BufferArray * /* body */, S32 /* body EMeshProcessingResult result = gMeshRepo.mThread->lodReceived(mMeshParams, mLOD, data, data_size); if (result == MESH_OK) { - // good fetch from sim, write to VFS for caching - LLVFile file(gVFS, mMeshParams.getSculptID(), LLAssetType::AT_MESH, LLVFile::WRITE); + // good fetch from sim, write to cache + LLVFile file(mMeshParams.getSculptID(), LLAssetType::AT_MESH, LLVFile::WRITE); S32 offset = mOffset; S32 size = mRequestedBytes; @@ -3337,8 +3335,8 @@ void LLMeshSkinInfoHandler::processData(LLCore::BufferArray * /* body */, S32 /* && ((data != NULL) == (data_size > 0)) // if we have data but no size or have size but no data, something is wrong && gMeshRepo.mThread->skinInfoReceived(mMeshID, data, data_size)) { - // good fetch from sim, write to VFS for caching - LLVFile file(gVFS, mMeshID, LLAssetType::AT_MESH, LLVFile::WRITE); + // good fetch from sim, write to cache + LLVFile file(mMeshID, LLAssetType::AT_MESH, LLVFile::WRITE); S32 offset = mOffset; S32 size = mRequestedBytes; @@ -3385,8 +3383,8 @@ void LLMeshDecompositionHandler::processData(LLCore::BufferArray * /* body */, S && ((data != NULL) == (data_size > 0)) // if we have data but no size or have size but no data, something is wrong && gMeshRepo.mThread->decompositionReceived(mMeshID, data, data_size)) { - // good fetch from sim, write to VFS for caching - LLVFile file(gVFS, mMeshID, LLAssetType::AT_MESH, LLVFile::WRITE); + // good fetch from sim, write to cache + LLVFile file(mMeshID, LLAssetType::AT_MESH, LLVFile::WRITE); S32 offset = mOffset; S32 size = mRequestedBytes; @@ -3432,8 +3430,8 @@ void LLMeshPhysicsShapeHandler::processData(LLCore::BufferArray * /* body */, S3 && ((data != NULL) == (data_size > 0)) // if we have data but no size or have size but no data, something is wrong && gMeshRepo.mThread->physicsShapeReceived(mMeshID, data, data_size)) { - // good fetch from sim, write to VFS for caching - LLVFile file(gVFS, mMeshID, LLAssetType::AT_MESH, LLVFile::WRITE); + // good fetch from sim, write to cache for caching + LLVFile file(mMeshID, LLAssetType::AT_MESH, LLVFile::WRITE); S32 offset = mOffset; S32 size = mRequestedBytes; diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index bba0c9f2cb..145c8733f3 100644 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -48,7 +48,6 @@ class LLVOVolume; class LLMutex; class LLCondition; -class LLVFS; class LLMeshRepository; typedef enum e_mesh_processing_result_enum diff --git a/indra/newview/lloutfitgallery.h b/indra/newview/lloutfitgallery.h index 6dd8a6298f..ce5c090134 100644 --- a/indra/newview/lloutfitgallery.h +++ b/indra/newview/lloutfitgallery.h @@ -38,7 +38,6 @@ #include -class LLVFS; class LLOutfitGallery; class LLOutfitGalleryItem; class LLOutfitListGearMenuBase; diff --git a/indra/newview/llpostcard.cpp b/indra/newview/llpostcard.cpp index d5775042c1..5201988d7c 100644 --- a/indra/newview/llpostcard.cpp +++ b/indra/newview/llpostcard.cpp @@ -29,7 +29,6 @@ #include "llpostcard.h" #include "llvfile.h" -#include "llvfs.h" #include "llviewerregion.h" #include "message.h" diff --git a/indra/newview/llpreviewgesture.cpp b/indra/newview/llpreviewgesture.cpp index 70ce275734..aaae6daf2c 100644 --- a/indra/newview/llpreviewgesture.cpp +++ b/indra/newview/llpreviewgesture.cpp @@ -30,7 +30,7 @@ #include "llagent.h" #include "llanimstatelabels.h" #include "llanimationstates.h" -#include "llappviewer.h" // gVFS +#include "llappviewer.h" #include "llcheckboxctrl.h" #include "llcombobox.h" #include "lldatapacker.h" @@ -841,10 +841,9 @@ void LLPreviewGesture::loadAsset() // static -void LLPreviewGesture::onLoadComplete(LLVFS *vfs, - const LLUUID& asset_uuid, - LLAssetType::EType type, - void* user_data, S32 status, LLExtStat ext_status) +void LLPreviewGesture::onLoadComplete(const LLUUID& asset_uuid, + LLAssetType::EType type, + void* user_data, S32 status, LLExtStat ext_status) { LLUUID* item_idp = (LLUUID*)user_data; @@ -853,7 +852,7 @@ void LLPreviewGesture::onLoadComplete(LLVFS *vfs, { if (0 == status) { - LLVFile file(vfs, asset_uuid, type, LLVFile::READ); + LLVFile file(asset_uuid, type, LLVFile::READ); S32 size = file.getSize(); std::vector buffer(size+1); @@ -1138,7 +1137,7 @@ void LLPreviewGesture::saveIfNeeded() tid.generate(); assetId = tid.makeAssetID(gAgent.getSecureSessionID()); - LLVFile file(gVFS, assetId, LLAssetType::AT_GESTURE, LLVFile::APPEND); + LLVFile file(assetId, LLAssetType::AT_GESTURE, LLVFile::APPEND); S32 size = dp.getCurrentSize(); file.setMaxSize(size); diff --git a/indra/newview/llpreviewgesture.h b/indra/newview/llpreviewgesture.h index 3ba4f56295..19bccf35bd 100644 --- a/indra/newview/llpreviewgesture.h +++ b/indra/newview/llpreviewgesture.h @@ -39,7 +39,6 @@ class LLScrollListCtrl; class LLScrollListItem; class LLButton; class LLRadioGroup; -class LLVFS; class LLPreviewGesture : public LLPreview { @@ -80,8 +79,7 @@ protected: void loadAsset(); - static void onLoadComplete(LLVFS *vfs, - const LLUUID& asset_uuid, + static void onLoadComplete(const LLUUID& asset_uuid, LLAssetType::EType type, void* user_data, S32 status, LLExtStat ext_status); diff --git a/indra/newview/llpreviewnotecard.cpp b/indra/newview/llpreviewnotecard.cpp index 7ef0ef0e8b..62ddfd5b3e 100644 --- a/indra/newview/llpreviewnotecard.cpp +++ b/indra/newview/llpreviewnotecard.cpp @@ -326,8 +326,7 @@ void LLPreviewNotecard::loadAsset() } // static -void LLPreviewNotecard::onLoadComplete(LLVFS *vfs, - const LLUUID& asset_uuid, +void LLPreviewNotecard::onLoadComplete(const LLUUID& asset_uuid, LLAssetType::EType type, void* user_data, S32 status, LLExtStat ext_status) { @@ -338,7 +337,7 @@ void LLPreviewNotecard::onLoadComplete(LLVFS *vfs, { if(0 == status) { - LLVFile file(vfs, asset_uuid, type, LLVFile::READ); + LLVFile file(asset_uuid, type, LLVFile::READ); S32 file_length = file.getSize(); @@ -445,7 +444,7 @@ void LLPreviewNotecard::finishInventoryUpload(LLUUID itemId, LLUUID newAssetId, LLPreviewNotecard* nc = LLFloaterReg::findTypedInstance("preview_notecard", LLSD(itemId)); if (nc) { - // *HACK: we have to delete the asset in the VFS so + // *HACK: we have to delete the asset in the cache so // that the viewer will redownload it. This is only // really necessary if the asset had to be modified by // the uploader, so this can be optimized away in some @@ -453,7 +452,7 @@ void LLPreviewNotecard::finishInventoryUpload(LLUUID itemId, LLUUID newAssetId, // script actually changed the asset. if (nc->hasEmbeddedInventory()) { - gVFS->removeFile(newAssetId, LLAssetType::AT_NOTECARD); + LLVFile::removeFile(newAssetId, LLAssetType::AT_NOTECARD); } if (newItemId.isNull()) { @@ -478,7 +477,7 @@ void LLPreviewNotecard::finishTaskUpload(LLUUID itemId, LLUUID newAssetId, LLUUI { if (nc->hasEmbeddedInventory()) { - gVFS->removeFile(newAssetId, LLAssetType::AT_NOTECARD); + LLVFile::removeFile(newAssetId, LLAssetType::AT_NOTECARD); } nc->setAssetId(newAssetId); nc->refreshFromInventory(); @@ -557,7 +556,7 @@ bool LLPreviewNotecard::saveIfNeeded(LLInventoryItem* copyitem, bool sync) tid.generate(); asset_id = tid.makeAssetID(gAgent.getSecureSessionID()); - LLVFile file(gVFS, asset_id, LLAssetType::AT_NOTECARD, LLVFile::APPEND); + LLVFile file(asset_id, LLAssetType::AT_NOTECARD, LLVFile::APPEND); LLSaveNotecardInfo* info = new LLSaveNotecardInfo(this, mItemUUID, mObjectUUID, diff --git a/indra/newview/llpreviewnotecard.h b/indra/newview/llpreviewnotecard.h index d9c14815c1..063a01ca47 100644 --- a/indra/newview/llpreviewnotecard.h +++ b/indra/newview/llpreviewnotecard.h @@ -83,8 +83,7 @@ protected: void deleteNotecard(); - static void onLoadComplete(LLVFS *vfs, - const LLUUID& asset_uuid, + static void onLoadComplete(const LLUUID& asset_uuid, LLAssetType::EType type, void* user_data, S32 status, LLExtStat ext_status); diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp index 7a68d1e270..2eedce9bc1 100644 --- a/indra/newview/llpreviewscript.cpp +++ b/indra/newview/llpreviewscript.cpp @@ -1704,8 +1704,8 @@ void LLPreviewLSL::saveIfNeeded(bool sync /*= true*/) } // static -void LLPreviewLSL::onLoadComplete( LLVFS *vfs, const LLUUID& asset_uuid, LLAssetType::EType type, - void* user_data, S32 status, LLExtStat ext_status) +void LLPreviewLSL::onLoadComplete(const LLUUID& asset_uuid, LLAssetType::EType type, + void* user_data, S32 status, LLExtStat ext_status) { LL_DEBUGS() << "LLPreviewLSL::onLoadComplete: got uuid " << asset_uuid << LL_ENDL; @@ -1715,7 +1715,7 @@ void LLPreviewLSL::onLoadComplete( LLVFS *vfs, const LLUUID& asset_uuid, LLAsset { if(0 == status) { - LLVFile file(vfs, asset_uuid, type); + LLVFile file(asset_uuid, type); S32 file_length = file.getSize(); std::vector buffer(file_length+1); @@ -1978,7 +1978,7 @@ void LLLiveLSLEditor::loadAsset() } // static -void LLLiveLSLEditor::onLoadComplete(LLVFS *vfs, const LLUUID& asset_id, +void LLLiveLSLEditor::onLoadComplete(const LLUUID& asset_id, LLAssetType::EType type, void* user_data, S32 status, LLExtStat ext_status) { @@ -1992,7 +1992,7 @@ void LLLiveLSLEditor::onLoadComplete(LLVFS *vfs, const LLUUID& asset_id, { if( LL_ERR_NOERR == status ) { - instance->loadScriptText(vfs, asset_id, type); + instance->loadScriptText(asset_id, type); instance->mScriptEd->setEnableEditing(TRUE); instance->mAssetStatus = PREVIEW_ASSET_LOADED; } @@ -2018,9 +2018,9 @@ void LLLiveLSLEditor::onLoadComplete(LLVFS *vfs, const LLUUID& asset_id, delete floater_key; } -void LLLiveLSLEditor::loadScriptText(LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type) +void LLLiveLSLEditor::loadScriptText(const LLUUID &uuid, LLAssetType::EType type) { - LLVFile file(vfs, uuid, type); + LLVFile file(uuid, type); S32 file_length = file.getSize(); std::vector buffer(file_length + 1); file.read((U8*)&buffer[0], file_length); diff --git a/indra/newview/llpreviewscript.h b/indra/newview/llpreviewscript.h index 74e4c00d43..40ab3a3dbb 100644 --- a/indra/newview/llpreviewscript.h +++ b/indra/newview/llpreviewscript.h @@ -48,7 +48,6 @@ struct LLEntryAndEdCore; class LLMenuBarGL; class LLFloaterScriptSearch; class LLKeywordToken; -class LLVFS; class LLViewerInventoryItem; class LLScriptEdContainer; class LLFloaterGotoLine; @@ -235,7 +234,7 @@ protected: static void onLoad(void* userdata); static void onSave(void* userdata, BOOL close_after_save); - static void onLoadComplete(LLVFS *vfs, const LLUUID& uuid, + static void onLoadComplete(const LLUUID& uuid, LLAssetType::EType type, void* user_data, S32 status, LLExtStat ext_status); @@ -296,13 +295,13 @@ private: static void onLoad(void* userdata); static void onSave(void* userdata, BOOL close_after_save); - static void onLoadComplete(LLVFS *vfs, const LLUUID& asset_uuid, + static void onLoadComplete(const LLUUID& asset_uuid, LLAssetType::EType type, void* user_data, S32 status, LLExtStat ext_status); static void onRunningCheckboxClicked(LLUICtrl*, void* userdata); static void onReset(void* userdata); - void loadScriptText(LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type); + void loadScriptText(const LLUUID &uuid, LLAssetType::EType type); static void onErrorList(LLUICtrl*, void* user_data); diff --git a/indra/newview/llsettingsvo.cpp b/indra/newview/llsettingsvo.cpp index 97b5b2a57d..0084180cf8 100644 --- a/indra/newview/llsettingsvo.cpp +++ b/indra/newview/llsettingsvo.cpp @@ -292,18 +292,18 @@ void LLSettingsVOBase::onTaskAssetUploadComplete(LLUUID itemId, LLUUID taskId, L void LLSettingsVOBase::getSettingsAsset(const LLUUID &assetId, LLSettingsVOBase::asset_download_fn callback) { gAssetStorage->getAssetData(assetId, LLAssetType::AT_SETTINGS, - [callback](LLVFS *vfs, const LLUUID &asset_id, LLAssetType::EType, void *, S32 status, LLExtStat ext_status) - { onAssetDownloadComplete(vfs, asset_id, status, ext_status, callback); }, + [callback](const LLUUID &asset_id, LLAssetType::EType, void *, S32 status, LLExtStat ext_status) + { onAssetDownloadComplete(asset_id, status, ext_status, callback); }, nullptr, true); } -void LLSettingsVOBase::onAssetDownloadComplete(LLVFS *vfs, const LLUUID &asset_id, S32 status, LLExtStat ext_status, LLSettingsVOBase::asset_download_fn callback) +void LLSettingsVOBase::onAssetDownloadComplete(const LLUUID &asset_id, S32 status, LLExtStat ext_status, LLSettingsVOBase::asset_download_fn callback) { LLSettingsBase::ptr_t settings; if (!status) { - LLVFile file(vfs, asset_id, LLAssetType::AT_SETTINGS, LLVFile::READ); + LLVFile file(asset_id, LLAssetType::AT_SETTINGS, LLVFile::READ); S32 size = file.getSize(); std::string buffer(size + 1, '\0'); diff --git a/indra/newview/llsettingsvo.h b/indra/newview/llsettingsvo.h index 65136ad2f5..a1baf02fe7 100644 --- a/indra/newview/llsettingsvo.h +++ b/indra/newview/llsettingsvo.h @@ -38,7 +38,6 @@ #include "llextendedstatus.h" #include -class LLVFS; class LLInventoryItem; class LLGLSLShader; @@ -81,7 +80,7 @@ private: static void onAgentAssetUploadComplete(LLUUID itemId, LLUUID newAssetId, LLUUID newItemId, LLSD response, LLSettingsBase::ptr_t psettings, inventory_result_fn callback); static void onTaskAssetUploadComplete(LLUUID itemId, LLUUID taskId, LLUUID newAssetId, LLSD response, LLSettingsBase::ptr_t psettings, inventory_result_fn callback); - static void onAssetDownloadComplete(LLVFS *vfs, const LLUUID &asset_id, S32 status, LLExtStat ext_status, asset_download_fn callback); + static void onAssetDownloadComplete(const LLUUID &asset_id, S32 status, LLExtStat ext_status, asset_download_fn callback); }; //========================================================================= diff --git a/indra/newview/llsnapshotlivepreview.cpp b/indra/newview/llsnapshotlivepreview.cpp index f3439daee9..4b8ceba80f 100644 --- a/indra/newview/llsnapshotlivepreview.cpp +++ b/indra/newview/llsnapshotlivepreview.cpp @@ -51,7 +51,6 @@ #include "llviewermenufile.h" // upload_new_resource() #include "llviewerstats.h" #include "llvfile.h" -#include "llvfs.h" #include "llwindow.h" #include "llworld.h" #include @@ -1006,7 +1005,7 @@ void LLSnapshotLivePreview::saveTexture(BOOL outfit_snapshot, std::string name) if (formatted->encode(scaled, 0.0f)) { - LLVFile::writeFile(formatted->getData(), formatted->getDataSize(), gVFS, new_asset_id, LLAssetType::AT_TEXTURE); + LLVFile::writeFile(formatted->getData(), formatted->getDataSize(), new_asset_id, LLAssetType::AT_TEXTURE); std::string pos_string; LLAgentUI::buildLocationString(pos_string, LLAgentUI::LOCATION_FORMAT_FULL); std::string who_took_it; diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 6d20dcf188..344e0492c2 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -82,7 +82,6 @@ #include "llversioninfo.h" #include "llviewercontrol.h" #include "llviewerhelp.h" -#include "llvfs.h" #include "llxorcipher.h" // saved password, MAC address #include "llwindow.h" #include "message.h" @@ -268,7 +267,7 @@ bool login_alert_status(const LLSD& notification, const LLSD& response); void login_packet_failed(void**, S32 result); void use_circuit_callback(void**, S32 result); void register_viewer_callbacks(LLMessageSystem* msg); -void asset_callback_nothing(LLVFS*, const LLUUID&, LLAssetType::EType, void*, S32); +void asset_callback_nothing(const LLUUID&, LLAssetType::EType, void*, S32); bool callback_choose_gender(const LLSD& notification, const LLSD& response); void init_start_screen(S32 location_id); void release_start_screen(); @@ -580,7 +579,7 @@ bool idle_startup() // start the xfer system. by default, choke the downloads // a lot... const S32 VIEWER_MAX_XFER = 3; - start_xfer_manager(gVFS); + start_xfer_manager(); gXferManager->setMaxIncomingXfers(VIEWER_MAX_XFER); F32 xfer_throttle_bps = gSavedSettings.getF32("XferThrottle"); if (xfer_throttle_bps > 1.f) @@ -588,7 +587,7 @@ bool idle_startup() gXferManager->setUseAckThrottling(TRUE); gXferManager->setAckThrottleBPS(xfer_throttle_bps); } - gAssetStorage = new LLViewerAssetStorage(msg, gXferManager, gVFS, gStaticVFS); + gAssetStorage = new LLViewerAssetStorage(msg, gXferManager); F32 dropPercent = gSavedSettings.getF32("PacketDropPercentage"); @@ -1004,13 +1003,6 @@ bool idle_startup() gViewerWindow->revealIntroPanel(); - // Poke the VFS, which could potentially block for a while if - // Windows XP is acting up - set_startup_status(0.07f, LLTrans::getString("LoginVerifyingCache"), LLStringUtil::null); - display_startup(); - - gVFS->pokeFiles(); - LLStartUp::setStartupState( STATE_LOGIN_AUTH_INIT ); return FALSE; @@ -2571,7 +2563,7 @@ void register_viewer_callbacks(LLMessageSystem* msg) msg->setHandlerFuncFast(_PREHASH_FeatureDisabled, process_feature_disabled_message); } -void asset_callback_nothing(LLVFS*, const LLUUID&, LLAssetType::EType, void*, S32) +void asset_callback_nothing(const LLUUID&, LLAssetType::EType, void*, S32) { // nothing } diff --git a/indra/newview/llviewerassetstorage.cpp b/indra/newview/llviewerassetstorage.cpp index cacdee7e83..b4e1e2633b 100644 --- a/indra/newview/llviewerassetstorage.cpp +++ b/indra/newview/llviewerassetstorage.cpp @@ -29,7 +29,6 @@ #include "llviewerassetstorage.h" #include "llvfile.h" -#include "llvfs.h" #include "message.h" #include "llagent.h" @@ -103,10 +102,8 @@ public: ///---------------------------------------------------------------------------- // Unused? -LLViewerAssetStorage::LLViewerAssetStorage(LLMessageSystem *msg, LLXferManager *xfer, - LLVFS *vfs, LLVFS *static_vfs, - const LLHost &upstream_host) - : LLAssetStorage(msg, xfer, vfs, static_vfs, upstream_host), +LLViewerAssetStorage::LLViewerAssetStorage(LLMessageSystem *msg, LLXferManager *xfer, const LLHost &upstream_host) + : LLAssetStorage(msg, xfer, upstream_host), mAssetCoroCount(0), mCountRequests(0), mCountStarted(0), @@ -116,10 +113,8 @@ LLViewerAssetStorage::LLViewerAssetStorage(LLMessageSystem *msg, LLXferManager * { } - -LLViewerAssetStorage::LLViewerAssetStorage(LLMessageSystem *msg, LLXferManager *xfer, - LLVFS *vfs, LLVFS *static_vfs) - : LLAssetStorage(msg, xfer, vfs, static_vfs), +LLViewerAssetStorage::LLViewerAssetStorage(LLMessageSystem *msg, LLXferManager *xfer) + : LLAssetStorage(msg, xfer), mAssetCoroCount(0), mCountRequests(0), mCountStarted(0), @@ -156,13 +151,13 @@ void LLViewerAssetStorage::storeAssetData( if (mUpstreamHost.isOk()) { - if (mVFS->getExists(asset_id, asset_type)) + if (LLVFile::getExists(asset_id, asset_type)) { // Pack data into this packet if we can fit it. U8 buffer[MTUBYTES]; buffer[0] = 0; - LLVFile vfile(mVFS, asset_id, asset_type, LLVFile::READ); + LLVFile vfile(asset_id, asset_type, LLVFile::READ); S32 asset_size = vfile.getSize(); LLAssetRequest *req = new LLAssetRequest(asset_id, asset_type); @@ -171,22 +166,20 @@ void LLViewerAssetStorage::storeAssetData( if (asset_size < 1) { - // This can happen if there's a bug in our code or if the VFS has been corrupted. - LL_WARNS("AssetStorage") << "LLViewerAssetStorage::storeAssetData() Data _should_ already be in the VFS, but it's not! " << asset_id << LL_ENDL; - // LLAssetStorage metric: Zero size VFS - reportMetric( asset_id, asset_type, LLStringUtil::null, LLUUID::null, 0, MR_ZERO_SIZE, __FILE__, __LINE__, "The file didn't exist or was zero length (VFS - can't tell which)" ); + // This can happen if there's a bug in our code or if the cache has been corrupted. + LL_WARNS("AssetStorage") << "LLViewerAssetStorage::storeAssetData() Data _should_ already be in the cache, but it's not! " << asset_id << LL_ENDL; delete req; if (callback) { - callback(asset_id, user_data, LL_ERR_ASSET_REQUEST_FAILED, LLExtStat::VFS_CORRUPT); + callback(asset_id, user_data, LL_ERR_ASSET_REQUEST_FAILED, LLExtStat::CACHE_CORRUPT); } return; } else { // LLAssetStorage metric: Successful Request - S32 size = mVFS->getSize(asset_id, asset_type); + S32 size = LLVFile::getFileSize(asset_id, asset_type); const char *message = "Added to upload queue"; reportMetric( asset_id, asset_type, LLStringUtil::null, LLUUID::null, size, MR_OKAY, __FILE__, __LINE__, message ); @@ -200,7 +193,7 @@ void LLViewerAssetStorage::storeAssetData( } } - // Read the data from the VFS if it'll fit in this packet. + // Read the data from the cache if it'll fit in this packet. if (asset_size + 100 < MTUBYTES) { BOOL res = vfile.read(buffer, asset_size); /* Flawfinder: ignore */ @@ -213,14 +206,11 @@ void LLViewerAssetStorage::storeAssetData( } else { - LL_WARNS("AssetStorage") << "Probable corruption in VFS file, aborting store asset data" << LL_ENDL; - - // LLAssetStorage metric: VFS corrupt - bogus size - reportMetric( asset_id, asset_type, LLStringUtil::null, LLUUID::null, asset_size, MR_VFS_CORRUPTION, __FILE__, __LINE__, "VFS corruption" ); + LL_WARNS("AssetStorage") << "Probable corruption in cache file, aborting store asset data" << LL_ENDL; if (callback) { - callback(asset_id, user_data, LL_ERR_ASSET_REQUEST_NONEXISTENT_FILE, LLExtStat::VFS_CORRUPT); + callback(asset_id, user_data, LL_ERR_ASSET_REQUEST_NONEXISTENT_FILE, LLExtStat::CACHE_CORRUPT); } return; } @@ -243,8 +233,7 @@ void LLViewerAssetStorage::storeAssetData( else { LL_WARNS("AssetStorage") << "AssetStorage: attempt to upload non-existent vfile " << asset_id << ":" << LLAssetType::lookup(asset_type) << LL_ENDL; - // LLAssetStorage metric: Zero size VFS - reportMetric( asset_id, asset_type, LLStringUtil::null, LLUUID::null, 0, MR_ZERO_SIZE, __FILE__, __LINE__, "The file didn't exist or was zero length (VFS - can't tell which)" ); + reportMetric( asset_id, asset_type, LLStringUtil::null, LLUUID::null, 0, MR_ZERO_SIZE, __FILE__, __LINE__, "The file didn't exist or was zero length (cache - can't tell which)" ); if (callback) { callback(asset_id, user_data, LL_ERR_ASSET_REQUEST_NONEXISTENT_FILE, LLExtStat::NONEXISTENT_FILE); @@ -277,7 +266,6 @@ void LLViewerAssetStorage::storeAssetData( if(filename.empty()) { // LLAssetStorage metric: no filename - reportMetric( LLUUID::null, asset_type, LLStringUtil::null, LLUUID::null, 0, MR_VFS_CORRUPTION, __FILE__, __LINE__, "Filename missing" ); LL_ERRS() << "No filename specified" << LL_ENDL; return; } @@ -302,7 +290,7 @@ void LLViewerAssetStorage::storeAssetData( legacy->mUpCallback = callback; legacy->mUserData = user_data; - LLVFile file(mVFS, asset_id, asset_type, LLVFile::WRITE); + LLVFile file(asset_id, asset_type, LLVFile::WRITE); file.setMaxSize(size); @@ -538,7 +526,7 @@ void LLViewerAssetStorage::assetRequestCoro( // case. LLUUID temp_id; temp_id.generate(); - LLVFile vf(gAssetStorage->mVFS, temp_id, atype, LLVFile::WRITE); + LLVFile vf(temp_id, atype, LLVFile::WRITE); vf.setMaxSize(size); req->mBytesFetched = size; if (!vf.write(raw.data(),size)) @@ -546,13 +534,13 @@ void LLViewerAssetStorage::assetRequestCoro( // TODO asset-http: handle error LL_WARNS("ViewerAsset") << "Failure in vf.write()" << LL_ENDL; result_code = LL_ERR_ASSET_REQUEST_FAILED; - ext_status = LLExtStat::VFS_CORRUPT; + ext_status = LLExtStat::CACHE_CORRUPT; } else if (!vf.rename(uuid, atype)) { LL_WARNS("ViewerAsset") << "rename failed" << LL_ENDL; result_code = LL_ERR_ASSET_REQUEST_FAILED; - ext_status = LLExtStat::VFS_CORRUPT; + ext_status = LLExtStat::CACHE_CORRUPT; } else { diff --git a/indra/newview/llviewerassetstorage.h b/indra/newview/llviewerassetstorage.h index ef01d179b7..5d3f01fbb5 100644 --- a/indra/newview/llviewerassetstorage.h +++ b/indra/newview/llviewerassetstorage.h @@ -37,11 +37,9 @@ class LLViewerAssetRequest; class LLViewerAssetStorage : public LLAssetStorage { public: - LLViewerAssetStorage(LLMessageSystem *msg, LLXferManager *xfer, - LLVFS *vfs, LLVFS *static_vfs, const LLHost &upstream_host); + LLViewerAssetStorage(LLMessageSystem *msg, LLXferManager *xfer, const LLHost &upstream_host); - LLViewerAssetStorage(LLMessageSystem *msg, LLXferManager *xfer, - LLVFS *vfs, LLVFS *static_vfs); + LLViewerAssetStorage(LLMessageSystem *msg, LLXferManager *xfer); ~LLViewerAssetStorage(); diff --git a/indra/newview/llviewerassetupload.cpp b/indra/newview/llviewerassetupload.cpp index d53cc3f745..d14b6b0568 100644 --- a/indra/newview/llviewerassetupload.cpp +++ b/indra/newview/llviewerassetupload.cpp @@ -466,13 +466,13 @@ LLSD LLNewFileResourceUploadInfo::exportTempFile() setAssetType(assetType); - // copy this file into the vfs for upload + // copy this file into the cache for upload S32 file_size; LLAPRFile infile; infile.open(filename, LL_APR_RB, NULL, &file_size); if (infile.getFileHandle()) { - LLVFile file(gVFS, getAssetId(), assetType, LLVFile::WRITE); + LLVFile file(getAssetId(), assetType, LLVFile::WRITE); file.setMaxSize(file_size); @@ -506,7 +506,7 @@ LLBufferedAssetUploadInfo::LLBufferedAssetUploadInfo(LLUUID itemId, LLAssetType: mContents(buffer), mInvnFinishFn(finish), mTaskFinishFn(nullptr), - mStoredToVFS(false) + mStoredToCache(false) { setItemId(itemId); setAssetType(assetType); @@ -520,7 +520,7 @@ LLBufferedAssetUploadInfo::LLBufferedAssetUploadInfo(LLUUID itemId, LLPointerrenameFile(getAssetId(), assetType, newAssetId, assetType); + LLVFile::renameFile(getAssetId(), assetType, newAssetId, assetType); } if (mTaskUpload) diff --git a/indra/newview/llviewerassetupload.h b/indra/newview/llviewerassetupload.h index d9eacf3167..e56ba7d8f7 100644 --- a/indra/newview/llviewerassetupload.h +++ b/indra/newview/llviewerassetupload.h @@ -197,7 +197,7 @@ private: std::string mContents; invnUploadFinish_f mInvnFinishFn; taskUploadFinish_f mTaskFinishFn; - bool mStoredToVFS; + bool mStoredToCache; }; //------------------------------------------------------------------------- diff --git a/indra/newview/llviewermedia_streamingaudio.cpp b/indra/newview/llviewermedia_streamingaudio.cpp index 3ccf3070ab..d3e24aece5 100644 --- a/indra/newview/llviewermedia_streamingaudio.cpp +++ b/indra/newview/llviewermedia_streamingaudio.cpp @@ -33,10 +33,8 @@ #include "llviewermedia_streamingaudio.h" #include "llmimetypes.h" -#include "llvfs.h" #include "lldir.h" - LLStreamingAudio_MediaPlugins::LLStreamingAudio_MediaPlugins() : mMediaPlugin(NULL), mGain(1.0) diff --git a/indra/newview/llviewermenufile.cpp b/indra/newview/llviewermenufile.cpp index cd48b1e8e7..cbf490d68d 100644 --- a/indra/newview/llviewermenufile.cpp +++ b/indra/newview/llviewermenufile.cpp @@ -54,7 +54,6 @@ #include "llviewertexturelist.h" #include "lluictrlfactory.h" #include "llvfile.h" -#include "llvfs.h" #include "llviewerinventory.h" #include "llviewermenu.h" // gMenuHolder #include "llviewerparcelmgr.h" diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 06a8ebbe89..2d41560441 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -45,7 +45,6 @@ #include "lltoastnotifypanel.h" #include "lltransactionflags.h" #include "llvfile.h" -#include "llvfs.h" #include "llxfermanager.h" #include "mean_collision_data.h" @@ -6809,16 +6808,15 @@ void process_covenant_reply(LLMessageSystem* msg, void**) } } -void onCovenantLoadComplete(LLVFS *vfs, - const LLUUID& asset_uuid, - LLAssetType::EType type, - void* user_data, S32 status, LLExtStat ext_status) +void onCovenantLoadComplete(const LLUUID& asset_uuid, + LLAssetType::EType type, + void* user_data, S32 status, LLExtStat ext_status) { LL_DEBUGS("Messaging") << "onCovenantLoadComplete()" << LL_ENDL; std::string covenant_text; if(0 == status) { - LLVFile file(vfs, asset_uuid, type, LLVFile::READ); + LLVFile file(asset_uuid, type, LLVFile::READ); S32 file_length = file.getSize(); diff --git a/indra/newview/llviewermessage.h b/indra/newview/llviewermessage.h index 78829a6a56..1e5a69ae13 100644 --- a/indra/newview/llviewermessage.h +++ b/indra/newview/llviewermessage.h @@ -47,7 +47,6 @@ class LLInventoryObject; class LLInventoryItem; class LLMeanCollisionData; class LLMessageSystem; -class LLVFS; class LLViewerObject; class LLViewerRegion; @@ -189,8 +188,7 @@ void process_script_dialog(LLMessageSystem* msg, void**); void process_load_url(LLMessageSystem* msg, void**); void process_script_teleport_request(LLMessageSystem* msg, void**); void process_covenant_reply(LLMessageSystem* msg, void**); -void onCovenantLoadComplete(LLVFS *vfs, - const LLUUID& asset_uuid, +void onCovenantLoadComplete(const LLUUID& asset_uuid, LLAssetType::EType type, void* user_data, S32 status, LLExtStat ext_status); diff --git a/indra/newview/llviewerprecompiledheaders.h b/indra/newview/llviewerprecompiledheaders.h index bbbacce8fa..e378c2448a 100644 --- a/indra/newview/llviewerprecompiledheaders.h +++ b/indra/newview/llviewerprecompiledheaders.h @@ -101,7 +101,6 @@ #include "v4math.h" #include "xform.h" -// Library includes from llvfs #include "lldir.h" // Library includes from llmessage project diff --git a/indra/newview/llviewerstats.cpp b/indra/newview/llviewerstats.cpp index 3fd709b3ce..470704d84e 100644 --- a/indra/newview/llviewerstats.cpp +++ b/indra/newview/llviewerstats.cpp @@ -144,7 +144,6 @@ LLTrace::SampleStatHandle<> FPS_SAMPLE("fpssample"), VISIBLE_AVATARS("visibleavatars", "Visible Avatars"), SHADER_OBJECTS("shaderobjects", "Object Shaders"), DRAW_DISTANCE("drawdistance", "Draw Distance"), - PENDING_VFS_OPERATIONS("vfspendingoperations"), WINDOW_WIDTH("windowwidth", "Window width"), WINDOW_HEIGHT("windowheight", "Window height"); @@ -382,7 +381,6 @@ void update_statistics() F64Bits layer_bits = gVLManager.getLandBits() + gVLManager.getWindBits() + gVLManager.getCloudBits(); add(LLStatViewer::LAYERS_NETWORK_DATA_RECEIVED, layer_bits); add(LLStatViewer::OBJECT_NETWORK_DATA_RECEIVED, gObjectData); - //sample(LLStatViewer::PENDING_VFS_OPERATIONS, LLVFile::getVFSThread()->getPending()); add(LLStatViewer::ASSET_UDP_DATA_RECEIVED, F64Bits(gTransferManager.getTransferBitsIn(LLTCT_ASSET))); gTransferManager.resetTransferBitsIn(LLTCT_ASSET); diff --git a/indra/newview/llviewerstats.h b/indra/newview/llviewerstats.h index d8d92d61d3..299f045e7b 100644 --- a/indra/newview/llviewerstats.h +++ b/indra/newview/llviewerstats.h @@ -186,7 +186,6 @@ extern LLTrace::SampleStatHandle<> FPS_SAMPLE, VISIBLE_AVATARS, SHADER_OBJECTS, DRAW_DISTANCE, - PENDING_VFS_OPERATIONS, WINDOW_WIDTH, WINDOW_HEIGHT; diff --git a/indra/newview/llviewertexlayer.cpp b/indra/newview/llviewertexlayer.cpp index 7f7d190b92..b1cbdc4443 100644 --- a/indra/newview/llviewertexlayer.cpp +++ b/indra/newview/llviewertexlayer.cpp @@ -32,7 +32,6 @@ #include "llimagej2c.h" #include "llnotificationsutil.h" #include "llvfile.h" -#include "llvfs.h" #include "llviewerregion.h" #include "llglslshader.h" #include "llvoavatarself.h" diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp index a2cec9a613..8782f282bf 100644 --- a/indra/newview/llviewertexture.cpp +++ b/indra/newview/llviewertexture.cpp @@ -40,7 +40,6 @@ #include "llimagetga.h" #include "llstl.h" #include "llvfile.h" -#include "llvfs.h" #include "message.h" #include "lltimer.h" diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp index 561319ca5d..312a8726ca 100644 --- a/indra/newview/llviewertexturelist.cpp +++ b/indra/newview/llviewertexturelist.cpp @@ -41,9 +41,7 @@ #include "llsdserialize.h" #include "llsys.h" -#include "llvfs.h" #include "llvfile.h" -#include "llvfsthread.h" #include "llxmltree.h" #include "message.h" diff --git a/indra/newview/llviewerwearable.cpp b/indra/newview/llviewerwearable.cpp index 2d7a0f920f..2201a5f70b 100644 --- a/indra/newview/llviewerwearable.cpp +++ b/indra/newview/llviewerwearable.cpp @@ -107,7 +107,6 @@ LLWearable::EImportResult LLViewerWearable::importStream( std::istream& input_st // Shouldn't really log the asset id for security reasons, but // we need it in this case. LL_WARNS() << "Bad Wearable asset header: " << mAssetID << LL_ENDL; - //gVFS->dumpMap(); return result; } diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index d567623ac0..efa0133a9a 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -1134,7 +1134,6 @@ void LLVOAvatar::initInstance() //------------------------------------------------------------------------- if (LLCharacter::sInstances.size() == 1) { - LLKeyframeMotion::setVFS(gStaticVFS); registerMotion( ANIM_AGENT_DO_NOT_DISTURB, LLNullMotion::create ); registerMotion( ANIM_AGENT_CROUCH, LLKeyframeStandMotion::create ); registerMotion( ANIM_AGENT_CROUCHWALK, LLKeyframeWalkMotion::create ); diff --git a/indra/newview/llvovolume.h b/indra/newview/llvovolume.h index de00ef494e..e6566e2ce1 100644 --- a/indra/newview/llvovolume.h +++ b/indra/newview/llvovolume.h @@ -218,10 +218,9 @@ public: void updateSculptTexture(); void setIndexInTex(U32 ch, S32 index) { mIndexInTex[ch] = index ;} void sculpt(); - static void rebuildMeshAssetCallback(LLVFS *vfs, - const LLUUID& asset_uuid, - LLAssetType::EType type, - void* user_data, S32 status, LLExtStat ext_status); + static void rebuildMeshAssetCallback(const LLUUID& asset_uuid, + LLAssetType::EType type, + void* user_data, S32 status, LLExtStat ext_status); void updateRelativeXform(bool force_identity = false); /*virtual*/ BOOL updateGeometry(LLDrawable *drawable); diff --git a/indra/newview/skins/default/xui/en/floater_scene_load_stats.xml b/indra/newview/skins/default/xui/en/floater_scene_load_stats.xml index 62cce3a1e3..35d4385487 100644 --- a/indra/newview/skins/default/xui/en/floater_scene_load_stats.xml +++ b/indra/newview/skins/default/xui/en/floater_scene_load_stats.xml @@ -206,12 +206,6 @@ bar_max="1024.f" tick_spacing="128.f" precision="1" - show_bar="false"/> - diff --git a/indra/newview/skins/default/xui/en/floater_stats.xml b/indra/newview/skins/default/xui/en/floater_stats.xml index e4f735740b..f9d44b2c69 100644 --- a/indra/newview/skins/default/xui/en/floater_stats.xml +++ b/indra/newview/skins/default/xui/en/floater_stats.xml @@ -198,10 +198,6 @@ stat="messagedataout" decimal_digits="1" show_history="false"/> - diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml index 1bfac6aeb7..9bd0f24d87 100644 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -17,7 +17,6 @@ Loading [APP_NAME]... Clearing cache... Initializing texture cache... - Initializing VFS... Graphics initialization failed. Please update your graphics driver! @@ -56,7 +55,6 @@ LOD factor: [LOD_FACTOR] Render quality: [RENDER_QUALITY] Advanced Lighting Model: [GPU_SHADERS] Texture memory: [TEXTURE_MEMORY]MB -VFS (cache) creation time: [VFS_TIME] HiDPI display mode: [HIDPI] diff --git a/indra/test/CMakeLists.txt b/indra/test/CMakeLists.txt index 87536e146b..f8e701794c 100644 --- a/indra/test/CMakeLists.txt +++ b/indra/test/CMakeLists.txt @@ -8,7 +8,7 @@ include(LLCoreHttp) include(LLInventory) include(LLMath) include(LLMessage) -include(LLVFS) +include(LLCache) include(LLXML) include(Linking) include(Tut) @@ -23,7 +23,7 @@ include_directories( ${LLMATH_INCLUDE_DIRS} ${LLMESSAGE_INCLUDE_DIRS} ${LLINVENTORY_INCLUDE_DIRS} - ${LLVFS_INCLUDE_DIRS} + ${LLCACHE_INCLUDE_DIRS} ${LLXML_INCLUDE_DIRS} ${LSCRIPT_INCLUDE_DIRS} ${GOOGLEMOCK_INCLUDE_DIRS} @@ -89,7 +89,7 @@ target_link_libraries(lltest ${LLINVENTORY_LIBRARIES} ${LLMESSAGE_LIBRARIES} ${LLMATH_LIBRARIES} - ${LLVFS_LIBRARIES} + ${LLCACHE_LIBRARIES} ${LLXML_LIBRARIES} ${LSCRIPT_LIBRARIES} ${LLCOMMON_LIBRARIES} diff --git a/indra/win_crash_logger/CMakeLists.txt b/indra/win_crash_logger/CMakeLists.txt index 86aa655f03..811ec00a3d 100644 --- a/indra/win_crash_logger/CMakeLists.txt +++ b/indra/win_crash_logger/CMakeLists.txt @@ -8,7 +8,7 @@ include(LLCoreHttp) include(LLCrashLogger) include(LLMath) include(LLMessage) -include(LLVFS) +include(LLCache) include(LLWindow) include(LLXML) include(Linking) @@ -23,7 +23,7 @@ include_directories( ${LLMATH_INCLUDE_DIRS} ${LLWINDOW_INCLUDE_DIRS} ${LLXML_INCLUDE_DIRS} - ${LLVFS_INCLUDE_DIRS} + ${LLCACHE_INCLUDE_DIRS} ${BREAKPAD_INCLUDE_DIRECTORIES} ) include_directories(SYSTEM @@ -76,7 +76,7 @@ target_link_libraries(windows-crash-logger ${BREAKPAD_EXCEPTION_HANDLER_LIBRARIES} ${LLCRASHLOGGER_LIBRARIES} ${LLWINDOW_LIBRARIES} - ${LLVFS_LIBRARIES} + ${LLCACHE_LIBRARIES} ${LLXML_LIBRARIES} ${LLMESSAGE_LIBRARIES} ${LLMATH_LIBRARIES} From 2e6f5164116e084fe35f952180c3f7092ad8350f Mon Sep 17 00:00:00 2001 From: Callum Prentice Date: Wed, 16 Sep 2020 21:12:53 -0700 Subject: [PATCH 04/85] Renamed the references to LLVFile and llvfile.* source code plus cmake scripts to use a different name - lldiskcache - since that more closely resembles what it is (or will be) now that the VFA is no more --- indra/llappearance/lltexlayer.cpp | 2 +- indra/llaudio/llaudiodecodemgr.cpp | 14 +- indra/llaudio/llaudioengine.cpp | 4 +- indra/llcache/CMakeLists.txt | 6 +- .../llcache/{llvfile.cpp => lldiskcache.cpp} | 66 ++--- indra/llcache/{llvfile.h => lldiskcache.h} | 14 +- indra/llcache/llpidlock.cpp | 276 ------------------ indra/llcache/llpidlock.h | 60 ---- indra/llcharacter/llkeyframemotion.cpp | 6 +- indra/llcrashlogger/llcrashlock.h | 2 +- indra/llmessage/llassetstorage.cpp | 28 +- indra/llmessage/llcorehttputil.cpp | 4 +- indra/llmessage/lltransfersourceasset.cpp | 6 +- indra/llmessage/lltransfersourceasset.h | 2 +- indra/llmessage/lltransfertargetvfile.cpp | 8 +- indra/llmessage/lltransfertargetvfile.h | 4 +- indra/llmessage/llxfer_vfile.cpp | 22 +- indra/llmessage/llxfer_vfile.h | 4 +- indra/llui/llviewereventrecorder.h | 2 +- indra/newview/llappviewer.cpp | 8 +- indra/newview/llcompilequeue.cpp | 2 +- indra/newview/llfloaterauction.cpp | 6 +- indra/newview/llfloaterbvhpreview.cpp | 4 +- indra/newview/llfloatermodelpreview.cpp | 2 +- indra/newview/llfloaterregioninfo.cpp | 4 +- indra/newview/llfloaterreporter.cpp | 4 +- indra/newview/llfloatertos.cpp | 2 +- indra/newview/llgesturemgr.cpp | 4 +- indra/newview/lllandmarklist.cpp | 4 +- indra/newview/llmeshrepository.cpp | 22 +- indra/newview/lloutfitgallery.cpp | 2 +- indra/newview/llpostcard.cpp | 2 +- indra/newview/llpreviewgesture.cpp | 6 +- indra/newview/llpreviewnotecard.cpp | 10 +- indra/newview/llpreviewscript.cpp | 6 +- indra/newview/llsettingsvo.cpp | 4 +- indra/newview/llsnapshotlivepreview.cpp | 4 +- indra/newview/llviewerassetstorage.cpp | 12 +- indra/newview/llviewerassetstorage.h | 2 +- indra/newview/llviewerassetupload.cpp | 8 +- indra/newview/llviewermenufile.cpp | 2 +- indra/newview/llviewermessage.cpp | 4 +- indra/newview/llviewerstats.cpp | 2 +- indra/newview/llviewertexlayer.cpp | 2 +- indra/newview/llviewertexture.cpp | 2 +- indra/newview/llviewertexture.h | 2 +- indra/newview/llviewertexturelist.cpp | 2 +- 47 files changed, 163 insertions(+), 501 deletions(-) rename indra/llcache/{llvfile.cpp => lldiskcache.cpp} (81%) rename indra/llcache/{llvfile.h => lldiskcache.h} (90%) delete mode 100644 indra/llcache/llpidlock.cpp delete mode 100644 indra/llcache/llpidlock.h diff --git a/indra/llappearance/lltexlayer.cpp b/indra/llappearance/lltexlayer.cpp index 2a7e5f3ddb..7360c1acd7 100644 --- a/indra/llappearance/lltexlayer.cpp +++ b/indra/llappearance/lltexlayer.cpp @@ -33,7 +33,7 @@ #include "llimagej2c.h" #include "llimagetga.h" #include "lldir.h" -#include "llvfile.h" +#include "lldiskcache.h" #include "lltexlayerparams.h" #include "lltexturemanagerbridge.h" #include "lllocaltextureobject.h" diff --git a/indra/llaudio/llaudiodecodemgr.cpp b/indra/llaudio/llaudiodecodemgr.cpp index d2c4163280..fcffea42a4 100644 --- a/indra/llaudio/llaudiodecodemgr.cpp +++ b/indra/llaudio/llaudiodecodemgr.cpp @@ -29,7 +29,7 @@ #include "llaudioengine.h" #include "lllfsthread.h" -#include "llvfile.h" +#include "lldiskcache.h" #include "llstring.h" #include "lldir.h" #include "llendianswizzle.h" @@ -93,14 +93,14 @@ protected: std::string mOutFilename; LLLFSThread::handle_t mFileHandle; - LLVFile *mInFilep; + LLDiskCache *mInFilep; OggVorbis_File mVF; S32 mCurrentSection; }; size_t cache_read(void *ptr, size_t size, size_t nmemb, void *datasource) { - LLVFile *file = (LLVFile *)datasource; + LLDiskCache *file = (LLDiskCache *)datasource; if (file->read((U8*)ptr, (S32)(size * nmemb))) /*Flawfinder: ignore*/ { @@ -115,7 +115,7 @@ size_t cache_read(void *ptr, size_t size, size_t nmemb, void *datasource) S32 cache_seek(void *datasource, ogg_int64_t offset, S32 whence) { - LLVFile *file = (LLVFile *)datasource; + LLDiskCache *file = (LLDiskCache *)datasource; // cache has 31-bit files if (offset > S32_MAX) @@ -151,14 +151,14 @@ S32 cache_seek(void *datasource, ogg_int64_t offset, S32 whence) S32 cache_close (void *datasource) { - LLVFile *file = (LLVFile *)datasource; + LLDiskCache *file = (LLDiskCache *)datasource; delete file; return 0; } long cache_tell (void *datasource) { - LLVFile *file = (LLVFile *)datasource; + LLDiskCache *file = (LLDiskCache *)datasource; return file->tell(); } @@ -198,7 +198,7 @@ BOOL LLVorbisDecodeState::initDecode() LL_DEBUGS("AudioEngine") << "Initing decode from vfile: " << mUUID << LL_ENDL; - mInFilep = new LLVFile(mUUID, LLAssetType::AT_SOUND); + mInFilep = new LLDiskCache(mUUID, LLAssetType::AT_SOUND); if (!mInFilep || !mInFilep->getSize()) { LL_WARNS("AudioEngine") << "unable to open vorbis source vfile for reading" << LL_ENDL; diff --git a/indra/llaudio/llaudioengine.cpp b/indra/llaudio/llaudioengine.cpp index 9c8bd3225b..9dd752f492 100644 --- a/indra/llaudio/llaudioengine.cpp +++ b/indra/llaudio/llaudioengine.cpp @@ -35,7 +35,7 @@ #include "sound_ids.h" // temporary hack for min/max distances -#include "llvfile.h" +#include "lldiskcache.h" #include "lldir.h" #include "llaudiodecodemgr.h" #include "llassetstorage.h" @@ -1015,7 +1015,7 @@ bool LLAudioEngine::hasDecodedFile(const LLUUID &uuid) bool LLAudioEngine::hasLocalFile(const LLUUID &uuid) { // See if it's in the cache. - bool have_local = LLVFile::getExists(uuid, LLAssetType::AT_SOUND); + bool have_local = LLDiskCache::getExists(uuid, LLAssetType::AT_SOUND); LL_DEBUGS("AudioEngine") << "sound uuid " << uuid << " exists in cache" << LL_ENDL; return have_local; } diff --git a/indra/llcache/CMakeLists.txt b/indra/llcache/CMakeLists.txt index ab84bd96a0..379e3ebdbf 100644 --- a/indra/llcache/CMakeLists.txt +++ b/indra/llcache/CMakeLists.txt @@ -15,8 +15,7 @@ set(llcache_SOURCE_FILES lldir.cpp lldiriterator.cpp lllfsthread.cpp - llpidlock.cpp - llvfile.cpp + lldiskcache.cpp ) set(llcache_HEADER_FILES @@ -26,8 +25,7 @@ set(llcache_HEADER_FILES lldirguard.h lldiriterator.h lllfsthread.h - llpidlock.h - llvfile.h + lldiskcache.h ) if (DARWIN) diff --git a/indra/llcache/llvfile.cpp b/indra/llcache/lldiskcache.cpp similarity index 81% rename from indra/llcache/llvfile.cpp rename to indra/llcache/lldiskcache.cpp index be753244c0..af93049e07 100644 --- a/indra/llcache/llvfile.cpp +++ b/indra/llcache/lldiskcache.cpp @@ -1,5 +1,5 @@ /** - * @file llvfile.cpp + * @file lldiskcache.cpp * @brief Implementation of virtual file * * $LicenseInfo:firstyear=2002&license=viewerlgpl$ @@ -26,7 +26,7 @@ #include "linden_common.h" -#include "llvfile.h" +#include "lldiskcache.h" #include "llerror.h" #include "llthread.h" @@ -37,14 +37,14 @@ #include #include "lldir.h" -const S32 LLVFile::READ = 0x00000001; -const S32 LLVFile::WRITE = 0x00000002; -const S32 LLVFile::READ_WRITE = 0x00000003; // LLVFile::READ & LLVFile::WRITE -const S32 LLVFile::APPEND = 0x00000006; // 0x00000004 & LLVFile::WRITE +const S32 LLDiskCache::READ = 0x00000001; +const S32 LLDiskCache::WRITE = 0x00000002; +const S32 LLDiskCache::READ_WRITE = 0x00000003; // LLDiskCache::READ & LLDiskCache::WRITE +const S32 LLDiskCache::APPEND = 0x00000006; // 0x00000004 & LLDiskCache::WRITE static LLTrace::BlockTimerStatHandle FTM_VFILE_WAIT("VFile Wait"); -LLVFile::LLVFile(const LLUUID &file_id, const LLAssetType::EType file_type, S32 mode) +LLDiskCache::LLDiskCache(const LLUUID &file_id, const LLAssetType::EType file_type, S32 mode) { mFileType = file_type; mFileID = file_id; @@ -54,7 +54,7 @@ LLVFile::LLVFile(const LLUUID &file_id, const LLAssetType::EType file_type, S32 mMode = mode; } -LLVFile::~LLVFile() +LLDiskCache::~LLDiskCache() { } @@ -124,7 +124,7 @@ const std::string idToFilepath(const std::string id, LLAssetType::EType at) } // static -bool LLVFile::getExists(const LLUUID &file_id, const LLAssetType::EType file_type) +bool LLDiskCache::getExists(const LLUUID &file_id, const LLAssetType::EType file_type) { std::string id_str; file_id.toString(id_str); @@ -140,7 +140,7 @@ bool LLVFile::getExists(const LLUUID &file_id, const LLAssetType::EType file_typ } // static -bool LLVFile::removeFile(const LLUUID &file_id, const LLAssetType::EType file_type) +bool LLDiskCache::removeFile(const LLUUID &file_id, const LLAssetType::EType file_type) { std::string id_str; file_id.toString(id_str); @@ -152,7 +152,7 @@ bool LLVFile::removeFile(const LLUUID &file_id, const LLAssetType::EType file_ty } // static -bool LLVFile::renameFile(const LLUUID &old_file_id, const LLAssetType::EType old_file_type, +bool LLDiskCache::renameFile(const LLUUID &old_file_id, const LLAssetType::EType old_file_type, const LLUUID &new_file_id, const LLAssetType::EType new_file_type) { std::string old_id_str; @@ -175,7 +175,7 @@ bool LLVFile::renameFile(const LLUUID &old_file_id, const LLAssetType::EType old } // static -S32 LLVFile::getFileSize(const LLUUID &file_id, const LLAssetType::EType file_type) +S32 LLDiskCache::getFileSize(const LLUUID &file_id, const LLAssetType::EType file_type) { std::string id_str; file_id.toString(id_str); @@ -192,7 +192,7 @@ S32 LLVFile::getFileSize(const LLUUID &file_id, const LLAssetType::EType file_ty return file_size; } -BOOL LLVFile::read(U8 *buffer, S32 bytes, BOOL async, F32 priority) +BOOL LLDiskCache::read(U8 *buffer, S32 bytes, BOOL async, F32 priority) { BOOL success = TRUE; @@ -232,7 +232,7 @@ BOOL LLVFile::read(U8 *buffer, S32 bytes, BOOL async, F32 priority) return success; } -BOOL LLVFile::isReadComplete() +BOOL LLDiskCache::isReadComplete() { if (mReadComplete) { @@ -242,17 +242,17 @@ BOOL LLVFile::isReadComplete() return FALSE; } -S32 LLVFile::getLastBytesRead() +S32 LLDiskCache::getLastBytesRead() { return mBytesRead; } -BOOL LLVFile::eof() +BOOL LLDiskCache::eof() { return mPosition >= getSize(); } -BOOL LLVFile::write(const U8 *buffer, S32 bytes) +BOOL LLDiskCache::write(const U8 *buffer, S32 bytes) { std::string id_str; mFileID.toString(id_str); @@ -287,14 +287,14 @@ BOOL LLVFile::write(const U8 *buffer, S32 bytes) } //static -BOOL LLVFile::writeFile(const U8 *buffer, S32 bytes, const LLUUID &uuid, LLAssetType::EType type) +BOOL LLDiskCache::writeFile(const U8 *buffer, S32 bytes, const LLUUID &uuid, LLAssetType::EType type) { - LLVFile file(uuid, type, LLVFile::WRITE); + LLDiskCache file(uuid, type, LLDiskCache::WRITE); file.setMaxSize(bytes); return file.write(buffer, bytes); } -BOOL LLVFile::seek(S32 offset, S32 origin) +BOOL LLDiskCache::seek(S32 offset, S32 origin) { if (-1 == origin) { @@ -324,32 +324,32 @@ BOOL LLVFile::seek(S32 offset, S32 origin) return TRUE; } -S32 LLVFile::tell() const +S32 LLDiskCache::tell() const { return mPosition; } -S32 LLVFile::getSize() +S32 LLDiskCache::getSize() { - return LLVFile::getFileSize(mFileID, mFileType); + return LLDiskCache::getFileSize(mFileID, mFileType); } -S32 LLVFile::getMaxSize() +S32 LLDiskCache::getMaxSize() { // offer up a huge size since we don't care what the max is return INT_MAX; } -BOOL LLVFile::setMaxSize(S32 size) +BOOL LLDiskCache::setMaxSize(S32 size) { // we don't care what the max size is so we do nothing // and return true to indicate all was okay return TRUE; } -BOOL LLVFile::rename(const LLUUID &new_id, const LLAssetType::EType new_type) +BOOL LLDiskCache::rename(const LLUUID &new_id, const LLAssetType::EType new_type) { - LLVFile::renameFile(mFileID, mFileType, new_id, new_type); + LLDiskCache::renameFile(mFileID, mFileType, new_id, new_type); mFileID = new_id; mFileType = new_type; @@ -357,31 +357,31 @@ BOOL LLVFile::rename(const LLUUID &new_id, const LLAssetType::EType new_type) return TRUE; } -BOOL LLVFile::remove() +BOOL LLDiskCache::remove() { - LLVFile::removeFile(mFileID, mFileType); + LLDiskCache::removeFile(mFileID, mFileType); return TRUE; } // static -void LLVFile::initClass() +void LLDiskCache::initClass() { } // static -void LLVFile::cleanupClass() +void LLDiskCache::cleanupClass() { } -bool LLVFile::isLocked() +bool LLDiskCache::isLocked() { // I don't think we care about this test since there is no locking // TODO: remove this function and calling sites? return FALSE; } -void LLVFile::waitForLock() +void LLDiskCache::waitForLock() { // TODO: remove this function and calling sites? } diff --git a/indra/llcache/llvfile.h b/indra/llcache/lldiskcache.h similarity index 90% rename from indra/llcache/llvfile.h rename to indra/llcache/lldiskcache.h index 30130df340..7ad06a8689 100644 --- a/indra/llcache/llvfile.h +++ b/indra/llcache/lldiskcache.h @@ -1,5 +1,5 @@ /** - * @file llvfile.h + * @file lldiskcacke.h * @brief Definition of virtual file * * $LicenseInfo:firstyear=2002&license=viewerlgpl$ @@ -24,17 +24,17 @@ * $/LicenseInfo$ */ -#ifndef LL_LLVFILE_H -#define LL_LLVFILE_H +#ifndef LL_LLDISKCACHE_H +#define LL_LLDISKCACHE_H #include "lluuid.h" #include "llassettype.h" -class LLVFile +class LLDiskCache { public: - LLVFile(const LLUUID &file_id, const LLAssetType::EType file_type, S32 mode = LLVFile::READ); - ~LLVFile(); + LLDiskCache(const LLUUID &file_id, const LLAssetType::EType file_type, S32 mode = LLDiskCache::READ); + ~LLDiskCache(); BOOL read(U8 *buffer, S32 bytes, BOOL async = FALSE, F32 priority = 128.f); /* Flawfinder: ignore */ BOOL isReadComplete(); @@ -79,4 +79,4 @@ protected: S32 mBytesRead; }; -#endif +#endif // LL_LLDISKCACHE_H diff --git a/indra/llcache/llpidlock.cpp b/indra/llcache/llpidlock.cpp deleted file mode 100644 index f770e93d45..0000000000 --- a/indra/llcache/llpidlock.cpp +++ /dev/null @@ -1,276 +0,0 @@ -/** - * @file llformat.cpp - * @date January 2007 - * @brief string formatting utility - * - * $LicenseInfo:firstyear=2007&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "linden_common.h" - -#include "llapr.h" // thread-related functions -#include "llpidlock.h" -#include "lldir.h" -#include "llsd.h" -#include "llsdserialize.h" -#include "llnametable.h" -#include "llframetimer.h" - -#if LL_WINDOWS //For windows platform. - -#include - -bool isProcessAlive(U32 pid) -{ - return (bool) GetProcessVersion((DWORD)pid); -} - -#else //Everyone Else -bool isProcessAlive(U32 pid) -{ - return (bool) kill( (pid_t)pid, 0); -} -#endif //Everyone else. - - - -class LLPidLockFile -{ - public: - LLPidLockFile( ) : - mAutosave(false), - mSaving(false), - mWaiting(false), - mPID(getpid()), - mNameTable(NULL), - mClean(true) - { - mLockName = gDirUtilp->getTempDir() + gDirUtilp->getDirDelimiter() + "savelock"; - } - bool requestLock(LLNameTable *name_table, bool autosave, - bool force_immediate=FALSE, F32 timeout=300.0); - bool checkLock(); - void releaseLock(); - - private: - void writeLockFile(LLSD pids); - public: - static LLPidLockFile& instance(); // return the singleton black list file - - bool mAutosave; - bool mSaving; - bool mWaiting; - LLFrameTimer mTimer; - U32 mPID; - std::string mLockName; - std::string mSaveName; - LLSD mPIDS_sd; - LLNameTable *mNameTable; - bool mClean; -}; - -LLPidLockFile& LLPidLockFile::instance() -{ - static LLPidLockFile the_file; - return the_file; -} - -void LLPidLockFile::writeLockFile(LLSD pids) -{ - llofstream ofile(mLockName.c_str()); - - if (!LLSDSerialize::toXML(pids,ofile)) - { - LL_WARNS() << "Unable to write concurrent save lock file." << LL_ENDL; - } - ofile.close(); -} - -bool LLPidLockFile::requestLock(LLNameTable *name_table, bool autosave, - bool force_immediate, F32 timeout) -{ - bool readyToSave = FALSE; - - if (mSaving) return FALSE; //Bail out if we're currently saving. Will not queue another save. - - if (!mWaiting){ - mNameTable=name_table; - mAutosave = autosave; - } - - LLSD out_pids; - out_pids.append( (LLSD::Integer)mPID ); - - llifstream ifile(mLockName.c_str()); - - if (ifile.is_open()) - { //If file exists, we need to decide whether or not to continue. - if ( force_immediate - || mTimer.hasExpired() ) //Only deserialize if we REALLY need to. - { - - LLSD in_pids; - - LLSDSerialize::fromXML(in_pids, ifile); - - //Clean up any dead PIDS that might be in there. - for (LLSD::array_iterator i=in_pids.beginArray(); - i !=in_pids.endArray(); - ++i) - { - U32 stored_pid=(*i).asInteger(); - - if (isProcessAlive(stored_pid)) - { - out_pids.append( (*i) ); - } - } - - readyToSave=TRUE; - } - ifile.close(); - } - else - { - readyToSave=TRUE; - } - - if (!mWaiting) //Not presently waiting to save. Queue up. - { - mTimer.resetWithExpiry(timeout); - mWaiting=TRUE; - } - - if (readyToSave) - { //Potential race condition won't kill us. Ignore it. - writeLockFile(out_pids); - mSaving=TRUE; - } - - return readyToSave; -} - -bool LLPidLockFile::checkLock() -{ - return mWaiting; -} - -void LLPidLockFile::releaseLock() -{ - llifstream ifile(mLockName.c_str()); - LLSD in_pids; - LLSD out_pids; - bool write_file=FALSE; - - LLSDSerialize::fromXML(in_pids, ifile); - - //Clean up this PID and any dead ones. - for (LLSD::array_iterator i=in_pids.beginArray(); - i !=in_pids.endArray(); - ++i) - { - U32 stored_pid=(*i).asInteger(); - - if (stored_pid != mPID && isProcessAlive(stored_pid)) - { - out_pids.append( (*i) ); - write_file=TRUE; - } - } - ifile.close(); - - if (write_file) - { - writeLockFile(out_pids); - } - else - { - unlink(mLockName.c_str()); - } - - mSaving=FALSE; - mWaiting=FALSE; -} - -//LLPidLock - -void LLPidLock::initClass() { - (void) LLPidLockFile::instance(); -} - -bool LLPidLock::checkLock() -{ - return LLPidLockFile::instance().checkLock(); -} - -bool LLPidLock::requestLock(LLNameTable *name_table, bool autosave, - bool force_immediate, F32 timeout) -{ - return LLPidLockFile::instance().requestLock(name_table,autosave,force_immediate,timeout); -} - -void LLPidLock::releaseLock() -{ - return LLPidLockFile::instance().releaseLock(); -} - -bool LLPidLock::isClean() -{ - return LLPidLockFile::instance().mClean; -} - -//getters -LLNameTable * LLPidLock::getNameTable() -{ - return LLPidLockFile::instance().mNameTable; -} - -bool LLPidLock::getAutosave() -{ - return LLPidLockFile::instance().mAutosave; -} - -bool LLPidLock::getClean() -{ - return LLPidLockFile::instance().mClean; -} - -std::string LLPidLock::getSaveName() -{ - return LLPidLockFile::instance().mSaveName; -} - -//setters -void LLPidLock::setClean(bool clean) -{ - LLPidLockFile::instance().mClean=clean; -} - -void LLPidLock::setSaveName(std::string savename) -{ - LLPidLockFile::instance().mSaveName=savename; -} - -S32 LLPidLock::getPID() -{ - return (S32)getpid(); -} diff --git a/indra/llcache/llpidlock.h b/indra/llcache/llpidlock.h deleted file mode 100644 index 334f26bb29..0000000000 --- a/indra/llcache/llpidlock.h +++ /dev/null @@ -1,60 +0,0 @@ -/** - * @file llpidlock.h - * @brief System information debugging classes. - * - * $LicenseInfo:firstyear=2001&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_PIDLOCK_H -#define LL_PIDLOCK_H -#include "llnametable.h" - -class LLSD; -class LLFrameTimer; - -#if !LL_WINDOWS //For non-windows platforms. -#include -#endif - -namespace LLPidLock -{ - void initClass(); // { (void) LLPidLockFile::instance(); } - - bool requestLock( LLNameTable *name_table=NULL, bool autosave=TRUE, - bool force_immediate=FALSE, F32 timeout=300.0); - bool checkLock(); - void releaseLock(); - bool isClean(); - - //getters - LLNameTable * getNameTable(); - bool getAutosave(); - bool getClean(); - std::string getSaveName(); - S32 getPID(); - - //setters - void setClean(bool clean); - void setSaveName(std::string savename); -}; - -#endif // LL_PIDLOCK_H diff --git a/indra/llcharacter/llkeyframemotion.cpp b/indra/llcharacter/llkeyframemotion.cpp index beca1af269..d1ac336fc1 100644 --- a/indra/llcharacter/llkeyframemotion.cpp +++ b/indra/llcharacter/llkeyframemotion.cpp @@ -39,7 +39,7 @@ #include "llendianswizzle.h" #include "llkeyframemotion.h" #include "llquantize.h" -#include "llvfile.h" +#include "lldiskcache.h" #include "m3math.h" #include "message.h" @@ -559,7 +559,7 @@ LLMotion::LLMotionInitStatus LLKeyframeMotion::onInitialize(LLCharacter *charact S32 anim_file_size; BOOL success = FALSE; - LLVFile* anim_file = new LLVFile(mID, LLAssetType::AT_ANIMATION); + LLDiskCache* anim_file = new LLDiskCache(mID, LLAssetType::AT_ANIMATION); if (!anim_file || !anim_file->getSize()) { delete anim_file; @@ -2324,7 +2324,7 @@ void LLKeyframeMotion::onLoadComplete(const LLUUID& asset_uuid, // asset already loaded return; } - LLVFile file(asset_uuid, type, LLVFile::READ); + LLDiskCache file(asset_uuid, type, LLDiskCache::READ); S32 size = file.getSize(); U8* buffer = new U8[size]; diff --git a/indra/llcrashlogger/llcrashlock.h b/indra/llcrashlogger/llcrashlock.h index cde183272f..60b060b736 100644 --- a/indra/llcrashlogger/llcrashlock.h +++ b/indra/llcrashlogger/llcrashlock.h @@ -1,5 +1,5 @@ /** - * @file llpidlock.h + * @file llcrashlock.h * @brief Maintainence of disk locking files for crash reporting * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ diff --git a/indra/llmessage/llassetstorage.cpp b/indra/llmessage/llassetstorage.cpp index be5d00d4c9..31c1edd75e 100644 --- a/indra/llmessage/llassetstorage.cpp +++ b/indra/llmessage/llassetstorage.cpp @@ -42,7 +42,7 @@ // this library includes #include "message.h" #include "llxfermanager.h" -#include "llvfile.h" +#include "lldiskcache.h" #include "lldbstrings.h" #include "lltransfersourceasset.h" @@ -438,7 +438,7 @@ void LLAssetStorage::_cleanupRequests(BOOL all, S32 error) BOOL LLAssetStorage::hasLocalAsset(const LLUUID &uuid, const LLAssetType::EType type) { - return LLVFile::getExists(uuid, type); + return LLDiskCache::getExists(uuid, type); } bool LLAssetStorage::findInCacheAndInvokeCallback(const LLUUID& uuid, LLAssetType::EType type, @@ -450,10 +450,10 @@ bool LLAssetStorage::findInCacheAndInvokeCallback(const LLUUID& uuid, LLAssetTyp llassert(callback != NULL); } - BOOL exists = LLVFile::getExists(uuid, type); + BOOL exists = LLDiskCache::getExists(uuid, type); if (exists) { - LLVFile file(uuid, type); + LLDiskCache file(uuid, type); U32 size = file.getSize(); if (size > 0) { @@ -523,8 +523,8 @@ void LLAssetStorage::getAssetData(const LLUUID uuid, return; } - BOOL exists = LLVFile::getExists(uuid, type); - LLVFile file(uuid, type); + BOOL exists = LLDiskCache::getExists(uuid, type); + LLDiskCache file(uuid, type); U32 size = exists ? file.getSize() : 0; if (size > 0) @@ -664,7 +664,7 @@ void LLAssetStorage::downloadCompleteCallback( if (LL_ERR_NOERR == result) { // we might have gotten a zero-size file - LLVFile vfile(callback_id, callback_type); + LLDiskCache vfile(callback_id, callback_type); if (vfile.getSize() <= 0) { LL_WARNS("AssetStorage") << "downloadCompleteCallback has non-existent or zero-size asset " << callback_id << LL_ENDL; @@ -724,8 +724,8 @@ void LLAssetStorage::getEstateAsset( return; } - BOOL exists = LLVFile::getExists(asset_id, atype); - LLVFile file(asset_id, atype); + BOOL exists = LLDiskCache::getExists(asset_id, atype); + LLDiskCache file(asset_id, atype); U32 size = exists ? file.getSize() : 0; if (size > 0) @@ -818,7 +818,7 @@ void LLAssetStorage::downloadEstateAssetCompleteCallback( if (LL_ERR_NOERR == result) { // we might have gotten a zero-size file - LLVFile vfile(req->getUUID(), req->getAType()); + LLDiskCache vfile(req->getUUID(), req->getAType()); if (vfile.getSize() <= 0) { LL_WARNS("AssetStorage") << "downloadCompleteCallback has non-existent or zero-size asset!" << LL_ENDL; @@ -860,8 +860,8 @@ void LLAssetStorage::getInvItemAsset( return; } - exists = LLVFile::getExists(asset_id, atype); - LLVFile file(asset_id, atype); + exists = LLDiskCache::getExists(asset_id, atype); + LLDiskCache file(asset_id, atype); size = exists ? file.getSize() : 0; if(exists && size < 1) { @@ -961,7 +961,7 @@ void LLAssetStorage::downloadInvItemCompleteCallback( if (LL_ERR_NOERR == result) { // we might have gotten a zero-size file - LLVFile vfile(req->getUUID(), req->getType()); + LLDiskCache vfile(req->getUUID(), req->getType()); if (vfile.getSize() <= 0) { LL_WARNS("AssetStorage") << "downloadCompleteCallback has non-existent or zero-size asset!" << LL_ENDL; @@ -1396,7 +1396,7 @@ void LLAssetStorage::legacyGetDataCallback(const LLUUID &uuid, if ( !status && !toxic ) { - LLVFile file(uuid, type); + LLDiskCache file(uuid, type); std::string uuid_str; diff --git a/indra/llmessage/llcorehttputil.cpp b/indra/llmessage/llcorehttputil.cpp index c931a89b5b..376558400c 100644 --- a/indra/llmessage/llcorehttputil.cpp +++ b/indra/llmessage/llcorehttputil.cpp @@ -37,7 +37,7 @@ #include "llsdserialize.h" #include "reader.h" // JSON #include "writer.h" // JSON -#include "llvfile.h" +#include "lldiskcache.h" #include "message.h" // for getting the port @@ -784,7 +784,7 @@ LLSD HttpCoroutineAdapter::postFileAndSuspend(LLCore::HttpRequest::ptr_t request // scoping for our streams so that they go away when we no longer need them. { LLCore::BufferArrayStream outs(fileData.get()); - LLVFile vfile(assetId, assetType, LLVFile::READ); + LLDiskCache vfile(assetId, assetType, LLDiskCache::READ); S32 fileSize = vfile.getSize(); U8* fileBuffer; diff --git a/indra/llmessage/lltransfersourceasset.cpp b/indra/llmessage/lltransfersourceasset.cpp index 0156d1a5ef..7b00a95b00 100644 --- a/indra/llmessage/lltransfersourceasset.cpp +++ b/indra/llmessage/lltransfersourceasset.cpp @@ -32,7 +32,7 @@ #include "message.h" #include "lldatapacker.h" #include "lldir.h" -#include "llvfile.h" +#include "lldiskcache.h" LLTransferSourceAsset::LLTransferSourceAsset(const LLUUID &request_id, const F32 priority) : LLTransferSource(LLTST_ASSET, request_id, priority), @@ -99,7 +99,7 @@ LLTSCode LLTransferSourceAsset::dataCallback(const S32 packet_id, return LLTS_SKIP; } - LLVFile vf(mParams.getAssetID(), mParams.getAssetType(), LLVFile::READ); + LLDiskCache vf(mParams.getAssetID(), mParams.getAssetType(), LLDiskCache::READ); if (!vf.getSize()) { @@ -198,7 +198,7 @@ void LLTransferSourceAsset::responderCallback(const LLUUID& uuid, LLAssetType::E if (LL_ERR_NOERR == result) { // Everything's OK. - LLVFile vf(uuid, type, LLVFile::READ); + LLDiskCache vf(uuid, type, LLDiskCache::READ); tsap->mSize = vf.getSize(); status = LLTS_OK; } diff --git a/indra/llmessage/lltransfersourceasset.h b/indra/llmessage/lltransfersourceasset.h index 2c798d0598..d9055202ec 100644 --- a/indra/llmessage/lltransfersourceasset.h +++ b/indra/llmessage/lltransfersourceasset.h @@ -30,7 +30,7 @@ #include "lltransfermanager.h" #include "llassetstorage.h" -class LLVFile; +class LLDiskCache; class LLTransferSourceParamsAsset : public LLTransferSourceParams { diff --git a/indra/llmessage/lltransfertargetvfile.cpp b/indra/llmessage/lltransfertargetvfile.cpp index f2e0232a05..f4a5e71d08 100644 --- a/indra/llmessage/lltransfertargetvfile.cpp +++ b/indra/llmessage/lltransfertargetvfile.cpp @@ -30,7 +30,7 @@ #include "lldatapacker.h" #include "llerror.h" -#include "llvfile.h" +#include "lldiskcache.h" //static void LLTransferTargetVFile::updateQueue(bool shutdown) @@ -138,7 +138,7 @@ LLTSCode LLTransferTargetVFile::dataCallback(const S32 packet_id, U8 *in_datap, //LL_INFOS() << "LLTransferTargetFile::dataCallback" << LL_ENDL; //LL_INFOS() << "Packet: " << packet_id << LL_ENDL; - LLVFile vf(mTempID, mParams.getAssetType(), LLVFile::APPEND); + LLDiskCache vf(mTempID, mParams.getAssetType(), LLDiskCache::APPEND); if (mNeedsCreate) { vf.setMaxSize(mSize); @@ -176,7 +176,7 @@ void LLTransferTargetVFile::completionCallback(const LLTSCode status) case LLTS_DONE: if (!mNeedsCreate) { - LLVFile file(mTempID, mParams.getAssetType(), LLVFile::WRITE); + LLDiskCache file(mTempID, mParams.getAssetType(), LLDiskCache::WRITE); if (!file.rename(mParams.getAssetID(), mParams.getAssetType())) { LL_ERRS() << "LLTransferTargetVFile: rename failed" << LL_ENDL; @@ -195,7 +195,7 @@ void LLTransferTargetVFile::completionCallback(const LLTSCode status) { // We're aborting this transfer, we don't want to keep this file. LL_WARNS() << "Aborting vfile transfer for " << mParams.getAssetID() << LL_ENDL; - LLVFile vf(mTempID, mParams.getAssetType(), LLVFile::APPEND); + LLDiskCache vf(mTempID, mParams.getAssetType(), LLDiskCache::APPEND); vf.remove(); } break; diff --git a/indra/llmessage/lltransfertargetvfile.h b/indra/llmessage/lltransfertargetvfile.h index c819c1e2f2..4c1bfe22c5 100644 --- a/indra/llmessage/lltransfertargetvfile.h +++ b/indra/llmessage/lltransfertargetvfile.h @@ -29,9 +29,9 @@ #include "lltransfermanager.h" #include "llassetstorage.h" -#include "llvfile.h" +#include "lldiskcache.h" -class LLVFile; +class LLDiskCache; // Lame, an S32 for now until I figure out the deal with how we want to do // error codes. diff --git a/indra/llmessage/llxfer_vfile.cpp b/indra/llmessage/llxfer_vfile.cpp index 0835970cfc..95629d5fea 100644 --- a/indra/llmessage/llxfer_vfile.cpp +++ b/indra/llmessage/llxfer_vfile.cpp @@ -30,7 +30,7 @@ #include "lluuid.h" #include "llerror.h" #include "llmath.h" -#include "llvfile.h" +#include "lldiskcache.h" #include "lldir.h" // size of chunks read from/written to disk @@ -79,9 +79,9 @@ void LLXfer_VFile::cleanup () if (mTempID.notNull() && mDeleteTempFile) { - if (LLVFile::getExists(mTempID, mType)) + if (LLDiskCache::getExists(mTempID, mType)) { - LLVFile file(mTempID, mType, LLVFile::WRITE); + LLDiskCache file(mTempID, mType, LLDiskCache::WRITE); file.remove(); } else @@ -187,9 +187,9 @@ S32 LLXfer_VFile::startSend (U64 xfer_id, const LLHost &remote_host) delete mVFile; mVFile = NULL; - if(LLVFile::getExists(mLocalID, mType)) + if(LLDiskCache::getExists(mLocalID, mType)) { - mVFile = new LLVFile(mLocalID, mType, LLVFile::READ); + mVFile = new LLDiskCache(mLocalID, mType, LLDiskCache::READ); if (mVFile->getSize() <= 0) { @@ -235,9 +235,9 @@ S32 LLXfer_VFile::reopenFileHandle() if (mVFile == NULL) { - if (LLVFile::getExists(mLocalID, mType)) + if (LLDiskCache::getExists(mLocalID, mType)) { - mVFile = new LLVFile(mLocalID, mType, LLVFile::READ); + mVFile = new LLDiskCache(mLocalID, mType, LLDiskCache::READ); } else { @@ -260,7 +260,7 @@ void LLXfer_VFile::setXferSize (S32 xfer_size) // It would be nice if LLXFers could tell which end of the pipe they were if (! mVFile) { - LLVFile file(mTempID, mType, LLVFile::APPEND); + LLDiskCache file(mTempID, mType, LLDiskCache::APPEND); file.setMaxSize(xfer_size); } } @@ -315,7 +315,7 @@ S32 LLXfer_VFile::flush() S32 retval = 0; if (mBufferLength) { - LLVFile file(mTempID, mType, LLVFile::APPEND); + LLDiskCache file(mTempID, mType, LLDiskCache::APPEND); file.write((U8*)mBuffer, mBufferLength); @@ -335,9 +335,9 @@ S32 LLXfer_VFile::processEOF() if (!mCallbackResult) { - if (LLVFile::getExists(mTempID, mType)) + if (LLDiskCache::getExists(mTempID, mType)) { - LLVFile file(mTempID, mType, LLVFile::WRITE); + LLDiskCache file(mTempID, mType, LLDiskCache::WRITE); if (!file.rename(mLocalID, mType)) { LL_WARNS("Xfer") << "Cache rename of temp file failed: unable to rename " << mTempID << " to " << mLocalID << LL_ENDL; diff --git a/indra/llmessage/llxfer_vfile.h b/indra/llmessage/llxfer_vfile.h index d830c4be96..d6ac6ff818 100644 --- a/indra/llmessage/llxfer_vfile.h +++ b/indra/llmessage/llxfer_vfile.h @@ -30,7 +30,7 @@ #include "llxfer.h" #include "llassetstorage.h" -class LLVFile; +class LLDiskCache; class LLXfer_VFile : public LLXfer { @@ -40,7 +40,7 @@ class LLXfer_VFile : public LLXfer LLUUID mTempID; LLAssetType::EType mType; - LLVFile *mVFile; + LLDiskCache *mVFile; std::string mName; diff --git a/indra/llui/llviewereventrecorder.h b/indra/llui/llviewereventrecorder.h index d1059d55de..fec0f9784f 100644 --- a/indra/llui/llviewereventrecorder.h +++ b/indra/llui/llviewereventrecorder.h @@ -32,7 +32,7 @@ #include "lldir.h" #include "llsd.h" #include "llfile.h" -#include "llvfile.h" +#include "lldiskcache.h" #include "lldate.h" #include "llsdserialize.h" #include "llkeyboard.h" diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index b9e3a44786..628a4da921 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -114,7 +114,7 @@ #include "llprimitive.h" #include "llurlaction.h" #include "llurlentry.h" -#include "llvfile.h" +#include "lldiskcache.h" #include "llvolumemgr.h" #include "llxfermanager.h" #include "llphysicsextensions.h" @@ -1874,8 +1874,8 @@ bool LLAppViewer::cleanup() SUBSYSTEM_CLEANUP(LLWorldMapView); SUBSYSTEM_CLEANUP(LLFolderViewItem); - LL_INFOS() << "Cleaning up VFILE" << LL_ENDL; - SUBSYSTEM_CLEANUP(LLVFile); + LL_INFOS() << "Cleaning up disk cache" << LL_ENDL; + SUBSYSTEM_CLEANUP(LLDiskCache); LL_INFOS() << "Saving Data" << LL_ENDL; @@ -4148,7 +4148,7 @@ bool LLAppViewer::initCache() LLVOCache::getInstance()->initCache(LL_PATH_CACHE, gSavedSettings.getU32("CacheNumberOfRegionsForObjects"), getObjectCacheVersion()); - LLVFile::initClass(); + LLDiskCache::initClass(); return true; } diff --git a/indra/newview/llcompilequeue.cpp b/indra/newview/llcompilequeue.cpp index 96a2f6796d..5ed8d24bcb 100644 --- a/indra/newview/llcompilequeue.cpp +++ b/indra/newview/llcompilequeue.cpp @@ -52,7 +52,7 @@ #include "lldir.h" #include "llnotificationsutil.h" #include "llviewerstats.h" -#include "llvfile.h" +#include "lldiskcache.h" #include "lluictrlfactory.h" #include "lltrans.h" diff --git a/indra/newview/llfloaterauction.cpp b/indra/newview/llfloaterauction.cpp index bbb6409111..42bcb86454 100644 --- a/indra/newview/llfloaterauction.cpp +++ b/indra/newview/llfloaterauction.cpp @@ -32,7 +32,7 @@ #include "llimagej2c.h" #include "llimagetga.h" #include "llparcel.h" -#include "llvfile.h" +#include "lldiskcache.h" #include "llwindow.h" #include "message.h" @@ -201,7 +201,7 @@ void LLFloaterAuction::onClickSnapshot(void* data) LLPointer tga = new LLImageTGA; tga->encode(raw); - LLVFile::writeFile(tga->getData(), tga->getDataSize(), self->mImageID, LLAssetType::AT_IMAGE_TGA); + LLDiskCache::writeFile(tga->getData(), tga->getDataSize(), self->mImageID, LLAssetType::AT_IMAGE_TGA); raw->biasedScaleToPowerOfTwo(LLViewerTexture::MAX_IMAGE_SIZE_DEFAULT); @@ -209,7 +209,7 @@ void LLFloaterAuction::onClickSnapshot(void* data) LLPointer j2c = new LLImageJ2C; j2c->encode(raw, 0.0f); - LLVFile::writeFile(j2c->getData(), j2c->getDataSize(), self->mImageID, LLAssetType::AT_TEXTURE); + LLDiskCache::writeFile(j2c->getData(), j2c->getDataSize(), self->mImageID, LLAssetType::AT_TEXTURE); self->mImage = LLViewerTextureManager::getLocalTexture((LLImageRaw*)raw, FALSE); gGL.getTexUnit(0)->bind(self->mImage); diff --git a/indra/newview/llfloaterbvhpreview.cpp b/indra/newview/llfloaterbvhpreview.cpp index 88ea3d74fb..303b4836e4 100644 --- a/indra/newview/llfloaterbvhpreview.cpp +++ b/indra/newview/llfloaterbvhpreview.cpp @@ -32,7 +32,7 @@ #include "lldatapacker.h" #include "lldir.h" #include "llnotificationsutil.h" -#include "llvfile.h" +#include "lldiskcache.h" #include "llapr.h" #include "llstring.h" @@ -997,7 +997,7 @@ void LLFloaterBvhPreview::onBtnOK(void* userdata) LLDataPackerBinaryBuffer dp(buffer, file_size); if (motionp->serialize(dp)) { - LLVFile file(motionp->getID(), LLAssetType::AT_ANIMATION, LLVFile::APPEND); + LLDiskCache file(motionp->getID(), LLAssetType::AT_ANIMATION, LLDiskCache::APPEND); S32 size = dp.getCurrentSize(); file.setMaxSize(size); diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp index bc35975cc6..057c4d0d5c 100644 --- a/indra/newview/llfloatermodelpreview.cpp +++ b/indra/newview/llfloatermodelpreview.cpp @@ -77,7 +77,7 @@ #include "llspinctrl.h" #include "lltoggleablemenu.h" #include "lltrans.h" -#include "llvfile.h" +#include "lldiskcache.h" #include "llcallbacklist.h" #include "llviewerobjectlist.h" #include "llanimationstates.h" diff --git a/indra/newview/llfloaterregioninfo.cpp b/indra/newview/llfloaterregioninfo.cpp index 4b67a0f605..d2ab15a9b4 100644 --- a/indra/newview/llfloaterregioninfo.cpp +++ b/indra/newview/llfloaterregioninfo.cpp @@ -36,7 +36,7 @@ #include "llglheaders.h" #include "llregionflags.h" #include "llstl.h" -#include "llvfile.h" +#include "lldiskcache.h" #include "llxfermanager.h" #include "indra_constants.h" #include "message.h" @@ -2239,7 +2239,7 @@ void LLPanelEstateCovenant::onLoadComplete(const LLUUID& asset_uuid, { if(0 == status) { - LLVFile file(asset_uuid, type, LLVFile::READ); + LLDiskCache file(asset_uuid, type, LLDiskCache::READ); S32 file_length = file.getSize(); diff --git a/indra/newview/llfloaterreporter.cpp b/indra/newview/llfloaterreporter.cpp index 09178166e9..d658352442 100644 --- a/indra/newview/llfloaterreporter.cpp +++ b/indra/newview/llfloaterreporter.cpp @@ -44,7 +44,7 @@ #include "llnotificationsutil.h" #include "llstring.h" #include "llsys.h" -#include "llvfile.h" +#include "lldiskcache.h" #include "mean_collision_data.h" #include "message.h" #include "v3math.h" @@ -899,7 +899,7 @@ void LLFloaterReporter::takeScreenshot(bool use_prev_screenshot) mResourceDatap->mAssetInfo.setDescription("screenshot_descr"); // store in cache - LLVFile::writeFile(upload_data->getData(), + LLDiskCache::writeFile(upload_data->getData(), upload_data->getDataSize(), mResourceDatap->mAssetInfo.mUuid, mResourceDatap->mAssetInfo.mType); diff --git a/indra/newview/llfloatertos.cpp b/indra/newview/llfloatertos.cpp index bd403f68d7..96da13915c 100644 --- a/indra/newview/llfloatertos.cpp +++ b/indra/newview/llfloatertos.cpp @@ -40,7 +40,7 @@ #include "lltextbox.h" #include "llui.h" #include "lluictrlfactory.h" -#include "llvfile.h" +#include "lldiskcache.h" #include "message.h" #include "llstartup.h" // login_alert_done #include "llcorehttputil.h" diff --git a/indra/newview/llgesturemgr.cpp b/indra/newview/llgesturemgr.cpp index 119e0d21b2..82feb891bc 100644 --- a/indra/newview/llgesturemgr.cpp +++ b/indra/newview/llgesturemgr.cpp @@ -42,7 +42,7 @@ #include "llnotificationsutil.h" #include "llstl.h" #include "llstring.h" // todo: remove -#include "llvfile.h" +#include "lldiskcache.h" #include "message.h" // newview @@ -1055,7 +1055,7 @@ void LLGestureMgr::onLoadComplete(const LLUUID& asset_uuid, if (0 == status) { - LLVFile file(asset_uuid, type, LLVFile::READ); + LLDiskCache file(asset_uuid, type, LLDiskCache::READ); S32 size = file.getSize(); std::vector buffer(size+1); diff --git a/indra/newview/lllandmarklist.cpp b/indra/newview/lllandmarklist.cpp index 247ebf7719..a139d138f8 100644 --- a/indra/newview/lllandmarklist.cpp +++ b/indra/newview/lllandmarklist.cpp @@ -33,7 +33,7 @@ #include "llappviewer.h" #include "llagent.h" -#include "llvfile.h" +#include "lldiskcache.h" #include "llviewerstats.h" // Globals @@ -105,7 +105,7 @@ void LLLandmarkList::processGetAssetReply( { if( status == 0 ) { - LLVFile file(uuid, type); + LLDiskCache file(uuid, type); S32 file_length = file.getSize(); std::vector buffer(file_length + 1); diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 55c64508d2..6e58126847 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -49,7 +49,7 @@ #include "llsdutil_math.h" #include "llsdserialize.h" #include "llthread.h" -#include "llvfile.h" +#include "lldiskcache.h" #include "llviewercontrol.h" #include "llviewerinventory.h" #include "llviewermenufile.h" @@ -1335,7 +1335,7 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id) if (version <= MAX_MESH_VERSION && offset >= 0 && size > 0) { //check cache for mesh skin info - LLVFile file(mesh_id, LLAssetType::AT_MESH); + LLDiskCache file(mesh_id, LLAssetType::AT_MESH); if (file.getSize() >= offset+size) { U8* buffer = new(std::nothrow) U8[size]; @@ -1431,7 +1431,7 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id) if (version <= MAX_MESH_VERSION && offset >= 0 && size > 0) { //check cache for mesh skin info - LLVFile file(mesh_id, LLAssetType::AT_MESH); + LLDiskCache file(mesh_id, LLAssetType::AT_MESH); if (file.getSize() >= offset+size) { U8* buffer = new(std::nothrow) U8[size]; @@ -1528,7 +1528,7 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id) if (version <= MAX_MESH_VERSION && offset >= 0 && size > 0) { //check cache for mesh physics shape info - LLVFile file(mesh_id, LLAssetType::AT_MESH); + LLDiskCache file(mesh_id, LLAssetType::AT_MESH); if (file.getSize() >= offset+size) { LLMeshRepository::sCacheBytesRead += size; @@ -1633,7 +1633,7 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, bool c { //look for mesh in asset in cache - LLVFile file(mesh_params.getSculptID(), LLAssetType::AT_MESH); + LLDiskCache file(mesh_params.getSculptID(), LLAssetType::AT_MESH); S32 size = file.getSize(); @@ -1712,7 +1712,7 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, { //check cache for mesh asset - LLVFile file(mesh_id, LLAssetType::AT_MESH); + LLDiskCache file(mesh_id, LLAssetType::AT_MESH); if (file.getSize() >= offset+size) { U8* buffer = new(std::nothrow) U8[size]; @@ -3200,7 +3200,7 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* b // only allocate as much space in the cache as is needed for the local cache data_size = llmin(data_size, bytes); - LLVFile file(mesh_id, LLAssetType::AT_MESH, LLVFile::WRITE); + LLDiskCache file(mesh_id, LLAssetType::AT_MESH, LLDiskCache::WRITE); if (file.getMaxSize() >= bytes || file.setMaxSize(bytes)) { LLMeshRepository::sCacheBytesWritten += data_size; @@ -3272,7 +3272,7 @@ void LLMeshLODHandler::processData(LLCore::BufferArray * /* body */, S32 /* body if (result == MESH_OK) { // good fetch from sim, write to cache - LLVFile file(mMeshParams.getSculptID(), LLAssetType::AT_MESH, LLVFile::WRITE); + LLDiskCache file(mMeshParams.getSculptID(), LLAssetType::AT_MESH, LLDiskCache::WRITE); S32 offset = mOffset; S32 size = mRequestedBytes; @@ -3336,7 +3336,7 @@ void LLMeshSkinInfoHandler::processData(LLCore::BufferArray * /* body */, S32 /* && gMeshRepo.mThread->skinInfoReceived(mMeshID, data, data_size)) { // good fetch from sim, write to cache - LLVFile file(mMeshID, LLAssetType::AT_MESH, LLVFile::WRITE); + LLDiskCache file(mMeshID, LLAssetType::AT_MESH, LLDiskCache::WRITE); S32 offset = mOffset; S32 size = mRequestedBytes; @@ -3384,7 +3384,7 @@ void LLMeshDecompositionHandler::processData(LLCore::BufferArray * /* body */, S && gMeshRepo.mThread->decompositionReceived(mMeshID, data, data_size)) { // good fetch from sim, write to cache - LLVFile file(mMeshID, LLAssetType::AT_MESH, LLVFile::WRITE); + LLDiskCache file(mMeshID, LLAssetType::AT_MESH, LLDiskCache::WRITE); S32 offset = mOffset; S32 size = mRequestedBytes; @@ -3431,7 +3431,7 @@ void LLMeshPhysicsShapeHandler::processData(LLCore::BufferArray * /* body */, S3 && gMeshRepo.mThread->physicsShapeReceived(mMeshID, data, data_size)) { // good fetch from sim, write to cache for caching - LLVFile file(mMeshID, LLAssetType::AT_MESH, LLVFile::WRITE); + LLDiskCache file(mMeshID, LLAssetType::AT_MESH, LLDiskCache::WRITE); S32 offset = mOffset; S32 size = mRequestedBytes; diff --git a/indra/newview/lloutfitgallery.cpp b/indra/newview/lloutfitgallery.cpp index 852ba846ff..30604df944 100644 --- a/indra/newview/lloutfitgallery.cpp +++ b/indra/newview/lloutfitgallery.cpp @@ -32,7 +32,7 @@ // llcommon #include "llcommonutils.h" -#include "llvfile.h" +#include "lldiskcache.h" #include "llaccordionctrltab.h" #include "llappearancemgr.h" diff --git a/indra/newview/llpostcard.cpp b/indra/newview/llpostcard.cpp index 5201988d7c..1fd57ef555 100644 --- a/indra/newview/llpostcard.cpp +++ b/indra/newview/llpostcard.cpp @@ -28,7 +28,7 @@ #include "llpostcard.h" -#include "llvfile.h" +#include "lldiskcache.h" #include "llviewerregion.h" #include "message.h" diff --git a/indra/newview/llpreviewgesture.cpp b/indra/newview/llpreviewgesture.cpp index aaae6daf2c..c7f8f790f4 100644 --- a/indra/newview/llpreviewgesture.cpp +++ b/indra/newview/llpreviewgesture.cpp @@ -47,7 +47,7 @@ #include "llradiogroup.h" #include "llresmgr.h" #include "lltrans.h" -#include "llvfile.h" +#include "lldiskcache.h" #include "llviewerobjectlist.h" #include "llviewerregion.h" #include "llviewerstats.h" @@ -852,7 +852,7 @@ void LLPreviewGesture::onLoadComplete(const LLUUID& asset_uuid, { if (0 == status) { - LLVFile file(asset_uuid, type, LLVFile::READ); + LLDiskCache file(asset_uuid, type, LLDiskCache::READ); S32 size = file.getSize(); std::vector buffer(size+1); @@ -1137,7 +1137,7 @@ void LLPreviewGesture::saveIfNeeded() tid.generate(); assetId = tid.makeAssetID(gAgent.getSecureSessionID()); - LLVFile file(assetId, LLAssetType::AT_GESTURE, LLVFile::APPEND); + LLDiskCache file(assetId, LLAssetType::AT_GESTURE, LLDiskCache::APPEND); S32 size = dp.getCurrentSize(); file.setMaxSize(size); diff --git a/indra/newview/llpreviewnotecard.cpp b/indra/newview/llpreviewnotecard.cpp index 62ddfd5b3e..0b21ff5047 100644 --- a/indra/newview/llpreviewnotecard.cpp +++ b/indra/newview/llpreviewnotecard.cpp @@ -46,7 +46,7 @@ #include "llselectmgr.h" #include "lltrans.h" #include "llviewertexteditor.h" -#include "llvfile.h" +#include "lldiskcache.h" #include "llviewerinventory.h" #include "llviewerobject.h" #include "llviewerobjectlist.h" @@ -337,7 +337,7 @@ void LLPreviewNotecard::onLoadComplete(const LLUUID& asset_uuid, { if(0 == status) { - LLVFile file(asset_uuid, type, LLVFile::READ); + LLDiskCache file(asset_uuid, type, LLDiskCache::READ); S32 file_length = file.getSize(); @@ -452,7 +452,7 @@ void LLPreviewNotecard::finishInventoryUpload(LLUUID itemId, LLUUID newAssetId, // script actually changed the asset. if (nc->hasEmbeddedInventory()) { - LLVFile::removeFile(newAssetId, LLAssetType::AT_NOTECARD); + LLDiskCache::removeFile(newAssetId, LLAssetType::AT_NOTECARD); } if (newItemId.isNull()) { @@ -477,7 +477,7 @@ void LLPreviewNotecard::finishTaskUpload(LLUUID itemId, LLUUID newAssetId, LLUUI { if (nc->hasEmbeddedInventory()) { - LLVFile::removeFile(newAssetId, LLAssetType::AT_NOTECARD); + LLDiskCache::removeFile(newAssetId, LLAssetType::AT_NOTECARD); } nc->setAssetId(newAssetId); nc->refreshFromInventory(); @@ -556,7 +556,7 @@ bool LLPreviewNotecard::saveIfNeeded(LLInventoryItem* copyitem, bool sync) tid.generate(); asset_id = tid.makeAssetID(gAgent.getSecureSessionID()); - LLVFile file(asset_id, LLAssetType::AT_NOTECARD, LLVFile::APPEND); + LLDiskCache file(asset_id, LLAssetType::AT_NOTECARD, LLDiskCache::APPEND); LLSaveNotecardInfo* info = new LLSaveNotecardInfo(this, mItemUUID, mObjectUUID, diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp index 2eedce9bc1..c2b687cf3b 100644 --- a/indra/newview/llpreviewscript.cpp +++ b/indra/newview/llpreviewscript.cpp @@ -51,7 +51,7 @@ #include "llsdserialize.h" #include "llslider.h" #include "lltooldraganddrop.h" -#include "llvfile.h" +#include "lldiskcache.h" #include "llagent.h" #include "llmenugl.h" @@ -1715,7 +1715,7 @@ void LLPreviewLSL::onLoadComplete(const LLUUID& asset_uuid, LLAssetType::EType t { if(0 == status) { - LLVFile file(asset_uuid, type); + LLDiskCache file(asset_uuid, type); S32 file_length = file.getSize(); std::vector buffer(file_length+1); @@ -2020,7 +2020,7 @@ void LLLiveLSLEditor::onLoadComplete(const LLUUID& asset_id, void LLLiveLSLEditor::loadScriptText(const LLUUID &uuid, LLAssetType::EType type) { - LLVFile file(uuid, type); + LLDiskCache file(uuid, type); S32 file_length = file.getSize(); std::vector buffer(file_length + 1); file.read((U8*)&buffer[0], file_length); diff --git a/indra/newview/llsettingsvo.cpp b/indra/newview/llsettingsvo.cpp index 0084180cf8..62dc9f24bd 100644 --- a/indra/newview/llsettingsvo.cpp +++ b/indra/newview/llsettingsvo.cpp @@ -57,7 +57,7 @@ #include "llinventorymodel.h" #include "llassetstorage.h" -#include "llvfile.h" +#include "lldiskcache.h" #include "lldrawpoolwater.h" #include @@ -303,7 +303,7 @@ void LLSettingsVOBase::onAssetDownloadComplete(const LLUUID &asset_id, S32 statu LLSettingsBase::ptr_t settings; if (!status) { - LLVFile file(asset_id, LLAssetType::AT_SETTINGS, LLVFile::READ); + LLDiskCache file(asset_id, LLAssetType::AT_SETTINGS, LLDiskCache::READ); S32 size = file.getSize(); std::string buffer(size + 1, '\0'); diff --git a/indra/newview/llsnapshotlivepreview.cpp b/indra/newview/llsnapshotlivepreview.cpp index 4b8ceba80f..50523e981a 100644 --- a/indra/newview/llsnapshotlivepreview.cpp +++ b/indra/newview/llsnapshotlivepreview.cpp @@ -50,7 +50,7 @@ #include "llviewercontrol.h" #include "llviewermenufile.h" // upload_new_resource() #include "llviewerstats.h" -#include "llvfile.h" +#include "lldiskcache.h" #include "llwindow.h" #include "llworld.h" #include @@ -1005,7 +1005,7 @@ void LLSnapshotLivePreview::saveTexture(BOOL outfit_snapshot, std::string name) if (formatted->encode(scaled, 0.0f)) { - LLVFile::writeFile(formatted->getData(), formatted->getDataSize(), new_asset_id, LLAssetType::AT_TEXTURE); + LLDiskCache::writeFile(formatted->getData(), formatted->getDataSize(), new_asset_id, LLAssetType::AT_TEXTURE); std::string pos_string; LLAgentUI::buildLocationString(pos_string, LLAgentUI::LOCATION_FORMAT_FULL); std::string who_took_it; diff --git a/indra/newview/llviewerassetstorage.cpp b/indra/newview/llviewerassetstorage.cpp index b4e1e2633b..e9c8909fe4 100644 --- a/indra/newview/llviewerassetstorage.cpp +++ b/indra/newview/llviewerassetstorage.cpp @@ -28,7 +28,7 @@ #include "llviewerassetstorage.h" -#include "llvfile.h" +#include "lldiskcache.h" #include "message.h" #include "llagent.h" @@ -151,13 +151,13 @@ void LLViewerAssetStorage::storeAssetData( if (mUpstreamHost.isOk()) { - if (LLVFile::getExists(asset_id, asset_type)) + if (LLDiskCache::getExists(asset_id, asset_type)) { // Pack data into this packet if we can fit it. U8 buffer[MTUBYTES]; buffer[0] = 0; - LLVFile vfile(asset_id, asset_type, LLVFile::READ); + LLDiskCache vfile(asset_id, asset_type, LLDiskCache::READ); S32 asset_size = vfile.getSize(); LLAssetRequest *req = new LLAssetRequest(asset_id, asset_type); @@ -179,7 +179,7 @@ void LLViewerAssetStorage::storeAssetData( else { // LLAssetStorage metric: Successful Request - S32 size = LLVFile::getFileSize(asset_id, asset_type); + S32 size = LLDiskCache::getFileSize(asset_id, asset_type); const char *message = "Added to upload queue"; reportMetric( asset_id, asset_type, LLStringUtil::null, LLUUID::null, size, MR_OKAY, __FILE__, __LINE__, message ); @@ -290,7 +290,7 @@ void LLViewerAssetStorage::storeAssetData( legacy->mUpCallback = callback; legacy->mUserData = user_data; - LLVFile file(asset_id, asset_type, LLVFile::WRITE); + LLDiskCache file(asset_id, asset_type, LLDiskCache::WRITE); file.setMaxSize(size); @@ -526,7 +526,7 @@ void LLViewerAssetStorage::assetRequestCoro( // case. LLUUID temp_id; temp_id.generate(); - LLVFile vf(temp_id, atype, LLVFile::WRITE); + LLDiskCache vf(temp_id, atype, LLDiskCache::WRITE); vf.setMaxSize(size); req->mBytesFetched = size; if (!vf.write(raw.data(),size)) diff --git a/indra/newview/llviewerassetstorage.h b/indra/newview/llviewerassetstorage.h index 5d3f01fbb5..e65bdc1aea 100644 --- a/indra/newview/llviewerassetstorage.h +++ b/indra/newview/llviewerassetstorage.h @@ -30,7 +30,7 @@ #include "llassetstorage.h" #include "llcorehttputil.h" -class LLVFile; +class LLDiskCache; class LLViewerAssetRequest; diff --git a/indra/newview/llviewerassetupload.cpp b/indra/newview/llviewerassetupload.cpp index d14b6b0568..d62962514c 100644 --- a/indra/newview/llviewerassetupload.cpp +++ b/indra/newview/llviewerassetupload.cpp @@ -45,7 +45,7 @@ #include "llviewerassetupload.h" #include "llappviewer.h" #include "llviewerstats.h" -#include "llvfile.h" +#include "lldiskcache.h" #include "llgesturemgr.h" #include "llpreviewnotecard.h" #include "llpreviewgesture.h" @@ -472,7 +472,7 @@ LLSD LLNewFileResourceUploadInfo::exportTempFile() infile.open(filename, LL_APR_RB, NULL, &file_size); if (infile.getFileHandle()) { - LLVFile file(getAssetId(), assetType, LLVFile::WRITE); + LLDiskCache file(getAssetId(), assetType, LLDiskCache::WRITE); file.setMaxSize(file_size); @@ -565,7 +565,7 @@ LLSD LLBufferedAssetUploadInfo::prepareUpload() if (getAssetId().isNull()) generateNewAssetId(); - LLVFile file(getAssetId(), getAssetType(), LLVFile::APPEND); + LLDiskCache file(getAssetId(), getAssetType(), LLDiskCache::APPEND); S32 size = mContents.length() + 1; file.setMaxSize(size); @@ -597,7 +597,7 @@ LLUUID LLBufferedAssetUploadInfo::finishUpload(LLSD &result) if (mStoredToCache) { LLAssetType::EType assetType(getAssetType()); - LLVFile::renameFile(getAssetId(), assetType, newAssetId, assetType); + LLDiskCache::renameFile(getAssetId(), assetType, newAssetId, assetType); } if (mTaskUpload) diff --git a/indra/newview/llviewermenufile.cpp b/indra/newview/llviewermenufile.cpp index cbf490d68d..6d4e12528d 100644 --- a/indra/newview/llviewermenufile.cpp +++ b/indra/newview/llviewermenufile.cpp @@ -53,7 +53,7 @@ #include "llviewercontrol.h" // gSavedSettings #include "llviewertexturelist.h" #include "lluictrlfactory.h" -#include "llvfile.h" +#include "lldiskcache.h" #include "llviewerinventory.h" #include "llviewermenu.h" // gMenuHolder #include "llviewerparcelmgr.h" diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 2d41560441..10ee92c130 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -44,7 +44,7 @@ #include "llteleportflags.h" #include "lltoastnotifypanel.h" #include "lltransactionflags.h" -#include "llvfile.h" +#include "lldiskcache.h" #include "llxfermanager.h" #include "mean_collision_data.h" @@ -6816,7 +6816,7 @@ void onCovenantLoadComplete(const LLUUID& asset_uuid, std::string covenant_text; if(0 == status) { - LLVFile file(asset_uuid, type, LLVFile::READ); + LLDiskCache file(asset_uuid, type, LLDiskCache::READ); S32 file_length = file.getSize(); diff --git a/indra/newview/llviewerstats.cpp b/indra/newview/llviewerstats.cpp index 470704d84e..874982ed60 100644 --- a/indra/newview/llviewerstats.cpp +++ b/indra/newview/llviewerstats.cpp @@ -33,7 +33,7 @@ #include "llfloaterreg.h" #include "llmemory.h" #include "lltimer.h" -#include "llvfile.h" +#include "lldiskcache.h" #include "llappviewer.h" diff --git a/indra/newview/llviewertexlayer.cpp b/indra/newview/llviewertexlayer.cpp index b1cbdc4443..5c36635923 100644 --- a/indra/newview/llviewertexlayer.cpp +++ b/indra/newview/llviewertexlayer.cpp @@ -31,7 +31,7 @@ #include "llagent.h" #include "llimagej2c.h" #include "llnotificationsutil.h" -#include "llvfile.h" +#include "lldiskcache.h" #include "llviewerregion.h" #include "llglslshader.h" #include "llvoavatarself.h" diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp index 8782f282bf..f12ab59e2b 100644 --- a/indra/newview/llviewertexture.cpp +++ b/indra/newview/llviewertexture.cpp @@ -39,7 +39,7 @@ #include "llimagej2c.h" #include "llimagetga.h" #include "llstl.h" -#include "llvfile.h" +#include "lldiskcache.h" #include "message.h" #include "lltimer.h" diff --git a/indra/newview/llviewertexture.h b/indra/newview/llviewertexture.h index 69568cc825..57a2421065 100644 --- a/indra/newview/llviewertexture.h +++ b/indra/newview/llviewertexture.h @@ -54,7 +54,7 @@ class LLTexturePipelineTester ; typedef void (*loaded_callback_func)( BOOL success, LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* src_aux, S32 discard_level, BOOL final, void* userdata ); -class LLVFile; +class LLDiskCache; class LLMessageSystem; class LLViewerMediaImpl ; class LLVOVolume ; diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp index 312a8726ca..e0727d51ba 100644 --- a/indra/newview/llviewertexturelist.cpp +++ b/indra/newview/llviewertexturelist.cpp @@ -41,7 +41,7 @@ #include "llsdserialize.h" #include "llsys.h" -#include "llvfile.h" +#include "lldiskcache.h" #include "llxmltree.h" #include "message.h" From 20b50f99c89271518ae37ade0ef0866167070c80 Mon Sep 17 00:00:00 2001 From: Mnikolenko ProductEngine Date: Thu, 17 Sep 2020 15:10:03 +0300 Subject: [PATCH 05/85] mac build fix --- indra/llcache/CMakeLists.txt | 2 + indra/llcache/lldir_mac.cpp | 2 +- indra/llcache/lldir_utils_objc.h | 43 ++++++++ indra/llcache/lldir_utils_objc.mm | 108 ++++++++++++++++++++ indra/mac_crash_logger/CMakeLists.txt | 7 +- indra/mac_crash_logger/mac_crash_logger.cpp | 1 - 6 files changed, 157 insertions(+), 6 deletions(-) create mode 100644 indra/llcache/lldir_utils_objc.h create mode 100644 indra/llcache/lldir_utils_objc.mm diff --git a/indra/llcache/CMakeLists.txt b/indra/llcache/CMakeLists.txt index 379e3ebdbf..f1e4e7e0a3 100644 --- a/indra/llcache/CMakeLists.txt +++ b/indra/llcache/CMakeLists.txt @@ -29,6 +29,8 @@ set(llcache_HEADER_FILES ) if (DARWIN) + LIST(APPEND llcache_SOURCE_FILES lldir_utils_objc.mm) + LIST(APPEND llcache_SOURCE_FILES lldir_utils_objc.h) LIST(APPEND llcache_SOURCE_FILES lldir_mac.cpp) LIST(APPEND llcache_HEADER_FILES lldir_mac.h) endif (DARWIN) diff --git a/indra/llcache/lldir_mac.cpp b/indra/llcache/lldir_mac.cpp index 87dc1b9795..3bc4ee844e 100644 --- a/indra/llcache/lldir_mac.cpp +++ b/indra/llcache/lldir_mac.cpp @@ -36,7 +36,7 @@ #include #include #include -#include "llvfs_objc.h" +#include "lldir_utils_objc.h" // -------------------------------------------------------------------------------- diff --git a/indra/llcache/lldir_utils_objc.h b/indra/llcache/lldir_utils_objc.h new file mode 100644 index 0000000000..12019c4284 --- /dev/null +++ b/indra/llcache/lldir_utils_objc.h @@ -0,0 +1,43 @@ +/** + * @file lldir_utils_objc.h + * @brief Definition of directory utilities class for Mac OS X + * + * $LicenseInfo:firstyear=2020&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2020, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#if !LL_DARWIN +#error This header must not be included when compiling for any target other than Mac OS. Consider including lldir.h instead. +#endif // !LL_DARWIN + +#ifndef LL_LLDIR_UTILS_OBJC_H +#define LL_LLDIR_UTILS_OBJC_H + +#include + +std::string* getSystemTempFolder(); +std::string* getSystemCacheFolder(); +std::string* getSystemApplicationSupportFolder(); +std::string* getSystemResourceFolder(); +std::string* getSystemExecutableFolder(); + + +#endif // LL_LLDIR_UTILS_OBJC_H diff --git a/indra/llcache/lldir_utils_objc.mm b/indra/llcache/lldir_utils_objc.mm new file mode 100644 index 0000000000..da55a2f897 --- /dev/null +++ b/indra/llcache/lldir_utils_objc.mm @@ -0,0 +1,108 @@ +/** + * @file lldir_utils_objc.mm + * @brief Cocoa implementation of directory utilities for Mac OS X + * + * $LicenseInfo:firstyear=2020&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2020, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ +#if LL_DARWIN + +//WARNING: This file CANNOT use standard linden includes due to conflicts between definitions of BOOL + +#include "lldir_utils_objc.h" +#import + +std::string* getSystemTempFolder() +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + NSString * tempDir = NSTemporaryDirectory(); + if (tempDir == nil) + tempDir = @"/tmp"; + std::string *result = ( new std::string([tempDir UTF8String]) ); + [pool release]; + + return result; +} + +//findSystemDirectory scoped exclusively to this file. +std::string* findSystemDirectory(NSSearchPathDirectory searchPathDirectory, + NSSearchPathDomainMask domainMask) +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + std::string *result = nil; + NSString *path = nil; + + // Search for the path + NSArray* paths = NSSearchPathForDirectoriesInDomains(searchPathDirectory, + domainMask, + YES); + if ([paths count]) + { + path = [paths objectAtIndex:0]; + //HACK: Always attempt to create directory, ignore errors. + NSError *error = nil; + + [[NSFileManager defaultManager] createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:&error]; + + + result = new std::string([path UTF8String]); + } + [pool release]; + return result; +} + +std::string* getSystemExecutableFolder() +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + NSString *bundlePath = [[NSBundle mainBundle] executablePath]; + std::string *result = (new std::string([bundlePath UTF8String])); + [pool release]; + + return result; +} + +std::string* getSystemResourceFolder() +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + NSString *bundlePath = [[NSBundle mainBundle] resourcePath]; + std::string *result = (new std::string([bundlePath UTF8String])); + [pool release]; + + return result; +} + +std::string* getSystemCacheFolder() +{ + return findSystemDirectory (NSCachesDirectory, + NSUserDomainMask); +} + +std::string* getSystemApplicationSupportFolder() +{ + return findSystemDirectory (NSApplicationSupportDirectory, + NSUserDomainMask); + +} + +#endif // LL_DARWIN diff --git a/indra/mac_crash_logger/CMakeLists.txt b/indra/mac_crash_logger/CMakeLists.txt index 95637c9a28..4a10076e6e 100644 --- a/indra/mac_crash_logger/CMakeLists.txt +++ b/indra/mac_crash_logger/CMakeLists.txt @@ -8,7 +8,7 @@ include(LLCoreHttp) include(LLCrashLogger) include(LLMath) include(LLMessage) -include(LLVFS) +include(LLCache) include(LLXML) include(Linking) include(LLSharedLibs) @@ -19,7 +19,7 @@ include_directories( ${LLCOMMON_INCLUDE_DIRS} ${LLCRASHLOGGER_INCLUDE_DIRS} ${LLMATH_INCLUDE_DIRS} - ${LLVFS_INCLUDE_DIRS} + ${LLCACHE_INCLUDE_DIRS} ${LLXML_INCLUDE_DIRS} ) include_directories(SYSTEM @@ -68,11 +68,10 @@ find_library(COCOA_LIBRARY Cocoa) target_link_libraries(mac-crash-logger ${LLCRASHLOGGER_LIBRARIES} - ${LLVFS_LIBRARIES} + ${LLCACHE_LIBRARIES} ${COCOA_LIBRARIES} ${LLXML_LIBRARIES} ${LLMESSAGE_LIBRARIES} - ${LLVFS_LIBRARIES} ${LLMATH_LIBRARIES} ${LLCOREHTTP_LIBRARIES} ${LLCOMMON_LIBRARIES} diff --git a/indra/mac_crash_logger/mac_crash_logger.cpp b/indra/mac_crash_logger/mac_crash_logger.cpp index 54e41a1954..66d8cfa590 100644 --- a/indra/mac_crash_logger/mac_crash_logger.cpp +++ b/indra/mac_crash_logger/mac_crash_logger.cpp @@ -27,7 +27,6 @@ #include "linden_common.h" #include "llcrashloggermac.h" #include "indra_constants.h" -#include "llpidlock.h" #include From d9448c6f52218146113d1d5c5ca4c4d5f01dc5cf Mon Sep 17 00:00:00 2001 From: Callum Prentice Date: Thu, 17 Sep 2020 09:45:06 -0700 Subject: [PATCH 06/85] The folder where the disk cache lives was originally renamed from llvfs to llcache but @henri's suggestion that that doesn't reflect the other files in the same place and it should be llfilesystem is a good one so I changed it over --- indra/CMakeLists.txt | 2 +- indra/cmake/CMakeLists.txt | 2 +- indra/cmake/LLCache.cmake | 7 ---- indra/cmake/LLFileSystem.cmake | 7 ++++ indra/llappearance/CMakeLists.txt | 20 ++-------- indra/llaudio/CMakeLists.txt | 6 +-- indra/llcharacter/CMakeLists.txt | 6 +-- indra/llcrashlogger/CMakeLists.txt | 4 +- .../{llcache => llfilesystem}/CMakeLists.txt | 38 +++++++++---------- indra/{llcache => llfilesystem}/lldir.cpp | 0 indra/{llcache => llfilesystem}/lldir.h | 0 .../{llcache => llfilesystem}/lldir_linux.cpp | 0 indra/{llcache => llfilesystem}/lldir_linux.h | 0 indra/{llcache => llfilesystem}/lldir_mac.cpp | 0 indra/{llcache => llfilesystem}/lldir_mac.h | 0 .../lldir_solaris.cpp | 0 .../{llcache => llfilesystem}/lldir_solaris.h | 0 .../lldir_utils_objc.h | 0 .../lldir_utils_objc.mm | 0 .../{llcache => llfilesystem}/lldir_win32.cpp | 0 indra/{llcache => llfilesystem}/lldir_win32.h | 0 indra/{llcache => llfilesystem}/lldirguard.h | 0 .../lldiriterator.cpp | 0 .../{llcache => llfilesystem}/lldiriterator.h | 0 .../{llcache => llfilesystem}/lldiskcache.cpp | 0 indra/{llcache => llfilesystem}/lldiskcache.h | 0 .../{llcache => llfilesystem}/lllfsthread.cpp | 0 indra/{llcache => llfilesystem}/lllfsthread.h | 0 .../tests/lldir_test.cpp | 0 .../tests/lldiriterator_test.cpp | 0 indra/llimage/CMakeLists.txt | 6 +-- indra/llinventory/CMakeLists.txt | 4 +- indra/llmessage/CMakeLists.txt | 12 +++--- indra/llrender/CMakeLists.txt | 10 ++--- indra/llui/CMakeLists.txt | 6 +-- indra/llwindow/CMakeLists.txt | 8 ++-- indra/llxml/CMakeLists.txt | 6 +-- indra/mac_crash_logger/CMakeLists.txt | 6 +-- indra/newview/CMakeLists.txt | 10 ++--- indra/test/CMakeLists.txt | 7 ++-- indra/win_crash_logger/CMakeLists.txt | 6 +-- 41 files changed, 79 insertions(+), 94 deletions(-) delete mode 100644 indra/cmake/LLCache.cmake create mode 100644 indra/cmake/LLFileSystem.cmake rename indra/{llcache => llfilesystem}/CMakeLists.txt (58%) rename indra/{llcache => llfilesystem}/lldir.cpp (100%) rename indra/{llcache => llfilesystem}/lldir.h (100%) rename indra/{llcache => llfilesystem}/lldir_linux.cpp (100%) rename indra/{llcache => llfilesystem}/lldir_linux.h (100%) rename indra/{llcache => llfilesystem}/lldir_mac.cpp (100%) rename indra/{llcache => llfilesystem}/lldir_mac.h (100%) rename indra/{llcache => llfilesystem}/lldir_solaris.cpp (100%) rename indra/{llcache => llfilesystem}/lldir_solaris.h (100%) rename indra/{llcache => llfilesystem}/lldir_utils_objc.h (100%) rename indra/{llcache => llfilesystem}/lldir_utils_objc.mm (100%) rename indra/{llcache => llfilesystem}/lldir_win32.cpp (100%) rename indra/{llcache => llfilesystem}/lldir_win32.h (100%) rename indra/{llcache => llfilesystem}/lldirguard.h (100%) rename indra/{llcache => llfilesystem}/lldiriterator.cpp (100%) rename indra/{llcache => llfilesystem}/lldiriterator.h (100%) rename indra/{llcache => llfilesystem}/lldiskcache.cpp (100%) rename indra/{llcache => llfilesystem}/lldiskcache.h (100%) rename indra/{llcache => llfilesystem}/lllfsthread.cpp (100%) rename indra/{llcache => llfilesystem}/lllfsthread.h (100%) rename indra/{llcache => llfilesystem}/tests/lldir_test.cpp (100%) rename indra/{llcache => llfilesystem}/tests/lldiriterator_test.cpp (100%) diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt index 04279cc887..4b39bfe332 100644 --- a/indra/CMakeLists.txt +++ b/indra/CMakeLists.txt @@ -40,7 +40,7 @@ add_subdirectory(${LIBS_OPEN_PREFIX}llmath) add_subdirectory(${LIBS_OPEN_PREFIX}llmessage) add_subdirectory(${LIBS_OPEN_PREFIX}llprimitive) add_subdirectory(${LIBS_OPEN_PREFIX}llrender) -add_subdirectory(${LIBS_OPEN_PREFIX}llcache) +add_subdirectory(${LIBS_OPEN_PREFIX}llfilesystem) add_subdirectory(${LIBS_OPEN_PREFIX}llwindow) add_subdirectory(${LIBS_OPEN_PREFIX}llxml) diff --git a/indra/cmake/CMakeLists.txt b/indra/cmake/CMakeLists.txt index d464c8ad74..352dfc0641 100644 --- a/indra/cmake/CMakeLists.txt +++ b/indra/cmake/CMakeLists.txt @@ -69,7 +69,7 @@ set(cmake_SOURCE_FILES LLSharedLibs.cmake LLTestCommand.cmake LLUI.cmake - LLCache.cmake + LLFileSystem.cmake LLWindow.cmake LLXML.cmake Linking.cmake diff --git a/indra/cmake/LLCache.cmake b/indra/cmake/LLCache.cmake deleted file mode 100644 index 6a82774133..0000000000 --- a/indra/cmake/LLCache.cmake +++ /dev/null @@ -1,7 +0,0 @@ -# -*- cmake -*- - -set(LLCACHE_INCLUDE_DIRS - ${LIBS_OPEN_DIR}/llcache - ) - -set(LLCACHE_LIBRARIES llcache) diff --git a/indra/cmake/LLFileSystem.cmake b/indra/cmake/LLFileSystem.cmake new file mode 100644 index 0000000000..2e6c42c30c --- /dev/null +++ b/indra/cmake/LLFileSystem.cmake @@ -0,0 +1,7 @@ +# -*- cmake -*- + +set(LLFILESYSTEM_INCLUDE_DIRS + ${LIBS_OPEN_DIR}/llfilesystem + ) + +set(LLFILESYSTEM_LIBRARIES llfilesystem) diff --git a/indra/llappearance/CMakeLists.txt b/indra/llappearance/CMakeLists.txt index ff784387dc..268849ad74 100644 --- a/indra/llappearance/CMakeLists.txt +++ b/indra/llappearance/CMakeLists.txt @@ -11,7 +11,7 @@ include(LLMath) include(LLMessage) include(LLCoreHttp) include(LLRender) -include(LLCache) +include(LLFileSystem) include(LLWindow) include(LLXML) include(Linking) @@ -23,7 +23,7 @@ include_directories( ${LLINVENTORY_INCLUDE_DIRS} ${LLMATH_INCLUDE_DIRS} ${LLRENDER_INCLUDE_DIRS} - ${LLCACHE_INCLUDE_DIRS} + ${LLFILESYSTEM_INCLUDE_DIRS} ${LLWINDOW_INCLUDE_DIRS} ${LLXML_INCLUDE_DIRS} ) @@ -83,7 +83,7 @@ target_link_libraries(llappearance ${LLINVENTORY_LIBRARIES} ${LLIMAGE_LIBRARIES} ${LLRENDER_LIBRARIES} - ${LLCACHE_LIBRARIES} + ${LLFILESYSTEM_LIBRARIES} ${LLMATH_LIBRARIES} ${LLXML_LIBRARIES} ${LLMATH_LIBRARIES} @@ -100,7 +100,7 @@ if (BUILD_HEADLESS) ${LLINVENTORY_LIBRARIES} ${LLIMAGE_LIBRARIES} ${LLRENDERHEADLESS_LIBRARIES} - ${LLCACHE_LIBRARIES} + ${LLFILESYSTEM_LIBRARIES} ${LLMATH_LIBRARIES} ${LLXML_LIBRARIES} ${LLMATH_LIBRARIES} @@ -109,15 +109,3 @@ if (BUILD_HEADLESS) ${LLCOMMON_LIBRARIES} ) endif (BUILD_HEADLESS) - -#add unit tests -#if (LL_TESTS) -# INCLUDE(LLAddBuildTest) -# SET(llappearance_TEST_SOURCE_FILES -# # no real unit tests yet! -# ) -# LL_ADD_PROJECT_UNIT_TESTS(llappearance "${llappearance_TEST_SOURCE_FILES}") - - #set(TEST_DEBUG on) -# set(test_libs llappearance ${LLCOMMON_LIBRARIES}) -#endif (LL_TESTS) diff --git a/indra/llaudio/CMakeLists.txt b/indra/llaudio/CMakeLists.txt index 2498561029..9dcd4d697e 100644 --- a/indra/llaudio/CMakeLists.txt +++ b/indra/llaudio/CMakeLists.txt @@ -9,14 +9,14 @@ include(OPENAL) include(LLCommon) include(LLMath) include(LLMessage) -include(LLCache) +include(LLFileSystem) include_directories( ${LLAUDIO_INCLUDE_DIRS} ${LLCOMMON_INCLUDE_DIRS} ${LLMATH_INCLUDE_DIRS} ${LLMESSAGE_INCLUDE_DIRS} - ${LLCACHE_INCLUDE_DIRS} + ${LLFILESYSTEM_INCLUDE_DIRS} ${OGG_INCLUDE_DIRS} ${VORBISENC_INCLUDE_DIRS} ${VORBISFILE_INCLUDE_DIRS} @@ -82,7 +82,7 @@ target_link_libraries( ${LLCOMMON_LIBRARIES} ${LLMATH_LIBRARIES} ${LLMESSAGE_LIBRARIES} - ${LLCACHE_LIBRARIES} + ${LLFILESYSTEM_LIBRARIES} ${VORBISENC_LIBRARIES} ${VORBISFILE_LIBRARIES} ${VORBIS_LIBRARIES} diff --git a/indra/llcharacter/CMakeLists.txt b/indra/llcharacter/CMakeLists.txt index e236a307c2..d90ffb5543 100644 --- a/indra/llcharacter/CMakeLists.txt +++ b/indra/llcharacter/CMakeLists.txt @@ -6,14 +6,14 @@ include(00-Common) include(LLCommon) include(LLMath) include(LLMessage) -include(LLCache) +include(LLFileSystem) include(LLXML) include_directories( ${LLCOMMON_INCLUDE_DIRS} ${LLMATH_INCLUDE_DIRS} ${LLMESSAGE_INCLUDE_DIRS} - ${LLCACHE_INCLUDE_DIRS} + ${LLFILESYSTEM_INCLUDE_DIRS} ${LLXML_INCLUDE_DIRS} ) include_directories(SYSTEM @@ -85,6 +85,6 @@ target_link_libraries( ${LLCOMMON_LIBRARIES} ${LLMATH_LIBRARIES} ${LLMESSAGE_LIBRARIES} - ${LLCACHE_LIBRARIES} + ${LLFILESYSTEM_LIBRARIES} ${LLXML_LIBRARIES} ) diff --git a/indra/llcrashlogger/CMakeLists.txt b/indra/llcrashlogger/CMakeLists.txt index 040a0e846c..d70a1e0fb0 100644 --- a/indra/llcrashlogger/CMakeLists.txt +++ b/indra/llcrashlogger/CMakeLists.txt @@ -7,7 +7,7 @@ include(LLCoreHttp) include(LLCommon) include(LLMath) include(LLMessage) -include(LLCache) +include(LLFileSystem) include(LLXML) include_directories( @@ -15,7 +15,7 @@ include_directories( ${LLCOMMON_INCLUDE_DIRS} ${LLMATH_INCLUDE_DIRS} ${LLMESSAGE_INCLUDE_DIRS} - ${LLCACHE_INCLUDE_DIRS} + ${LLFILESYSTEM_INCLUDE_DIRS} ${LLXML_INCLUDE_DIRS} ) include_directories(SYSTEM diff --git a/indra/llcache/CMakeLists.txt b/indra/llfilesystem/CMakeLists.txt similarity index 58% rename from indra/llcache/CMakeLists.txt rename to indra/llfilesystem/CMakeLists.txt index f1e4e7e0a3..4af14d6d3a 100644 --- a/indra/llcache/CMakeLists.txt +++ b/indra/llfilesystem/CMakeLists.txt @@ -1,6 +1,6 @@ # -*- cmake -*- -project(llcache) +project(llfilesystem) include(00-Common) include(LLCommon) @@ -11,14 +11,14 @@ include_directories( ${LLCOMMON_SYSTEM_INCLUDE_DIRS} ) -set(llcache_SOURCE_FILES +set(llfilesystem_SOURCE_FILES lldir.cpp lldiriterator.cpp lllfsthread.cpp lldiskcache.cpp ) -set(llcache_HEADER_FILES +set(llfilesystem_HEADER_FILES CMakeLists.txt lldir.h @@ -29,15 +29,15 @@ set(llcache_HEADER_FILES ) if (DARWIN) - LIST(APPEND llcache_SOURCE_FILES lldir_utils_objc.mm) - LIST(APPEND llcache_SOURCE_FILES lldir_utils_objc.h) - LIST(APPEND llcache_SOURCE_FILES lldir_mac.cpp) - LIST(APPEND llcache_HEADER_FILES lldir_mac.h) + LIST(APPEND llfilesystem_SOURCE_FILES lldir_utils_objc.mm) + LIST(APPEND llfilesystem_SOURCE_FILES lldir_utils_objc.h) + LIST(APPEND llfilesystem_SOURCE_FILES lldir_mac.cpp) + LIST(APPEND llfilesystem_HEADER_FILES lldir_mac.h) endif (DARWIN) if (LINUX) - LIST(APPEND llcache_SOURCE_FILES lldir_linux.cpp) - LIST(APPEND llcache_HEADER_FILES lldir_linux.h) + LIST(APPEND llfilesystem_SOURCE_FILES lldir_linux.cpp) + LIST(APPEND llfilesystem_HEADER_FILES lldir_linux.h) if (INSTALL) set_source_files_properties(lldir_linux.cpp @@ -48,23 +48,23 @@ if (LINUX) endif (LINUX) if (WINDOWS) - LIST(APPEND llcache_SOURCE_FILES lldir_win32.cpp) - LIST(APPEND llcache_HEADER_FILES lldir_win32.h) + LIST(APPEND llfilesystem_SOURCE_FILES lldir_win32.cpp) + LIST(APPEND llfilesystem_HEADER_FILES lldir_win32.h) endif (WINDOWS) -set_source_files_properties(${llcache_HEADER_FILES} +set_source_files_properties(${llfilesystem_HEADER_FILES} PROPERTIES HEADER_FILE_ONLY TRUE) -list(APPEND llcache_SOURCE_FILES ${llcache_HEADER_FILES}) +list(APPEND llfilesystem_SOURCE_FILES ${llfilesystem_HEADER_FILES}) -add_library (llcache ${llcache_SOURCE_FILES}) +add_library (llfilesystem ${llfilesystem_SOURCE_FILES}) set(cache_BOOST_LIBRARIES ${BOOST_FILESYSTEM_LIBRARY} ${BOOST_SYSTEM_LIBRARY} ) -target_link_libraries(llcache +target_link_libraries(llfilesystem ${LLCOMMON_LIBRARIES} ${cache_BOOST_LIBRARIES} ) @@ -72,7 +72,7 @@ target_link_libraries(llcache if (DARWIN) include(CMakeFindFrameworks) find_library(COCOA_LIBRARY Cocoa) - target_link_libraries(llcache ${COCOA_LIBRARY}) + target_link_libraries(llfilesystem ${COCOA_LIBRARY}) endif (DARWIN) @@ -80,7 +80,7 @@ endif (DARWIN) if (LL_TESTS) include(LLAddBuildTest) # UNIT TESTS - SET(llcache_TEST_SOURCE_FILES + SET(llfilesystem_TEST_SOURCE_FILES lldiriterator.cpp ) @@ -88,10 +88,10 @@ if (LL_TESTS) PROPERTIES LL_TEST_ADDITIONAL_LIBRARIES "${cache_BOOST_LIBRARIES}" ) - LL_ADD_PROJECT_UNIT_TESTS(llcache "${llcache_TEST_SOURCE_FILES}") + LL_ADD_PROJECT_UNIT_TESTS(llfilesystem "${llfilesystem_TEST_SOURCE_FILES}") # INTEGRATION TESTS - set(test_libs llmath llcommon llcache ${LLCOMMON_LIBRARIES} ${WINDOWS_LIBRARIES}) + set(test_libs llmath llcommon llfilesystem ${LLCOMMON_LIBRARIES} ${WINDOWS_LIBRARIES}) # TODO: Some of these need refactoring to be proper Unit tests rather than Integration tests. LL_ADD_INTEGRATION_TEST(lldir "" "${test_libs}") diff --git a/indra/llcache/lldir.cpp b/indra/llfilesystem/lldir.cpp similarity index 100% rename from indra/llcache/lldir.cpp rename to indra/llfilesystem/lldir.cpp diff --git a/indra/llcache/lldir.h b/indra/llfilesystem/lldir.h similarity index 100% rename from indra/llcache/lldir.h rename to indra/llfilesystem/lldir.h diff --git a/indra/llcache/lldir_linux.cpp b/indra/llfilesystem/lldir_linux.cpp similarity index 100% rename from indra/llcache/lldir_linux.cpp rename to indra/llfilesystem/lldir_linux.cpp diff --git a/indra/llcache/lldir_linux.h b/indra/llfilesystem/lldir_linux.h similarity index 100% rename from indra/llcache/lldir_linux.h rename to indra/llfilesystem/lldir_linux.h diff --git a/indra/llcache/lldir_mac.cpp b/indra/llfilesystem/lldir_mac.cpp similarity index 100% rename from indra/llcache/lldir_mac.cpp rename to indra/llfilesystem/lldir_mac.cpp diff --git a/indra/llcache/lldir_mac.h b/indra/llfilesystem/lldir_mac.h similarity index 100% rename from indra/llcache/lldir_mac.h rename to indra/llfilesystem/lldir_mac.h diff --git a/indra/llcache/lldir_solaris.cpp b/indra/llfilesystem/lldir_solaris.cpp similarity index 100% rename from indra/llcache/lldir_solaris.cpp rename to indra/llfilesystem/lldir_solaris.cpp diff --git a/indra/llcache/lldir_solaris.h b/indra/llfilesystem/lldir_solaris.h similarity index 100% rename from indra/llcache/lldir_solaris.h rename to indra/llfilesystem/lldir_solaris.h diff --git a/indra/llcache/lldir_utils_objc.h b/indra/llfilesystem/lldir_utils_objc.h similarity index 100% rename from indra/llcache/lldir_utils_objc.h rename to indra/llfilesystem/lldir_utils_objc.h diff --git a/indra/llcache/lldir_utils_objc.mm b/indra/llfilesystem/lldir_utils_objc.mm similarity index 100% rename from indra/llcache/lldir_utils_objc.mm rename to indra/llfilesystem/lldir_utils_objc.mm diff --git a/indra/llcache/lldir_win32.cpp b/indra/llfilesystem/lldir_win32.cpp similarity index 100% rename from indra/llcache/lldir_win32.cpp rename to indra/llfilesystem/lldir_win32.cpp diff --git a/indra/llcache/lldir_win32.h b/indra/llfilesystem/lldir_win32.h similarity index 100% rename from indra/llcache/lldir_win32.h rename to indra/llfilesystem/lldir_win32.h diff --git a/indra/llcache/lldirguard.h b/indra/llfilesystem/lldirguard.h similarity index 100% rename from indra/llcache/lldirguard.h rename to indra/llfilesystem/lldirguard.h diff --git a/indra/llcache/lldiriterator.cpp b/indra/llfilesystem/lldiriterator.cpp similarity index 100% rename from indra/llcache/lldiriterator.cpp rename to indra/llfilesystem/lldiriterator.cpp diff --git a/indra/llcache/lldiriterator.h b/indra/llfilesystem/lldiriterator.h similarity index 100% rename from indra/llcache/lldiriterator.h rename to indra/llfilesystem/lldiriterator.h diff --git a/indra/llcache/lldiskcache.cpp b/indra/llfilesystem/lldiskcache.cpp similarity index 100% rename from indra/llcache/lldiskcache.cpp rename to indra/llfilesystem/lldiskcache.cpp diff --git a/indra/llcache/lldiskcache.h b/indra/llfilesystem/lldiskcache.h similarity index 100% rename from indra/llcache/lldiskcache.h rename to indra/llfilesystem/lldiskcache.h diff --git a/indra/llcache/lllfsthread.cpp b/indra/llfilesystem/lllfsthread.cpp similarity index 100% rename from indra/llcache/lllfsthread.cpp rename to indra/llfilesystem/lllfsthread.cpp diff --git a/indra/llcache/lllfsthread.h b/indra/llfilesystem/lllfsthread.h similarity index 100% rename from indra/llcache/lllfsthread.h rename to indra/llfilesystem/lllfsthread.h diff --git a/indra/llcache/tests/lldir_test.cpp b/indra/llfilesystem/tests/lldir_test.cpp similarity index 100% rename from indra/llcache/tests/lldir_test.cpp rename to indra/llfilesystem/tests/lldir_test.cpp diff --git a/indra/llcache/tests/lldiriterator_test.cpp b/indra/llfilesystem/tests/lldiriterator_test.cpp similarity index 100% rename from indra/llcache/tests/lldiriterator_test.cpp rename to indra/llfilesystem/tests/lldiriterator_test.cpp diff --git a/indra/llimage/CMakeLists.txt b/indra/llimage/CMakeLists.txt index 1f6b981346..dc8e9f7c2f 100644 --- a/indra/llimage/CMakeLists.txt +++ b/indra/llimage/CMakeLists.txt @@ -6,7 +6,7 @@ include(00-Common) include(LLCommon) include(LLImage) include(LLMath) -include(LLCache) +include(LLFileSystem) include(LLKDU) include(LLImageJ2COJ) include(ZLIB) @@ -17,7 +17,7 @@ include_directories( ${LLCOMMON_INCLUDE_DIRS} ${LLCOMMON_SYSTEM_INCLUDE_DIRS} ${LLMATH_INCLUDE_DIRS} - ${LLCACHE_INCLUDE_DIRS} + ${LLFILESYSTEM_INCLUDE_DIRS} ${PNG_INCLUDE_DIRS} ${ZLIB_INCLUDE_DIRS} ) @@ -68,7 +68,7 @@ else (USE_KDU) endif (USE_KDU) target_link_libraries(llimage - ${LLCACHE_LIBRARIES} + ${LLFILESYSTEM_LIBRARIES} ${LLMATH_LIBRARIES} ${LLCOMMON_LIBRARIES} ${JPEG_LIBRARIES} diff --git a/indra/llinventory/CMakeLists.txt b/indra/llinventory/CMakeLists.txt index ba9f8af390..04975940aa 100644 --- a/indra/llinventory/CMakeLists.txt +++ b/indra/llinventory/CMakeLists.txt @@ -7,7 +7,7 @@ include(LLCommon) include(LLCoreHttp) include(LLMath) include(LLMessage) -include(LLCache) +include(LLFileSystem) include(LLXML) include_directories( @@ -81,7 +81,7 @@ if (LL_TESTS) LL_ADD_PROJECT_UNIT_TESTS(llinventory "${llinventory_TEST_SOURCE_FILES}") #set(TEST_DEBUG on) - set(test_libs llinventory ${LLMESSAGE_LIBRARIES} ${LLCACHE_LIBRARIES} ${LLCOREHTTP_LIBRARIES} ${LLMATH_LIBRARIES} ${LLCOMMON_LIBRARIES} ${WINDOWS_LIBRARIES}) + set(test_libs llinventory ${LLMESSAGE_LIBRARIES} ${LLFILESYSTEM_LIBRARIES} ${LLCOREHTTP_LIBRARIES} ${LLMATH_LIBRARIES} ${LLCOMMON_LIBRARIES} ${WINDOWS_LIBRARIES}) LL_ADD_INTEGRATION_TEST(inventorymisc "" "${test_libs}") LL_ADD_INTEGRATION_TEST(llparcel "" "${test_libs}") endif (LL_TESTS) diff --git a/indra/llmessage/CMakeLists.txt b/indra/llmessage/CMakeLists.txt index 66a7d3ea04..f0a1dfe940 100644 --- a/indra/llmessage/CMakeLists.txt +++ b/indra/llmessage/CMakeLists.txt @@ -9,7 +9,7 @@ include(LLCommon) include(LLCoreHttp) include(LLMath) include(LLMessage) -include(LLCache) +include(LLFileSystem) include(LLAddBuildTest) include(Python) include(Tut) @@ -23,7 +23,7 @@ include_directories( ${LLCOREHTTP_INCLUDE_DIRS} ${LLMATH_INCLUDE_DIRS} ${LLMESSAGE_INCLUDE_DIRS} - ${LLCACHE_INCLUDE_DIRS} + ${LLFILESYSTEM_INCLUDE_DIRS} ${JSONCPP_INCLUDE_DIR} ) @@ -209,7 +209,7 @@ target_link_libraries( llmessage ${CURL_LIBRARIES} ${LLCOMMON_LIBRARIES} - ${LLCACHE_LIBRARIES} + ${LLFILESYSTEM_LIBRARIES} ${LLMATH_LIBRARIES} ${JSONCPP_LIBRARIES} ${OPENSSL_LIBRARIES} @@ -227,7 +227,7 @@ target_link_libraries( llmessage ${CURL_LIBRARIES} ${LLCOMMON_LIBRARIES} - ${LLCACHE_LIBRARIES} + ${LLFILESYSTEM_LIBRARIES} ${LLMATH_LIBRARIES} ${JSONCPP_LIBRARIES} ${OPENSSL_LIBRARIES} @@ -257,7 +257,7 @@ if (LL_TESTS) if (LINUX) set(test_libs ${WINDOWS_LIBRARIES} - ${LLCACHE_LIBRARIES} + ${LLFILESYSTEM_LIBRARIES} ${LLMATH_LIBRARIES} ${CURL_LIBRARIES} ${NGHTTP2_LIBRARIES} @@ -273,7 +273,7 @@ if (LINUX) else (LINUX) set(test_libs ${WINDOWS_LIBRARIES} - ${LLCACHE_LIBRARIES} + ${LLFILESYSTEM_LIBRARIES} ${LLMATH_LIBRARIES} ${CURL_LIBRARIES} ${NGHTTP2_LIBRARIES} diff --git a/indra/llrender/CMakeLists.txt b/indra/llrender/CMakeLists.txt index 524e2a1729..baab09a104 100644 --- a/indra/llrender/CMakeLists.txt +++ b/indra/llrender/CMakeLists.txt @@ -11,7 +11,7 @@ include(LLMath) include(LLRender) include(LLWindow) include(LLXML) -include(LLCache) +include(LLFileSystem) include_directories( ${FREETYPE_INCLUDE_DIRS} @@ -19,10 +19,9 @@ include_directories( ${LLIMAGE_INCLUDE_DIRS} ${LLMATH_INCLUDE_DIRS} ${LLRENDER_INCLUDE_DIRS} - ${LLCACHE_INCLUDE_DIRS} + ${LLFILESYSTEM_INCLUDE_DIRS} ${LLWINDOW_INCLUDE_DIRS} ${LLXML_INCLUDE_DIRS} - ${LLCACHE_INCLUDE_DIRS} ) include_directories(SYSTEM ${LLCOMMON_SYSTEM_INCLUDE_DIRS} @@ -103,9 +102,8 @@ if (BUILD_HEADLESS) ${LLIMAGE_LIBRARIES} ${LLMATH_LIBRARIES} ${LLRENDER_HEADLESS_LIBRARIES} - ${LLCACHE_LIBRARIES} ${LLXML_LIBRARIES} - ${LLCACHE_LIBRARIES} + ${LLFILESYSTEM_LIBRARIES} ${LLWINDOW_HEADLESS_LIBRARIES} ${OPENGL_HEADLESS_LIBRARIES}) @@ -125,7 +123,7 @@ target_link_libraries(llrender ${LLCOMMON_LIBRARIES} ${LLIMAGE_LIBRARIES} ${LLMATH_LIBRARIES} - ${LLCACHE_LIBRARIES} + ${LLFILESYSTEM_LIBRARIES} ${LLXML_LIBRARIES} ${LLWINDOW_LIBRARIES} ${FREETYPE_LIBRARIES} diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt index 02020b19c6..7401e6130a 100644 --- a/indra/llui/CMakeLists.txt +++ b/indra/llui/CMakeLists.txt @@ -13,7 +13,7 @@ include(LLCoreHttp) include(LLRender) include(LLWindow) include(LLCoreHttp) -include(LLCache) +include(LLFileSystem) include(LLXML) include_directories( @@ -25,7 +25,7 @@ include_directories( ${LLMESSAGE_INCLUDE_DIRS} ${LLRENDER_INCLUDE_DIRS} ${LLWINDOW_INCLUDE_DIRS} - ${LLCACHE_INCLUDE_DIRS} + ${LLFILESYSTEM_INCLUDE_DIRS} ${LLXML_INCLUDE_DIRS} ${LIBS_PREBUILD_DIR}/include/hunspell ) @@ -283,7 +283,7 @@ target_link_libraries(llui ${LLINVENTORY_LIBRARIES} ${LLMESSAGE_LIBRARIES} ${LLCOREHTTP_LIBRARIES} - ${LLCACHE_LIBRARIES} # ugh, just for LLDir + ${LLFILESYSTEM_LIBRARIES} ${LLXUIXML_LIBRARIES} ${LLXML_LIBRARIES} ${LLMATH_LIBRARIES} diff --git a/indra/llwindow/CMakeLists.txt b/indra/llwindow/CMakeLists.txt index 5603c2f322..70eb99c86c 100644 --- a/indra/llwindow/CMakeLists.txt +++ b/indra/llwindow/CMakeLists.txt @@ -16,7 +16,7 @@ include(LLCommon) include(LLImage) include(LLMath) include(LLRender) -include(LLCache) +include(LLFileSystem) include(LLWindow) include(LLXML) include(UI) @@ -26,7 +26,7 @@ include_directories( ${LLIMAGE_INCLUDE_DIRS} ${LLMATH_INCLUDE_DIRS} ${LLRENDER_INCLUDE_DIRS} - ${LLCACHE_INCLUDE_DIRS} + ${LLFILESYSTEM_INCLUDE_DIRS} ${LLWINDOW_INCLUDE_DIRS} ${LLXML_INCLUDE_DIRS} ) @@ -72,7 +72,7 @@ if (LINUX) ${LLIMAGE_LIBRARIES} ${LLMATH_LIBRARIES} ${LLRENDER_LIBRARIES} - ${LLCACHE_LIBRARIES} + ${LLFILESYSTEM_LIBRARIES} ${LLWINDOW_LIBRARIES} ${LLXML_LIBRARIES} ${UI_LIBRARIES} # for GTK @@ -95,7 +95,7 @@ if (LINUX) ${LLIMAGE_LIBRARIES} ${LLMATH_LIBRARIES} ${LLRENDER_HEADLESS_LIBRARIES} - ${LLCACHE_LIBRARIES} + ${LLFILESYSTEM_LIBRARIES} ${LLWINDOW_HEADLESS_LIBRARIES} ${LLXML_LIBRARIES} fontconfig # For FCInit and other FC* functions. diff --git a/indra/llxml/CMakeLists.txt b/indra/llxml/CMakeLists.txt index e2c6026496..3a7a54e51d 100644 --- a/indra/llxml/CMakeLists.txt +++ b/indra/llxml/CMakeLists.txt @@ -5,13 +5,13 @@ project(llxml) include(00-Common) include(LLCommon) include(LLMath) -include(LLCache) +include(LLFileSystem) include(LLXML) include_directories( ${LLCOMMON_INCLUDE_DIRS} ${LLMATH_INCLUDE_DIRS} - ${LLCACHE_INCLUDE_DIRS} + ${LLFILESYSTEM_INCLUDE_DIRS} ) include_directories( ${LLCOMMON_SYSTEM_INCLUDE_DIRS} @@ -42,7 +42,7 @@ add_library (llxml ${llxml_SOURCE_FILES}) # Libraries on which this library depends, needed for Linux builds # Sort by high-level to low-level target_link_libraries( llxml - ${LLCACHE_LIBRARIES} + ${LLFILESYSTEM_LIBRARIES} ${LLMATH_LIBRARIES} ${LLCOMMON_LIBRARIES} ${EXPAT_LIBRARIES} diff --git a/indra/mac_crash_logger/CMakeLists.txt b/indra/mac_crash_logger/CMakeLists.txt index 4a10076e6e..75621d7b75 100644 --- a/indra/mac_crash_logger/CMakeLists.txt +++ b/indra/mac_crash_logger/CMakeLists.txt @@ -8,7 +8,7 @@ include(LLCoreHttp) include(LLCrashLogger) include(LLMath) include(LLMessage) -include(LLCache) +include(LLFilesystem) include(LLXML) include(Linking) include(LLSharedLibs) @@ -19,7 +19,7 @@ include_directories( ${LLCOMMON_INCLUDE_DIRS} ${LLCRASHLOGGER_INCLUDE_DIRS} ${LLMATH_INCLUDE_DIRS} - ${LLCACHE_INCLUDE_DIRS} + ${LLFILESYSTEM_INCLUDE_DIRS} ${LLXML_INCLUDE_DIRS} ) include_directories(SYSTEM @@ -68,7 +68,7 @@ find_library(COCOA_LIBRARY Cocoa) target_link_libraries(mac-crash-logger ${LLCRASHLOGGER_LIBRARIES} - ${LLCACHE_LIBRARIES} + ${LLFILESYSTEM_LIBRARIES} ${COCOA_LIBRARIES} ${LLXML_LIBRARIES} ${LLMESSAGE_LIBRARIES} diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index ccf7ad675f..cda2672e12 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -39,7 +39,7 @@ include(LLPlugin) include(LLPrimitive) include(LLRender) include(LLUI) -include(LLCache) +include(LLFileSystem) include(LLWindow) include(LLXML) include(NDOF) @@ -84,7 +84,7 @@ include_directories( ${LLPRIMITIVE_INCLUDE_DIRS} ${LLRENDER_INCLUDE_DIRS} ${LLUI_INCLUDE_DIRS} - ${LLCACHE_INCLUDE_DIRS} + ${LLFILESYSTEM_INCLUDE_DIRS} ${LLWINDOW_INCLUDE_DIRS} ${LLXML_INCLUDE_DIRS} ${LLLOGIN_INCLUDE_DIRS} @@ -1995,7 +1995,7 @@ target_link_libraries(${VIEWER_BINARY_NAME} ${LLRENDER_LIBRARIES} ${FREETYPE_LIBRARIES} ${LLUI_LIBRARIES} - ${LLCACHE_LIBRARIES} + ${LLFILESYSTEM_LIBRARIES} ${LLWINDOW_LIBRARIES} ${LLXML_LIBRARIES} ${LLMATH_LIBRARIES} @@ -2467,7 +2467,7 @@ if (LL_TESTS) set(test_libs ${LLMESSAGE_LIBRARIES} ${WINDOWS_LIBRARIES} - ${LLCACHE_LIBRARIES} + ${LLFILESYSTEM_LIBRARIES} ${LLMATH_LIBRARIES} ${LLCOMMON_LIBRARIES} ${GOOGLEMOCK_LIBRARIES} @@ -2482,7 +2482,7 @@ if (LL_TESTS) set(test_libs ${WINDOWS_LIBRARIES} - ${LLCACHE_LIBRARIES} + ${LLFILESYSTEM_LIBRARIES} ${LLMATH_LIBRARIES} ${LLCOMMON_LIBRARIES} ${LLMESSAGE_LIBRARIES} diff --git a/indra/test/CMakeLists.txt b/indra/test/CMakeLists.txt index f8e701794c..b3bff3611a 100644 --- a/indra/test/CMakeLists.txt +++ b/indra/test/CMakeLists.txt @@ -8,12 +8,11 @@ include(LLCoreHttp) include(LLInventory) include(LLMath) include(LLMessage) -include(LLCache) +include(LLFileSystem) include(LLXML) include(Linking) include(Tut) include(LLAddBuildTest) - include(GoogleMock) include_directories( @@ -23,7 +22,7 @@ include_directories( ${LLMATH_INCLUDE_DIRS} ${LLMESSAGE_INCLUDE_DIRS} ${LLINVENTORY_INCLUDE_DIRS} - ${LLCACHE_INCLUDE_DIRS} + ${LLFILESYSTEM_INCLUDE_DIRS} ${LLXML_INCLUDE_DIRS} ${LSCRIPT_INCLUDE_DIRS} ${GOOGLEMOCK_INCLUDE_DIRS} @@ -89,7 +88,7 @@ target_link_libraries(lltest ${LLINVENTORY_LIBRARIES} ${LLMESSAGE_LIBRARIES} ${LLMATH_LIBRARIES} - ${LLCACHE_LIBRARIES} + ${LLFILESYSTEM_LIBRARIES} ${LLXML_LIBRARIES} ${LSCRIPT_LIBRARIES} ${LLCOMMON_LIBRARIES} diff --git a/indra/win_crash_logger/CMakeLists.txt b/indra/win_crash_logger/CMakeLists.txt index 811ec00a3d..1b187d624a 100644 --- a/indra/win_crash_logger/CMakeLists.txt +++ b/indra/win_crash_logger/CMakeLists.txt @@ -8,7 +8,7 @@ include(LLCoreHttp) include(LLCrashLogger) include(LLMath) include(LLMessage) -include(LLCache) +include(LLFileSystem) include(LLWindow) include(LLXML) include(Linking) @@ -23,7 +23,7 @@ include_directories( ${LLMATH_INCLUDE_DIRS} ${LLWINDOW_INCLUDE_DIRS} ${LLXML_INCLUDE_DIRS} - ${LLCACHE_INCLUDE_DIRS} + ${LLFILESYSTEM_INCLUDE_DIRS} ${BREAKPAD_INCLUDE_DIRECTORIES} ) include_directories(SYSTEM @@ -76,7 +76,7 @@ target_link_libraries(windows-crash-logger ${BREAKPAD_EXCEPTION_HANDLER_LIBRARIES} ${LLCRASHLOGGER_LIBRARIES} ${LLWINDOW_LIBRARIES} - ${LLCACHE_LIBRARIES} + ${LLFILESYSTEM_LIBRARIES} ${LLXML_LIBRARIES} ${LLMESSAGE_LIBRARIES} ${LLMATH_LIBRARIES} From d9d1b12f5f3d6f8cbb0780ef8c0aa7844972bbe2 Mon Sep 17 00:00:00 2001 From: Callum Prentice Date: Thu, 17 Sep 2020 11:28:37 -0700 Subject: [PATCH 07/85] Some small changes to replace 'vfs' with 'filesystem' outside of my dev env --- indra/integration_tests/llimage_libtest/CMakeLists.txt | 6 +++--- indra/integration_tests/llui_libtest/CMakeLists.txt | 4 ++-- indra/linux_crash_logger/CMakeLists.txt | 7 +++---- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/indra/integration_tests/llimage_libtest/CMakeLists.txt b/indra/integration_tests/llimage_libtest/CMakeLists.txt index 5787d4d600..bd59f57e49 100644 --- a/indra/integration_tests/llimage_libtest/CMakeLists.txt +++ b/indra/integration_tests/llimage_libtest/CMakeLists.txt @@ -10,11 +10,11 @@ include(LLImage) include(LLMath) include(LLImageJ2COJ) include(LLKDU) -include(LLVFS) +include(LLFileSystem) include_directories( ${LLCOMMON_INCLUDE_DIRS} - ${LLVFS_INCLUDE_DIRS} + ${LLFILESYSTEM_INCLUDE_DIRS} ${LLIMAGE_INCLUDE_DIRS} ${LLMATH_INCLUDE_DIRS} ) @@ -66,7 +66,7 @@ endif (DARWIN) target_link_libraries(llimage_libtest ${LEGACY_STDIO_LIBS} ${LLCOMMON_LIBRARIES} - ${LLVFS_LIBRARIES} + ${LLFILESYSTEM_LIBRARIES} ${LLMATH_LIBRARIES} ${LLIMAGE_LIBRARIES} ${LLKDU_LIBRARIES} diff --git a/indra/integration_tests/llui_libtest/CMakeLists.txt b/indra/integration_tests/llui_libtest/CMakeLists.txt index 1cec660eb0..d7706e73b2 100644 --- a/indra/integration_tests/llui_libtest/CMakeLists.txt +++ b/indra/integration_tests/llui_libtest/CMakeLists.txt @@ -16,7 +16,7 @@ include(LLMessage) include(LLRender) include(LLWindow) include(LLUI) -include(LLVFS) # ugh, needed for LLDir +include(LLFileSystem) include(LLXML) include(Hunspell) include(Linking) @@ -29,7 +29,7 @@ include_directories( ${LLMATH_INCLUDE_DIRS} ${LLRENDER_INCLUDE_DIRS} ${LLUI_INCLUDE_DIRS} - ${LLVFS_INCLUDE_DIRS} + ${LLFILESYSTEM_INCLUDE_DIRS} ${LLWINDOW_INCLUDE_DIRS} ${LLXML_INCLUDE_DIRS} ${LIBS_PREBUILD_DIR}/include/hunspell diff --git a/indra/linux_crash_logger/CMakeLists.txt b/indra/linux_crash_logger/CMakeLists.txt index d789c850a0..aa82ed12cc 100644 --- a/indra/linux_crash_logger/CMakeLists.txt +++ b/indra/linux_crash_logger/CMakeLists.txt @@ -9,7 +9,7 @@ include(LLCommon) include(LLCrashLogger) include(LLMath) include(LLMessage) -include(LLVFS) +include(LLFileSystem) include(LLXML) include(Linking) include(UI) @@ -21,7 +21,7 @@ include_directories( ${LLCOMMON_INCLUDE_DIRS} ${LLCRASHLOGGER_INCLUDE_DIRS} ${LLMATH_INCLUDE_DIRS} - ${LLVFS_INCLUDE_DIRS} + ${LLFILESYSTEM_INCLUDE_DIRS} ${LLXML_INCLUDE_DIRS} ${FREETYPE_INCLUDE_DIRS} ) @@ -62,10 +62,9 @@ set(LIBRT_LIBRARY rt) target_link_libraries(linux-crash-logger ${LLCRASHLOGGER_LIBRARIES} - ${LLVFS_LIBRARIES} + ${LLFILESYSTEM_LIBRARIES} ${LLXML_LIBRARIES} ${LLMESSAGE_LIBRARIES} - ${LLVFS_LIBRARIES} ${LLMATH_LIBRARIES} ${LLCOREHTTP_LIBRARIES} ${LLCOMMON_LIBRARIES} From 14b5d490c37d234db7a52295502b130723186f3c Mon Sep 17 00:00:00 2001 From: Callum Prentice Date: Thu, 17 Sep 2020 13:17:27 -0700 Subject: [PATCH 08/85] Remove references to VFS in the non-English language XML files (already removed from English (en) versions) --- indra/newview/skins/default/xui/da/floater_stats.xml | 1 - indra/newview/skins/default/xui/da/strings.xml | 3 --- .../newview/skins/default/xui/de/floater_scene_load_stats.xml | 1 - indra/newview/skins/default/xui/de/floater_stats.xml | 1 - indra/newview/skins/default/xui/de/strings.xml | 4 ---- .../newview/skins/default/xui/es/floater_scene_load_stats.xml | 1 - indra/newview/skins/default/xui/es/floater_stats.xml | 1 - indra/newview/skins/default/xui/es/strings.xml | 4 ---- .../newview/skins/default/xui/fr/floater_scene_load_stats.xml | 1 - indra/newview/skins/default/xui/fr/floater_stats.xml | 1 - indra/newview/skins/default/xui/fr/strings.xml | 4 ---- .../newview/skins/default/xui/it/floater_scene_load_stats.xml | 1 - indra/newview/skins/default/xui/it/floater_stats.xml | 1 - indra/newview/skins/default/xui/it/strings.xml | 4 ---- .../newview/skins/default/xui/ja/floater_scene_load_stats.xml | 1 - indra/newview/skins/default/xui/ja/floater_stats.xml | 1 - indra/newview/skins/default/xui/ja/strings.xml | 4 ---- .../newview/skins/default/xui/pl/floater_scene_load_stats.xml | 1 - indra/newview/skins/default/xui/pl/floater_stats.xml | 1 - indra/newview/skins/default/xui/pl/strings.xml | 3 --- .../newview/skins/default/xui/pt/floater_scene_load_stats.xml | 1 - indra/newview/skins/default/xui/pt/floater_stats.xml | 1 - indra/newview/skins/default/xui/pt/strings.xml | 4 ---- .../newview/skins/default/xui/ru/floater_scene_load_stats.xml | 1 - indra/newview/skins/default/xui/ru/floater_stats.xml | 1 - indra/newview/skins/default/xui/ru/strings.xml | 4 ---- .../newview/skins/default/xui/tr/floater_scene_load_stats.xml | 1 - indra/newview/skins/default/xui/tr/floater_stats.xml | 1 - indra/newview/skins/default/xui/tr/strings.xml | 4 ---- .../newview/skins/default/xui/zh/floater_scene_load_stats.xml | 1 - indra/newview/skins/default/xui/zh/floater_stats.xml | 1 - indra/newview/skins/default/xui/zh/strings.xml | 4 ---- 32 files changed, 63 deletions(-) diff --git a/indra/newview/skins/default/xui/da/floater_stats.xml b/indra/newview/skins/default/xui/da/floater_stats.xml index fe3fa9626e..d07f9e48ca 100644 --- a/indra/newview/skins/default/xui/da/floater_stats.xml +++ b/indra/newview/skins/default/xui/da/floater_stats.xml @@ -32,7 +32,6 @@ - diff --git a/indra/newview/skins/default/xui/da/strings.xml b/indra/newview/skins/default/xui/da/strings.xml index ec6ba4800d..814305c1bc 100644 --- a/indra/newview/skins/default/xui/da/strings.xml +++ b/indra/newview/skins/default/xui/da/strings.xml @@ -22,9 +22,6 @@ Initialiserer tekstur cache... - - Initialiserer VFS... - Gendanner... diff --git a/indra/newview/skins/default/xui/de/floater_scene_load_stats.xml b/indra/newview/skins/default/xui/de/floater_scene_load_stats.xml index dff462a594..a3749f1cfa 100644 --- a/indra/newview/skins/default/xui/de/floater_scene_load_stats.xml +++ b/indra/newview/skins/default/xui/de/floater_scene_load_stats.xml @@ -29,7 +29,6 @@ - diff --git a/indra/newview/skins/default/xui/de/floater_stats.xml b/indra/newview/skins/default/xui/de/floater_stats.xml index 4e6f56cd94..1838c2081a 100644 --- a/indra/newview/skins/default/xui/de/floater_stats.xml +++ b/indra/newview/skins/default/xui/de/floater_stats.xml @@ -56,7 +56,6 @@ - diff --git a/indra/newview/skins/default/xui/de/strings.xml b/indra/newview/skins/default/xui/de/strings.xml index 7db76ec552..a0cff646d9 100644 --- a/indra/newview/skins/default/xui/de/strings.xml +++ b/indra/newview/skins/default/xui/de/strings.xml @@ -31,9 +31,6 @@ Textur-Cache wird initialisiert... - - VFS wird initialisiert... - Grafikinitialisierung fehlgeschlagen. Bitte aktualisieren Sie Ihren Grafiktreiber. @@ -73,7 +70,6 @@ LOD-Faktor: [LOD_FACTOR] Darstellungsqualität: [RENDER_QUALITY] Erweitertes Beleuchtungsmodell: [GPU_SHADERS] Texturspeicher: [TEXTURE_MEMORY] MB -Erstellungszeit VFS (Cache): [VFS_TIME] HiDPI-Anzeigemodus: [HIDPI] diff --git a/indra/newview/skins/default/xui/es/floater_scene_load_stats.xml b/indra/newview/skins/default/xui/es/floater_scene_load_stats.xml index f625d5257c..cfc5e524b5 100644 --- a/indra/newview/skins/default/xui/es/floater_scene_load_stats.xml +++ b/indra/newview/skins/default/xui/es/floater_scene_load_stats.xml @@ -29,7 +29,6 @@ - diff --git a/indra/newview/skins/default/xui/es/floater_stats.xml b/indra/newview/skins/default/xui/es/floater_stats.xml index d1c5e867db..0aec786f25 100644 --- a/indra/newview/skins/default/xui/es/floater_stats.xml +++ b/indra/newview/skins/default/xui/es/floater_stats.xml @@ -56,7 +56,6 @@ - diff --git a/indra/newview/skins/default/xui/es/strings.xml b/indra/newview/skins/default/xui/es/strings.xml index af66907f8d..45fd004c5c 100644 --- a/indra/newview/skins/default/xui/es/strings.xml +++ b/indra/newview/skins/default/xui/es/strings.xml @@ -22,9 +22,6 @@ Iniciando la caché de las texturas... - - Iniciando VFS... - Error de inicialización de gráficos. Actualiza tu controlador de gráficos. @@ -65,7 +62,6 @@ Factor LOD: [LOD_FACTOR] Calidad de renderización: [RENDER_QUALITY] Modelo de iluminación avanzado: [GPU_SHADERS] Memoria de textura: [TEXTURE_MEMORY]MB -VFS (cache) hora de creación: [VFS_TIME] Modo de visualización HiDPi: [HIDPI] diff --git a/indra/newview/skins/default/xui/fr/floater_scene_load_stats.xml b/indra/newview/skins/default/xui/fr/floater_scene_load_stats.xml index 62830054bf..3889b13f0c 100644 --- a/indra/newview/skins/default/xui/fr/floater_scene_load_stats.xml +++ b/indra/newview/skins/default/xui/fr/floater_scene_load_stats.xml @@ -29,7 +29,6 @@ - diff --git a/indra/newview/skins/default/xui/fr/floater_stats.xml b/indra/newview/skins/default/xui/fr/floater_stats.xml index fae17e3608..d0f7f42939 100644 --- a/indra/newview/skins/default/xui/fr/floater_stats.xml +++ b/indra/newview/skins/default/xui/fr/floater_stats.xml @@ -56,7 +56,6 @@ - diff --git a/indra/newview/skins/default/xui/fr/strings.xml b/indra/newview/skins/default/xui/fr/strings.xml index 6d40ab4bc9..8c55222f79 100644 --- a/indra/newview/skins/default/xui/fr/strings.xml +++ b/indra/newview/skins/default/xui/fr/strings.xml @@ -31,9 +31,6 @@ Initialisation du cache des textures... - - Initialisation VFS... - Échec d'initialisation des graphiques. Veuillez mettre votre pilote graphique à jour. @@ -74,7 +71,6 @@ Facteur LOD (niveau de détail) : [LOD_FACTOR] Qualité de rendu : [RENDER_QUALITY] Modèle d’éclairage avancé : [GPU_SHADERS] Mémoire textures : [TEXTURE_MEMORY] Mo -Heure de création VFS (cache) : [VFS_TIME] Mode d'affichage HiDPI : [HIDPI] diff --git a/indra/newview/skins/default/xui/it/floater_scene_load_stats.xml b/indra/newview/skins/default/xui/it/floater_scene_load_stats.xml index ca18812eb7..efceb05298 100644 --- a/indra/newview/skins/default/xui/it/floater_scene_load_stats.xml +++ b/indra/newview/skins/default/xui/it/floater_scene_load_stats.xml @@ -29,7 +29,6 @@ - diff --git a/indra/newview/skins/default/xui/it/floater_stats.xml b/indra/newview/skins/default/xui/it/floater_stats.xml index 7ef13aa37f..6434c3b66b 100644 --- a/indra/newview/skins/default/xui/it/floater_stats.xml +++ b/indra/newview/skins/default/xui/it/floater_stats.xml @@ -56,7 +56,6 @@ - diff --git a/indra/newview/skins/default/xui/it/strings.xml b/indra/newview/skins/default/xui/it/strings.xml index ca486f832e..cb4144b238 100644 --- a/indra/newview/skins/default/xui/it/strings.xml +++ b/indra/newview/skins/default/xui/it/strings.xml @@ -28,9 +28,6 @@ Inizializzazione della cache texture... - - Inizializzazione VFS... - Inizializzazione grafica non riuscita. Aggiorna il driver della scheda grafica! @@ -71,7 +68,6 @@ Fattore livello di dettaglio: [LOD_FACTOR] Qualità di rendering: [RENDER_QUALITY] Modello illuminazione avanzato: [GPU_SHADERS] Memoria texture: [TEXTURE_MEMORY]MB -Data/ora creazione VFS (cache): [VFS_TIME] Modalità display HiDPI: [HIDPI] diff --git a/indra/newview/skins/default/xui/ja/floater_scene_load_stats.xml b/indra/newview/skins/default/xui/ja/floater_scene_load_stats.xml index f6edce026f..f348ce3c4d 100644 --- a/indra/newview/skins/default/xui/ja/floater_scene_load_stats.xml +++ b/indra/newview/skins/default/xui/ja/floater_scene_load_stats.xml @@ -29,7 +29,6 @@ - diff --git a/indra/newview/skins/default/xui/ja/floater_stats.xml b/indra/newview/skins/default/xui/ja/floater_stats.xml index 3bc343639b..1da0e5ebc9 100644 --- a/indra/newview/skins/default/xui/ja/floater_stats.xml +++ b/indra/newview/skins/default/xui/ja/floater_stats.xml @@ -56,7 +56,6 @@ - diff --git a/indra/newview/skins/default/xui/ja/strings.xml b/indra/newview/skins/default/xui/ja/strings.xml index 3f46376595..3dc201f3e7 100644 --- a/indra/newview/skins/default/xui/ja/strings.xml +++ b/indra/newview/skins/default/xui/ja/strings.xml @@ -31,9 +31,6 @@ テクスチャキャッシュを初期化中です... - - VFS を初期化中です... - グラフィックを初期化できませんでした。グラフィックドライバを更新してください。 @@ -74,7 +71,6 @@ LOD 係数: [LOD_FACTOR] 描画の質: [RENDER_QUALITY] 高度なライティングモデル: [GPU_SHADERS] テクスチャメモリ: [TEXTURE_MEMORY]MB -VFS(キャッシュ)作成時間: [VFS_TIME] HiDPI 表示モード: [HIDPI] diff --git a/indra/newview/skins/default/xui/pl/floater_scene_load_stats.xml b/indra/newview/skins/default/xui/pl/floater_scene_load_stats.xml index 6fdc7e19f6..8f5d0c5c70 100644 --- a/indra/newview/skins/default/xui/pl/floater_scene_load_stats.xml +++ b/indra/newview/skins/default/xui/pl/floater_scene_load_stats.xml @@ -29,7 +29,6 @@ - diff --git a/indra/newview/skins/default/xui/pl/floater_stats.xml b/indra/newview/skins/default/xui/pl/floater_stats.xml index 5dd7d19bab..21e37717c2 100644 --- a/indra/newview/skins/default/xui/pl/floater_stats.xml +++ b/indra/newview/skins/default/xui/pl/floater_stats.xml @@ -55,7 +55,6 @@ - diff --git a/indra/newview/skins/default/xui/pl/strings.xml b/indra/newview/skins/default/xui/pl/strings.xml index 91fea234d2..cf033df3c9 100644 --- a/indra/newview/skins/default/xui/pl/strings.xml +++ b/indra/newview/skins/default/xui/pl/strings.xml @@ -15,9 +15,6 @@ Inicjowanie bufora danych tekstur... - - Inicjowanie wirtualnego systemu plików... - Nie można zainicjować grafiki. Zaktualizuj sterowniki! diff --git a/indra/newview/skins/default/xui/pt/floater_scene_load_stats.xml b/indra/newview/skins/default/xui/pt/floater_scene_load_stats.xml index 027e1ef311..dbaab1d782 100644 --- a/indra/newview/skins/default/xui/pt/floater_scene_load_stats.xml +++ b/indra/newview/skins/default/xui/pt/floater_scene_load_stats.xml @@ -29,7 +29,6 @@ - diff --git a/indra/newview/skins/default/xui/pt/floater_stats.xml b/indra/newview/skins/default/xui/pt/floater_stats.xml index f41fe17778..3253984268 100644 --- a/indra/newview/skins/default/xui/pt/floater_stats.xml +++ b/indra/newview/skins/default/xui/pt/floater_stats.xml @@ -56,7 +56,6 @@ - diff --git a/indra/newview/skins/default/xui/pt/strings.xml b/indra/newview/skins/default/xui/pt/strings.xml index 6b86c4330c..a12e70a999 100644 --- a/indra/newview/skins/default/xui/pt/strings.xml +++ b/indra/newview/skins/default/xui/pt/strings.xml @@ -22,9 +22,6 @@ Iniciando cache de texturas... - - Iniciando VFS... - Falha na inicialização dos gráficos. Atualize seu driver gráfico! @@ -65,7 +62,6 @@ LOD fator: [LOD_FACTOR] Qualidade de renderização: [RENDER_QUALITY] Modelo avançado de luzes: [GPU_SHADERS] Memória de textura: [TEXTURE_MEMORY]MB -VFS (cache) tempo de criação: [VFS_TIME] HiDPI modo de exibição: [HIDPI] diff --git a/indra/newview/skins/default/xui/ru/floater_scene_load_stats.xml b/indra/newview/skins/default/xui/ru/floater_scene_load_stats.xml index a101e62627..c4f432023c 100644 --- a/indra/newview/skins/default/xui/ru/floater_scene_load_stats.xml +++ b/indra/newview/skins/default/xui/ru/floater_scene_load_stats.xml @@ -29,7 +29,6 @@ - diff --git a/indra/newview/skins/default/xui/ru/floater_stats.xml b/indra/newview/skins/default/xui/ru/floater_stats.xml index 10e9f5a7f4..a7d26029c2 100644 --- a/indra/newview/skins/default/xui/ru/floater_stats.xml +++ b/indra/newview/skins/default/xui/ru/floater_stats.xml @@ -56,7 +56,6 @@ - diff --git a/indra/newview/skins/default/xui/ru/strings.xml b/indra/newview/skins/default/xui/ru/strings.xml index edcf9d3e00..0519a4fa11 100644 --- a/indra/newview/skins/default/xui/ru/strings.xml +++ b/indra/newview/skins/default/xui/ru/strings.xml @@ -31,9 +31,6 @@ Инициализация кэша текстур... - - Инициализация виртуальной файловой системы... - Ошибка инициализации графики. Обновите графический драйвер! @@ -74,7 +71,6 @@ SLURL: <nolink>[SLURL]</nolink> Качество визуализации: [RENDER_QUALITY] Расширенная модель освещения: [GPU_SHADERS] Память текстур: [TEXTURE_MEMORY] МБ -Время создания VFS (кэш): [VFS_TIME] Режим отображения HiDPI: [HIDPI] diff --git a/indra/newview/skins/default/xui/tr/floater_scene_load_stats.xml b/indra/newview/skins/default/xui/tr/floater_scene_load_stats.xml index ae0a94595d..7d5f4adb02 100644 --- a/indra/newview/skins/default/xui/tr/floater_scene_load_stats.xml +++ b/indra/newview/skins/default/xui/tr/floater_scene_load_stats.xml @@ -29,7 +29,6 @@ - diff --git a/indra/newview/skins/default/xui/tr/floater_stats.xml b/indra/newview/skins/default/xui/tr/floater_stats.xml index 1ae42ad382..bd36d4916f 100644 --- a/indra/newview/skins/default/xui/tr/floater_stats.xml +++ b/indra/newview/skins/default/xui/tr/floater_stats.xml @@ -56,7 +56,6 @@ - diff --git a/indra/newview/skins/default/xui/tr/strings.xml b/indra/newview/skins/default/xui/tr/strings.xml index 3fd466d71c..e45adc4f8e 100644 --- a/indra/newview/skins/default/xui/tr/strings.xml +++ b/indra/newview/skins/default/xui/tr/strings.xml @@ -31,9 +31,6 @@ Doku önbelleği başlatılıyor... - - VFS Başlatılıyor... - Grafik başlatma başarılamadı. Lütfen grafik sürücünüzü güncelleştirin! @@ -74,7 +71,6 @@ LOD faktörü: [LOD_FACTOR] İşleme kalitesi: [RENDER_QUALITY] Gelişmiş Aydınlatma Modeli: [GPU_SHADERS] Doku belleği: [TEXTURE_MEMORY]MB -VFS (önbellek) oluşturma saati: [VFS_TIME] HiDPI görüntü modu: [HIDPI] diff --git a/indra/newview/skins/default/xui/zh/floater_scene_load_stats.xml b/indra/newview/skins/default/xui/zh/floater_scene_load_stats.xml index 1a5c20abeb..20344e299f 100644 --- a/indra/newview/skins/default/xui/zh/floater_scene_load_stats.xml +++ b/indra/newview/skins/default/xui/zh/floater_scene_load_stats.xml @@ -29,7 +29,6 @@ - diff --git a/indra/newview/skins/default/xui/zh/floater_stats.xml b/indra/newview/skins/default/xui/zh/floater_stats.xml index f06eb5e78f..e233ece527 100644 --- a/indra/newview/skins/default/xui/zh/floater_stats.xml +++ b/indra/newview/skins/default/xui/zh/floater_stats.xml @@ -56,7 +56,6 @@ - diff --git a/indra/newview/skins/default/xui/zh/strings.xml b/indra/newview/skins/default/xui/zh/strings.xml index e6c61a5d94..3221cde3b7 100644 --- a/indra/newview/skins/default/xui/zh/strings.xml +++ b/indra/newview/skins/default/xui/zh/strings.xml @@ -31,9 +31,6 @@ 正在初始化材質快取... - - VFS 初始化中... - 顯像初始化失敗。 請更新你的顯像卡驅動程式! @@ -74,7 +71,6 @@ 呈像品質:[RENDER_QUALITY] 進階照明模型:[GPU_SHADERS] 材質記憶體:[TEXTURE_MEMORY]MB -VFS(快取)建立時間:[VFS_TIME] HiDPI顯示模式:[HIDPI] From 5858bb87c7d9d222e61c7c4f7f0f1dc8ba02c77f Mon Sep 17 00:00:00 2001 From: Callum Prentice Date: Tue, 22 Sep 2020 16:52:10 -0700 Subject: [PATCH 09/85] Add SQLite third package to this viewer and pull in the most recent build (3.33) --- autobuild.xml | 56 +++++++++++++++++++ indra/cmake/SQLite.cmake | 11 ++++ indra/llfilesystem/CMakeLists.txt | 3 + indra/newview/CMakeLists.txt | 2 + indra/newview/llappviewer.cpp | 10 ++++ .../newview/skins/default/xui/en/strings.xml | 1 + 6 files changed, 83 insertions(+) create mode 100644 indra/cmake/SQLite.cmake diff --git a/autobuild.xml b/autobuild.xml index eacf11fb0f..6abb089455 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -3065,6 +3065,62 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors version 4.10.0000.32327.5fc3fe7c.539691 + sqlite + + canonical_repo + https://bitbucket.org/lindenlab/3p-sqlite + copyright + Copyright (C) 1995-2013 Jean-loup Gailly and Mark Adler + description + SQLite is a C-language library that implements a small, fast, self-contained, high-reliability, full-featured, SQL database engine. + license + Public Domain + license_file + LICENSES/sqlite_copyright.html + name + sqlite + platforms + + darwin64 + + archive + + hash + 31cb0e0b1557660691766441ba966f10 + url + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/69032/665217/sqlite-3.33.0-darwin64-549465.tar.bz2 + + name + darwin64 + + windows + + archive + + hash + 4102b91b473812ba4619ed3bfefb7de9 + url + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/69040/665286/sqlite-3.33.0-windows-549465.tar.bz2 + + name + windows + + windows64 + + archive + + hash + 0e9a0ae93d749dc8eeadf2edb293b291 + url + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/69039/665277/sqlite-3.33.0-windows64-549465.tar.bz2 + + name + windows64 + + + version + 3.33.0 + tut copyright diff --git a/indra/cmake/SQLite.cmake b/indra/cmake/SQLite.cmake new file mode 100644 index 0000000000..3571ca7d1e --- /dev/null +++ b/indra/cmake/SQLite.cmake @@ -0,0 +1,11 @@ +# -*- cmake -*- +include(Prebuilt) + +if (USESYSTEMLIBS) + include(FindPkgConfig) + pkg_check_modules(SQLITE REQUIRED sqlite3) +else (USESYSTEMLIBS) + use_prebuilt_binary(sqlite) + set(SQLITE_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/include/sqlite/) + set(SQLITE_LIBRARIES sqlite) +endif (USESYSTEMLIBS) diff --git a/indra/llfilesystem/CMakeLists.txt b/indra/llfilesystem/CMakeLists.txt index 4af14d6d3a..306b483097 100644 --- a/indra/llfilesystem/CMakeLists.txt +++ b/indra/llfilesystem/CMakeLists.txt @@ -4,11 +4,13 @@ project(llfilesystem) include(00-Common) include(LLCommon) +include(SQLite) include(UnixInstall) include_directories( ${LLCOMMON_INCLUDE_DIRS} ${LLCOMMON_SYSTEM_INCLUDE_DIRS} + ${SQLITE_INCLUDE_DIR} ) set(llfilesystem_SOURCE_FILES @@ -67,6 +69,7 @@ set(cache_BOOST_LIBRARIES target_link_libraries(llfilesystem ${LLCOMMON_LIBRARIES} ${cache_BOOST_LIBRARIES} + ${SQLITE_LIBRARIES} ) if (DARWIN) diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 9fe89c1a19..129e436d5f 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -48,6 +48,7 @@ include(OPENAL) include(OpenGL) include(OpenSSL) include(PNG) +include(SQLite) include(TemplateCheck) include(UI) include(UnixInstall) @@ -70,6 +71,7 @@ include_directories( ${DBUSGLIB_INCLUDE_DIRS} ${JSONCPP_INCLUDE_DIR} ${GLOD_INCLUDE_DIR} + ${SQLITE_INCLUDE_DIR} ${LLAUDIO_INCLUDE_DIRS} ${LLCHARACTER_INCLUDE_DIRS} ${LLCOMMON_INCLUDE_DIRS} diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index eca5aafa55..6181683b9e 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -130,6 +130,7 @@ #if !LL_LINUX #include "cef/dullahan_version.h" #include "vlc/libvlc_version.h" +#include "sqlite3.h" #endif // LL_LINUX // Third party library includes @@ -3178,6 +3179,15 @@ LLSD LLAppViewer::getViewerInfo() const info["LIBCEF_VERSION"] = "Undefined"; #endif +#if !LL_LINUX + std::ostringstream sqlite_ver_codec; + sqlite_ver_codec << "SQLite: "; + sqlite_ver_codec << SQLITE_VERSION; + info["SQLITE_VERSION"] = sqlite_ver_codec.str(); +#else + info["SQLITE_VERSION"] = "Undefined"; +#endif + #if !LL_LINUX std::ostringstream vlc_ver_codec; vlc_ver_codec << LIBVLC_VERSION_MAJOR; diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml index 03aed8aa7e..bca577c48a 100644 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -62,6 +62,7 @@ HiDPI display mode: [HIDPI] J2C Decoder Version: [J2C_VERSION] Audio Driver Version: [AUDIO_DRIVER_VERSION] +[SQLITE_VERSION] [LIBCEF_VERSION] LibVLC Version: [LIBVLC_VERSION] Voice Server Version: [VOICE_VERSION] From 96e2873bfa2c6b8823aed3b4190c43cd5dab54e6 Mon Sep 17 00:00:00 2001 From: Callum Prentice Date: Thu, 24 Sep 2020 10:23:39 -0700 Subject: [PATCH 10/85] Rename lldiskcache.* to llfilesystem.* - i think this is the right name since it's responsible for performing file system operations and (will eventually) delegrate to a separate disk cache component to save/load data and keep track of metadata etc. --- indra/llfilesystem/{lldiskcache.cpp => llfilesystem.cpp} | 0 indra/llfilesystem/{lldiskcache.h => llfilesystem.h} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename indra/llfilesystem/{lldiskcache.cpp => llfilesystem.cpp} (100%) rename indra/llfilesystem/{lldiskcache.h => llfilesystem.h} (100%) diff --git a/indra/llfilesystem/lldiskcache.cpp b/indra/llfilesystem/llfilesystem.cpp similarity index 100% rename from indra/llfilesystem/lldiskcache.cpp rename to indra/llfilesystem/llfilesystem.cpp diff --git a/indra/llfilesystem/lldiskcache.h b/indra/llfilesystem/llfilesystem.h similarity index 100% rename from indra/llfilesystem/lldiskcache.h rename to indra/llfilesystem/llfilesystem.h From 6be1f88a5ef99e1e40bb5701a250ba0728f56005 Mon Sep 17 00:00:00 2001 From: Callum Prentice Date: Thu, 24 Sep 2020 14:45:39 -0700 Subject: [PATCH 11/85] Complete the change from lldiskcache -> llfilesystem and then addition of new lldiskcache implementation --- indra/llappearance/lltexlayer.cpp | 1 - indra/llaudio/llaudiodecodemgr.cpp | 14 +- indra/llaudio/llaudioengine.cpp | 4 +- indra/llcharacter/llkeyframemotion.cpp | 6 +- indra/llfilesystem/CMakeLists.txt | 3 +- indra/llfilesystem/lldiskcache.cpp | 773 ++++++++++++++++++++++ indra/llfilesystem/lldiskcache.h | 130 ++++ indra/llfilesystem/llfilesystem.cpp | 79 ++- indra/llfilesystem/llfilesystem.h | 19 +- indra/llmessage/llassetstorage.cpp | 28 +- indra/llmessage/llcorehttputil.cpp | 4 +- indra/llmessage/lltransfersourceasset.cpp | 6 +- indra/llmessage/lltransfersourceasset.h | 2 +- indra/llmessage/lltransfertargetvfile.cpp | 8 +- indra/llmessage/lltransfertargetvfile.h | 4 +- indra/llmessage/llxfer_vfile.cpp | 22 +- indra/llmessage/llxfer_vfile.h | 4 +- indra/llui/llviewereventrecorder.h | 1 - indra/newview/llappviewer.cpp | 6 +- indra/newview/llcompilequeue.cpp | 2 +- indra/newview/llfloaterauction.cpp | 6 +- indra/newview/llfloaterbvhpreview.cpp | 4 +- indra/newview/llfloatermodelpreview.cpp | 2 +- indra/newview/llfloaterregioninfo.cpp | 4 +- indra/newview/llfloaterreporter.cpp | 4 +- indra/newview/llfloatertos.cpp | 2 +- indra/newview/llgesturemgr.cpp | 4 +- indra/newview/lllandmarklist.cpp | 4 +- indra/newview/llmeshrepository.cpp | 22 +- indra/newview/lloutfitgallery.cpp | 2 +- indra/newview/llpostcard.cpp | 2 +- indra/newview/llpreviewgesture.cpp | 6 +- indra/newview/llpreviewnotecard.cpp | 10 +- indra/newview/llpreviewscript.cpp | 6 +- indra/newview/llsettingsvo.cpp | 4 +- indra/newview/llsnapshotlivepreview.cpp | 4 +- indra/newview/llviewerassetstorage.cpp | 12 +- indra/newview/llviewerassetstorage.h | 2 +- indra/newview/llviewerassetupload.cpp | 8 +- indra/newview/llviewermenufile.cpp | 1 - indra/newview/llviewermessage.cpp | 4 +- indra/newview/llviewerstats.cpp | 1 - indra/newview/llviewertexlayer.cpp | 1 - indra/newview/llviewertexture.cpp | 1 - indra/newview/llviewertexture.h | 2 +- indra/newview/llviewertexturelist.cpp | 2 +- 46 files changed, 1068 insertions(+), 168 deletions(-) create mode 100644 indra/llfilesystem/lldiskcache.cpp create mode 100644 indra/llfilesystem/lldiskcache.h diff --git a/indra/llappearance/lltexlayer.cpp b/indra/llappearance/lltexlayer.cpp index 7360c1acd7..1348fb0763 100644 --- a/indra/llappearance/lltexlayer.cpp +++ b/indra/llappearance/lltexlayer.cpp @@ -33,7 +33,6 @@ #include "llimagej2c.h" #include "llimagetga.h" #include "lldir.h" -#include "lldiskcache.h" #include "lltexlayerparams.h" #include "lltexturemanagerbridge.h" #include "lllocaltextureobject.h" diff --git a/indra/llaudio/llaudiodecodemgr.cpp b/indra/llaudio/llaudiodecodemgr.cpp index fcffea42a4..ff0aa6e76e 100644 --- a/indra/llaudio/llaudiodecodemgr.cpp +++ b/indra/llaudio/llaudiodecodemgr.cpp @@ -29,7 +29,7 @@ #include "llaudioengine.h" #include "lllfsthread.h" -#include "lldiskcache.h" +#include "llfilesystem.h" #include "llstring.h" #include "lldir.h" #include "llendianswizzle.h" @@ -93,14 +93,14 @@ protected: std::string mOutFilename; LLLFSThread::handle_t mFileHandle; - LLDiskCache *mInFilep; + LLFileSystem *mInFilep; OggVorbis_File mVF; S32 mCurrentSection; }; size_t cache_read(void *ptr, size_t size, size_t nmemb, void *datasource) { - LLDiskCache *file = (LLDiskCache *)datasource; + LLFileSystem *file = (LLFileSystem *)datasource; if (file->read((U8*)ptr, (S32)(size * nmemb))) /*Flawfinder: ignore*/ { @@ -115,7 +115,7 @@ size_t cache_read(void *ptr, size_t size, size_t nmemb, void *datasource) S32 cache_seek(void *datasource, ogg_int64_t offset, S32 whence) { - LLDiskCache *file = (LLDiskCache *)datasource; + LLFileSystem *file = (LLFileSystem *)datasource; // cache has 31-bit files if (offset > S32_MAX) @@ -151,14 +151,14 @@ S32 cache_seek(void *datasource, ogg_int64_t offset, S32 whence) S32 cache_close (void *datasource) { - LLDiskCache *file = (LLDiskCache *)datasource; + LLFileSystem *file = (LLFileSystem *)datasource; delete file; return 0; } long cache_tell (void *datasource) { - LLDiskCache *file = (LLDiskCache *)datasource; + LLFileSystem *file = (LLFileSystem *)datasource; return file->tell(); } @@ -198,7 +198,7 @@ BOOL LLVorbisDecodeState::initDecode() LL_DEBUGS("AudioEngine") << "Initing decode from vfile: " << mUUID << LL_ENDL; - mInFilep = new LLDiskCache(mUUID, LLAssetType::AT_SOUND); + mInFilep = new LLFileSystem(mUUID, LLAssetType::AT_SOUND); if (!mInFilep || !mInFilep->getSize()) { LL_WARNS("AudioEngine") << "unable to open vorbis source vfile for reading" << LL_ENDL; diff --git a/indra/llaudio/llaudioengine.cpp b/indra/llaudio/llaudioengine.cpp index 9dd752f492..d35f249973 100644 --- a/indra/llaudio/llaudioengine.cpp +++ b/indra/llaudio/llaudioengine.cpp @@ -35,7 +35,7 @@ #include "sound_ids.h" // temporary hack for min/max distances -#include "lldiskcache.h" +#include "llfilesystem.h" #include "lldir.h" #include "llaudiodecodemgr.h" #include "llassetstorage.h" @@ -1015,7 +1015,7 @@ bool LLAudioEngine::hasDecodedFile(const LLUUID &uuid) bool LLAudioEngine::hasLocalFile(const LLUUID &uuid) { // See if it's in the cache. - bool have_local = LLDiskCache::getExists(uuid, LLAssetType::AT_SOUND); + bool have_local = LLFileSystem::getExists(uuid, LLAssetType::AT_SOUND); LL_DEBUGS("AudioEngine") << "sound uuid " << uuid << " exists in cache" << LL_ENDL; return have_local; } diff --git a/indra/llcharacter/llkeyframemotion.cpp b/indra/llcharacter/llkeyframemotion.cpp index d1ac336fc1..fe9de30f0a 100644 --- a/indra/llcharacter/llkeyframemotion.cpp +++ b/indra/llcharacter/llkeyframemotion.cpp @@ -39,9 +39,9 @@ #include "llendianswizzle.h" #include "llkeyframemotion.h" #include "llquantize.h" -#include "lldiskcache.h" #include "m3math.h" #include "message.h" +#include "llfilesystem.h" //----------------------------------------------------------------------------- // Static Definitions @@ -559,7 +559,7 @@ LLMotion::LLMotionInitStatus LLKeyframeMotion::onInitialize(LLCharacter *charact S32 anim_file_size; BOOL success = FALSE; - LLDiskCache* anim_file = new LLDiskCache(mID, LLAssetType::AT_ANIMATION); + LLFileSystem* anim_file = new LLFileSystem(mID, LLAssetType::AT_ANIMATION); if (!anim_file || !anim_file->getSize()) { delete anim_file; @@ -2324,7 +2324,7 @@ void LLKeyframeMotion::onLoadComplete(const LLUUID& asset_uuid, // asset already loaded return; } - LLDiskCache file(asset_uuid, type, LLDiskCache::READ); + LLFileSystem file(asset_uuid, type, LLFileSystem::READ); S32 size = file.getSize(); U8* buffer = new U8[size]; diff --git a/indra/llfilesystem/CMakeLists.txt b/indra/llfilesystem/CMakeLists.txt index 306b483097..d1dece5bba 100644 --- a/indra/llfilesystem/CMakeLists.txt +++ b/indra/llfilesystem/CMakeLists.txt @@ -18,16 +18,17 @@ set(llfilesystem_SOURCE_FILES lldiriterator.cpp lllfsthread.cpp lldiskcache.cpp + llfilesystem.cpp ) set(llfilesystem_HEADER_FILES CMakeLists.txt - lldir.h lldirguard.h lldiriterator.h lllfsthread.h lldiskcache.h + llfilesystem.h ) if (DARWIN) diff --git a/indra/llfilesystem/lldiskcache.cpp b/indra/llfilesystem/lldiskcache.cpp new file mode 100644 index 0000000000..72982db069 --- /dev/null +++ b/indra/llfilesystem/lldiskcache.cpp @@ -0,0 +1,773 @@ +/** + * @file lldiskcache.cpp + * @brief The SQLite based disk cache implementation. + * @Note Eventually, this component might split into an interface + * file and multiple implemtations but for now, this is the + * only one so I think it's okay to combine everything. + * + * $LicenseInfo:firstyear=2009&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2020, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#if (defined(LL_WINDOWS) || defined(LL_LINUX) || defined(LL_DARWIN)) +#include "linden_common.h" +#endif + +#include "lldiskcache.h" + +#include +#include +#include +#include +#include + +/////////////////////////////////////////////////////////////////////////////// +// +llDiskCache::llDiskCache() : + mDataStorePath("") +{ +} + +llDiskCache::~llDiskCache() +{ +} + +/////////////////////////////////////////////////////////////////////////////// +// Opens the database - typically done when the application starts +// and is complementary to close() which is called when the application +// is finisahed and exits. +// Pass in the folder and filename of the SQLite database you want to +// use or create (file doesn't have to exist but the folder must) +// Returns true or false and writes a message to console on error +bool llDiskCache::open(const std::string db_folder, const std::string db_filename) +{ + mDataStorePath = db_folder; + std::string db_pathname = makeFullPath(db_filename); + + // simple flags for the moment - these will likely be extended + // later on to support the SQLite mutex model for reading/writing + // simultaneously - perhaps when we look at supporting textures too + int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE; + + if (sqlite3_open_v2( + db_pathname.c_str(), + &mDb, + flags, + nullptr // Name of VFS module to use + ) != SQLITE_OK) + { + printError(__FUNCDNAME__ , "open_v2", true); + close(); + return false; + } + + // I elected to store the code that generates the statement + // in sepsrate functions throughout - this seemed like a cleaner + // approach than having hundreds of stmt << "REPLACE AS" lines + // interspersed in the logic code. They are all prefixed with + // 'sqlCompose' and followed by a short description. + const std::string stmt = sqlComposeCreateTable(); + if (! sqliteExec(stmt, __FUNCDNAME__ )) + { + // Not sure if we need close here - if open fails, then we + // perhaps don't need to close it - TODO: look in SQLite docs + close(); + return false; + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// Determines if an entry exists. +// Pass in the id and a variable that will indicate existance +// Returns true/false if function succeeded and the boolean +// you pass in will be set to true/false if entry exists or not +bool llDiskCache::exists(const std::string id, bool& exists) +{ + if (!mDb) + { + printError(__FUNCDNAME__ , "mDb is invalid", false); + return false; + } + + if (id.empty()) + { + printError(__FUNCDNAME__ , "id is empty", false); + return false; + } + + // As well as the separate function to compose the SQL statement, + // worth pointing out that the code to 'prepare' and 'step' the + // SQLite "prepared statement" has been factored out into its own + // function and used in several other functions. + const std::string stmt = sqlComposeExists(id); + sqlite3_stmt* prepared_statement = sqlitePrepareStep(__FUNCDNAME__ , stmt, SQLITE_ROW); + if (! prepared_statement) + { + return false; + } + + int result_column_index = 0; + int result_count = sqlite3_column_int(prepared_statement, result_column_index); + if (sqlite3_finalize(prepared_statement) != SQLITE_OK) + { + printError(__FUNCDNAME__ , "sqlite3_finalize()", true); + return false; + } + + // given the uniqueness of the ID, this can only ever be + // either 1 or 0 so this might be a bit confusing + exists = result_count > 0 ? true : false; + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// Given an id (likely a UUID + decoration but can be any string), a +// pointer to a blob of binary data along with its length, write the +// entry to the cache +// Returns true/false for success/failure +bool llDiskCache::put(const std::string id, + char* binary_data, + int binary_data_size) +{ + if (!mDb) + { + printError(__FUNCDNAME__ , "mDb is invalid", false); + return false; + } + + if (id.empty()) + { + printError(__FUNCDNAME__ , "id is empty", false); + return false; + } + + // < 0 is obvious but we assert the data must be at least 1 byte long + if (binary_data_size <= 0) + { + printError(__FUNCDNAME__ , "size of binary file to write is invalid", false); + return false; + } + + // we generate a unique filename for the actual data itself + // which is stored on disk directly and not in the database. + // TODO: consider making the filename more like the ID passed in + // although the problem with that is we would have to parse the id + // to remove invalid filename chars, consider length etc. As it + // stands, we can write a simple SQL statement to return the filename + // given the ID. + const std::string filename = makeUniqueFilename(); + const std::string filepath = makeFullPath(filename); + std::ofstream file(filepath, std::ios::out | std::ios::binary); + if (! file.is_open()) + { + std::ostringstream error; + error << "Unable to open " << filepath << " for writing"; + printError(__FUNCDNAME__ , error.str(), false); + return false; + } + + file.write((char*)binary_data, binary_data_size); + file.close(); + + // I think is a catchall "wasn't able to write the file to disk" + // conditional but should revisit when we hook this up to viewer + // code to make sure - we never want to write bad/no data to the + // disk and not indicate something has gone wrong + if (file.bad()) + { + std::ostringstream error; + error << "Unable to write " << binary_data_size; + error << " bytes to " << filepath; + printError(__FUNCDNAME__ , error.str(), false); + + return false; + } + + // this is where the filename/size is written to the database along + // with the current date/time for the created/last access times + const std::string stmt = sqlComposePut(id, filename, binary_data_size); + if (! sqlitePrepareStep(__FUNCDNAME__ , stmt, SQLITE_DONE)) + { + return false; + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// Given an id (likely a UUID + decoration but can be any string), the +// address of a pointer that will be used to allocate memory and a +// varialble that will contain the length of the data, get an item from +// the cache. Note that the memory buffer returned belongs to the calling +// function and it is its responsiblity to clean it up when it's no +// longer needed. +// Returns true/false for success/failure +const bool llDiskCache::get(const std::string id, + char** mem_buffer, + int& mem_buffer_size) +{ + // Check if the entry exists first to avoid dealing with a bunch + // of conditions that look like failure but aren't in the main code. + // Note exists() is a public method and also tests for mDB and id + // being valid so we can safely put this about the same tests + // in this function + bool get_exists = false; + if (! exists(id, get_exists)) + { + return false; + } + if (! get_exists) + { + return false; + } + + if (!mDb) + { + printError(__FUNCDNAME__ , "mDb is invalid", false); + return false; + } + + if (id.empty()) + { + printError(__FUNCDNAME__ , "id is empty", false); + return false; + } + + const std::string stmt_select = sqlComposeGetSelect(id); + sqlite3_stmt* prepared_statement = sqlitePrepareStep(__FUNCDNAME__ , stmt_select, SQLITE_ROW); + if (! prepared_statement) + { + return false; + } + + int result_column_index = 0; + const unsigned char* text = sqlite3_column_text(prepared_statement, result_column_index); + if (text == nullptr) + { + printError(__FUNCDNAME__ , "filename is nullptr", true); + return false; + } + const std::string filename = std::string(reinterpret_cast(text)); + const std::string filepath = makeFullPath(filename); + + result_column_index = 1; + int filesize_db = sqlite3_column_int(prepared_statement, result_column_index); + if (filesize_db <= 0) + { + printError(__FUNCDNAME__ , "filesize is invalid", true); + return false; + } + + if (sqlite3_finalize(prepared_statement) != SQLITE_OK) + { + printError(__FUNCDNAME__ , "sqlite3_finalize()", true); + return false; + } + + // Now we have the fiename, we can read the file from disk + std::ifstream file(filepath, std::ios::in | std::ios::binary | std::ios::ate); + if (! file.is_open()) + { + std::ostringstream error; + error << "Unable to open " << filepath << " for reading"; + printError(__FUNCDNAME__ , error.str(), false); + return false; + } + + // we get the expected filesize from the database but we can also + // get it (easily) when we read the file from the disk. We compare + // the two and return false if they don't match + std::streampos filesize_file = file.tellg(); + if (filesize_db != filesize_file) + { + std::ostringstream error; + error << "File size from DB (" << filesize_db << ")"; + error << " and "; + error << "file size from file (" << filesize_file << ")"; + error << " in file " << filepath << " are different"; + printError(__FUNCDNAME__ , error.str(), false); + + return false; + } + + // doest matter if we choose DB or file version - they must be + // identical if we get this far - just used for clarity + int filesize = filesize_db; + + // allocate a block of memory that we pass back for the calling + // function to use then delete when it's no longer needed + *mem_buffer = new char[filesize]; + mem_buffer_size = filesize; + + file.seekg(0, std::ios::beg); + file.read(*mem_buffer, filesize); + file.close(); + + if (file.bad()) + { + std::ostringstream error; + error << "Unable to read " << filesize; + error << " bytes from " << filepath; + printError(__FUNCDNAME__ , error.str(), false); + + return false; + } + + // here we update the count of times the file is accessed so + // we can keep track of how many times it's been requested. + // This will be useful for metrics and perhaps determining + // if a file should not be purged even though its age + // might suggest that it should. + // In addition, this is where the time of last access is updated + // in the database and that us used to determine what is purged + // in an LRU fashion when the purge function is called. + const std::string stmt_update = sqlComposeGetUpdate(id); + if (! sqliteExec(stmt_update, __FUNCDNAME__ )) + { + return false; + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// Purges the database of older entries using an LRU approach. +// Pass in the number of entries to retain. +// This is called now after open to "clean up" the cache when the +// application starts. +// TODO: IMPORTANT: compose a list of files that will be deleted +// and delete them from disk too - not just from the DB +bool llDiskCache::purge(int num_entries) +{ + if (num_entries < 0) + { + printError(__FUNCDNAME__ , "number of entries to purge is invalid", false); + return false; + } + + // find the rows affected and get the filenames for them +//swww + + // delete oldest entries leaving the correct number in place + const std::string stmt = sqlComposePurge(num_entries); + if (! sqliteExec(stmt, __FUNCDNAME__ )) + { + return false; + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// Call at application shutdown +void llDiskCache::close() +{ + sqlite3_close(mDb); +} + +/////////////////////////////////////////////////////////////////////////////// +// Determine the version of SQLite in use +// TODO: make this a static so we can get to it from the Viewer About +// box without instantiating the whole thing. +const std::string llDiskCache::dbVersion() +{ + std::ostringstream version; + + version << sqlite3_libversion(); + + return version.str(); +} + +/////////////////////////////////////////////////////////////////////////////// +// Given an id, return the matching filename +const std::string llDiskCache::getFilenameById(const std::string id) +{ + // TODO: + return std::string(); +} + +/////////////////////////////////////////////////////////////////////////////// +// Given an id, return the number of times that entry has been +// accessed from the cache +const int llDiskCache::getAccessCountById(const std::string id) +{ + // TODO: + return -1; +} + +/////////////////////////////////////////////////////////////////////////////// +// Return the number of entries currently in the cache as well as +// the maximum possible entries. +void llDiskCache::getNumEntries(int& num_entries, int& max_entries) +{ + num_entries = -1; + max_entries = -1; +} + +/////////////////////////////////////////////////////////////////////////////// +// Wraps the sqlite3_exec(..) used in many places +bool llDiskCache::sqliteExec(const std::string stmt, + const std::string funcname) +{ + if (sqlite3_exec( + mDb, + stmt.c_str(), + nullptr, // Callback function (unused) + nullptr, // 1st argument to callback (unused) + nullptr // Error msg written here (unused) + ) != SQLITE_OK) + { + printError(funcname, "sqlite3_exec", true); + return false; + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// Wraps the sqlite3_prepare_v2 and sqlite3_step calls used in many places +sqlite3_stmt* llDiskCache::sqlitePrepareStep(const std::string funcname, + const std::string stmt, + int sqlite_success_condition) +{ + sqlite3_stmt* prepared_statement; + + if (sqlite3_prepare_v2( + mDb, + stmt.c_str(), + -1, // Maximum length of zSql in bytes. + &prepared_statement, + 0 // OUT: Pointer to unused portion of zSql + ) != SQLITE_OK) + { + printError(funcname, "sqlite3_prepare_v2", true); + return nullptr; + } + + if (sqlite3_step(prepared_statement) != sqlite_success_condition) + { + printError(funcname, "sqlite3_step", true); + sqlite3_finalize(prepared_statement); + return nullptr; + } + return prepared_statement; +} + +/////////////////////////////////////////////////////////////////////////////// +// When an "error" occurss - e.g. database cannot be found, file cannot be +// written, invalid argument passed into a function etc. a message is +// written to stderr that should end up in the viewer log +// TODO: Set the verbosity using the usual Viewer mechanism +void llDiskCache::printError(const std::string funcname, + const std::string desc, + bool is_sqlite_err) +{ + std::ostringstream err_msg; + + err_msg << "llDiskCache error in "; + err_msg << __FUNCDNAME__ << "(...) "; + err_msg << desc; + + if (is_sqlite_err) + { + err_msg << " - "; + err_msg << std::string(sqlite3_errmsg(mDb)); + } + + // TODO: set via viewer verbosity level + const int verbosity = 1; + if (verbosity > 0) + { + std::cerr << err_msg.str() << std::endl; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// wrapper for SQLite code to begin an SQL transaction - not used yet but +// it will be eventually +bool llDiskCache::beginTransaction() +{ + const std::string stmt("BEGIN TRANSACTION"); + if (! sqliteExec(stmt, __FUNCDNAME__ )) + { + return false; + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// wrapper for SQLite code to end an SQL transaction - not used yet but +// it will be eventually +bool llDiskCache::endTransaction() +{ + const std::string stmt("COMMIT"); + if (! sqliteExec(stmt, __FUNCDNAME__ )) + { + return false; + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// Build a unique filename that will be used to store the actual file +// on disk (as opposed to the meta data in the database) +// TODO: I think this needs more work once we move it to the viewer +// and espcially to make it cross platform - see 'std::tmpnam' +// depreciation comments in compiler output for example +std::string llDiskCache::makeUniqueFilename() +{ + // TODO: replace with boost - this is marked as deprecated? + std::string base = std::tmpnam(nullptr); + + // C++11 random number generation!!! + static std::random_device dev; + static std::mt19937 rng(dev()); + std::uniform_int_distribution dist(100000, 999999); + + // currently the tmp filename from std::tmpnam() on macOS + // is of the form `/tmp/foo/bar.12345 and the following code + // strips all the preceding dirs - we likely want a more + // robust (and cross platform solution) when we move to the + // viewer code + std::size_t found = base.rfind(systemSeparator()); + if (found != std::string::npos && found < base.size() - 2) + { + base = base.substr(found + 1); + } + else + { + base = ""; + } + + // we mix in a random number for some more entropy.. + // (i know, i know...) + std::ostringstream unique_filename; + unique_filename << base; + unique_filename << "."; + unique_filename << dist(rng); + + return unique_filename.str(); +} + +/////////////////////////////////////////////////////////////////////////////// +// Return system file/path separator - likely replaced by the version +// in the viewer +const std::string llDiskCache::systemSeparator() +{ +// TODO: replace in viewer with relevant call +#ifdef _WIN32 + return "\\"; +#else + return "/"; +#endif +} + +/////////////////////////////////////////////////////////////////////////////// +// Given a filename, compose a full path based on the path name passed +// in when the database was opened and the separator in play. +const std::string llDiskCache::makeFullPath(const std::string filename) +{ + std::string pathname = mDataStorePath + systemSeparator() + filename; + + return pathname; +} + +/////////////////////////////////////////////////////////////////////////////// +// +const std::string llDiskCache::sqlComposeCreateTable() +{ + std::ostringstream stmt; + stmt << "CREATE TABLE IF NOT EXISTS "; + stmt << mTableName; + stmt << "("; + stmt << mIdFieldName; + stmt << " TEXT PRIMARY KEY, "; + stmt << mFilenameFieldName; + stmt << " TEXT, "; + stmt << mFilesizeFieldName; + stmt << " INTEGER DEFAULT 0, "; + stmt << mInsertionDateTimeFieldName; + stmt << " TEXT, "; + stmt << mLastAccessDateTimeFieldName; + stmt << " TEXT, "; + stmt << mAccessCountFieldName; + stmt << " INTEGER DEFAULT 0"; + stmt << ")"; + +#ifdef SHOW_STATEMENTS + std::cout << stmt.str() << std::endl; +#endif + + return stmt.str(); +} + +/////////////////////////////////////////////////////////////////////////////// +// +const std::string llDiskCache::sqlComposeExists(const std::string id) +{ + std::ostringstream stmt; + stmt << "SELECT COUNT(*) FROM "; + stmt << mTableName; + stmt << " WHERE "; + stmt << mIdFieldName; + stmt << "='"; + stmt << id; + stmt << "'"; + +#ifdef SHOW_STATEMENTS + std::cout << stmt.str() << std::endl; +#endif + + return stmt.str(); +} + +/////////////////////////////////////////////////////////////////////////////// +// SQL statement to write an entry to the DB +// Saves id, filename (generated), file size and create/last access date +const std::string llDiskCache::sqlComposePut(const std::string id, + const std::string filename, + int binary_data_size) +{ + std::ostringstream stmt; + stmt << "REPLACE INTO "; + stmt << mTableName; + stmt << "("; + stmt << mIdFieldName; + stmt << ","; + stmt << mFilenameFieldName; + stmt << ","; + stmt << mFilesizeFieldName; + stmt << ","; + stmt << mInsertionDateTimeFieldName; + stmt << ","; + stmt << mLastAccessDateTimeFieldName; + stmt << ") "; + stmt << "VALUES("; + stmt << "'"; + stmt << id; + stmt << "', "; + stmt << "'"; + stmt << filename; + stmt << "', "; + stmt << binary_data_size; + stmt << ", "; + stmt << "strftime('%Y-%m-%d %H:%M:%f', 'now')"; + stmt << ", "; + stmt << "strftime('%Y-%m-%d %H:%M:%f', 'now')"; + stmt << ")"; + +#ifdef SHOW_STATEMENTS + std::cout << stmt.str() << std::endl; +#endif + + return stmt.str(); +} + +/////////////////////////////////////////////////////////////////////////////// +// SQL statement used in get() to look up the filename and file size +const std::string llDiskCache::sqlComposeGetSelect(const std::string id) +{ + std::ostringstream stmt; + stmt << "SELECT "; + stmt << mFilenameFieldName; + stmt << ", "; + stmt << mFilesizeFieldName; + stmt << " FROM "; + stmt << mTableName; + stmt << " WHERE "; + stmt << mIdFieldName; + stmt << "='"; + stmt << id; + stmt << "'"; + +#ifdef SHOW_STATEMENTS + std::cout << stmt.str() << std::endl; +#endif + + return stmt.str(); +} + +/////////////////////////////////////////////////////////////////////////////// +// SQL statement to update the date/time of last access as well as the +// count of number of times the file has been accessed. +// Note: the more accurate representation of date/time is used to +// ensure ms accuracy vs the standard INTEGER days since epoch approach +const std::string llDiskCache::sqlComposeGetUpdate(const std::string id) +{ + std::ostringstream stmt; + stmt << "UPDATE "; + stmt << mTableName; + stmt << " SET "; + stmt << mAccessCountFieldName; + stmt << "="; + stmt << mAccessCountFieldName; + stmt << "+1"; + stmt << ", "; + stmt << mLastAccessDateTimeFieldName; + stmt << "="; + stmt << "strftime('%Y-%m-%d %H:%M:%f', 'now')"; + stmt << " WHERE "; + stmt << mIdFieldName; + stmt << "='"; + stmt << id; + stmt << "'"; + +#ifdef SHOW_STATEMENTS + std::cout << stmt.str() << std::endl; +#endif + + return stmt.str(); +} + +/////////////////////////////////////////////////////////////////////////////// +// SQL statement to remove items from the database that are older +// than the newest num_elements entries +const std::string llDiskCache::sqlComposePurge(int num_entries) +{ + std::ostringstream stmt; + stmt << "DELETE FROM "; + stmt << mTableName; + stmt << " WHERE "; + stmt << mLastAccessDateTimeFieldName; + stmt << " NOT IN "; + stmt << "("; + stmt << "SELECT "; + stmt << mLastAccessDateTimeFieldName; + stmt << " FROM "; + stmt << mTableName; + stmt << " ORDER BY "; + stmt << mLastAccessDateTimeFieldName; + stmt << " DESC"; + stmt << " LIMIT "; + stmt << num_entries; + stmt << ")"; + +//#ifdef SHOW_STATEMENTS + std::cout << stmt.str() << std::endl; +//#endif + + return stmt.str(); +} diff --git a/indra/llfilesystem/lldiskcache.h b/indra/llfilesystem/lldiskcache.h new file mode 100644 index 0000000000..39b8f7ef48 --- /dev/null +++ b/indra/llfilesystem/lldiskcache.h @@ -0,0 +1,130 @@ +/** + * @file lldiskcache.h + * @brief Declaration SQLite meta data / file storage API + * @brief Declaration of the generic disk cache interface + as well the SQLite header/implementation. + * @Note Eventually, this component might split into an interface + * file and multiple implemtations but for now, this is the + * only one so I think it's okay to combine everything. + * + * $LicenseInfo:firstyear=2009&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2020, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLDISKCACHE +#define _LLDISKCACHE + +#include +#include + +#include "sqlite3.h" + +// Enable this line to display each SQL statement when it is executed +// It can lead to a lot of spam but useful for debugging +//#define SHOW_STATEMENTS + +// I toyed with the idea of a variety of approaches and thought have +// an abstract base class with which to hang different implementations +// off was a good idea but I think we settled on a single approach +// so I will likely remove this level of indirection if not other +// interesting implementation ideas present themselves. +class llDiskCacheBase +{ + public: + llDiskCacheBase() {}; + virtual ~llDiskCacheBase() {}; + virtual bool open(const std::string db_folder, + const std::string db_filename) = 0; + virtual bool exists(const std::string id, bool& exists) = 0; + virtual bool put(const std::string id, + char* binary_data, + int binary_data_size) = 0; + virtual const bool get(const std::string id, + char** mem_buffer, + int& mem_buffer_size) = 0; + virtual bool purge(int num_entries) = 0; + virtual void close() = 0; + virtual const std::string dbVersion() = 0; + virtual const std::string getFilenameById(const std::string id) = 0; + virtual const int getAccessCountById(const std::string id) = 0; + virtual void getNumEntries(int& num_entries, int& max_entries) = 0; +}; + +// implementation for the SQLite/disk blended case +// see source file for detailed comments +class llDiskCache : + public llDiskCacheBase +{ + public: + llDiskCache(); + virtual ~llDiskCache(); + virtual bool open(const std::string db_folder, + const std::string db_filename) override; + virtual bool exists(const std::string id, bool& exists) override; + virtual bool put(const std::string id, + char* binary_data, + int binary_data_size) override; + virtual const bool get(const std::string id, + char** mem_buffer, + int& mem_buffer_size) override; + virtual bool purge(int num_entries) override; + virtual void close() override; + virtual const std::string dbVersion() override; + virtual const std::string getFilenameById(const std::string id) override; + virtual const int getAccessCountById(const std::string id) override; + virtual void getNumEntries(int& num_entries, int& max_entries) override; + + private: + sqlite3* mDb; + std::string mDataStorePath; + const std::string mTableName = "lldiskcache"; + const std::string mIdFieldName = "id"; + const std::string mFilenameFieldName = "filename"; + const std::string mFilesizeFieldName = "filesize"; + const std::string mInsertionDateTimeFieldName = "insert_datetime"; + const std::string mLastAccessDateTimeFieldName = "last_access_datetime"; + const std::string mAccessCountFieldName = "access_count"; + + private: + void printError(const std::string funcname, + const std::string desc, + bool is_sqlite_err); + bool beginTransaction(); + bool endTransaction(); + std::string makeUniqueFilename(); + const std::string systemSeparator(); + const std::string makeFullPath(const std::string filename); + bool sqliteExec(const std::string stmt, + const std::string funcname); + sqlite3_stmt* sqlitePrepareStep(const std::string funcname, + const std::string stmt, + int sqlite_success_condition); + const std::string sqlComposeCreateTable(); + const std::string sqlComposeExists(const std::string id); + const std::string sqlComposePut(const std::string id, + const std::string filename, + int binary_data_size); + const std::string sqlComposeGetSelect(const std::string id); + const std::string sqlComposeGetUpdate(const std::string id); + const std::string sqlComposePurge(int num_entries); +}; + +#endif // _LLDISKCACHE diff --git a/indra/llfilesystem/llfilesystem.cpp b/indra/llfilesystem/llfilesystem.cpp index af93049e07..f0037c9a22 100644 --- a/indra/llfilesystem/llfilesystem.cpp +++ b/indra/llfilesystem/llfilesystem.cpp @@ -1,6 +1,9 @@ /** - * @file lldiskcache.cpp - * @brief Implementation of virtual file + * @file filesystem.h + * @brief Simulate local file system operations. + * @Note The initial implementation does actually use standard C++ + * file operations but eventually, there will be another + * layer that caches and manages file meta data too. * * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * Second Life Viewer Source Code @@ -26,25 +29,21 @@ #include "linden_common.h" +#include "lldir.h" +#include "llfilesystem.h" +#include "llfasttimer.h" #include "lldiskcache.h" -#include "llerror.h" -#include "llthread.h" -#include "lltimer.h" -#include "llfasttimer.h" -#include "llmemory.h" - #include -#include "lldir.h" -const S32 LLDiskCache::READ = 0x00000001; -const S32 LLDiskCache::WRITE = 0x00000002; -const S32 LLDiskCache::READ_WRITE = 0x00000003; // LLDiskCache::READ & LLDiskCache::WRITE -const S32 LLDiskCache::APPEND = 0x00000006; // 0x00000004 & LLDiskCache::WRITE +const S32 LLFileSystem::READ = 0x00000001; +const S32 LLFileSystem::WRITE = 0x00000002; +const S32 LLFileSystem::READ_WRITE = 0x00000003; // LLFileSystem::READ & LLFileSystem::WRITE +const S32 LLFileSystem::APPEND = 0x00000006; // 0x00000004 & LLFileSystem::WRITE static LLTrace::BlockTimerStatHandle FTM_VFILE_WAIT("VFile Wait"); -LLDiskCache::LLDiskCache(const LLUUID &file_id, const LLAssetType::EType file_type, S32 mode) +LLFileSystem::LLFileSystem(const LLUUID &file_id, const LLAssetType::EType file_type, S32 mode) { mFileType = file_type; mFileID = file_id; @@ -54,7 +53,7 @@ LLDiskCache::LLDiskCache(const LLUUID &file_id, const LLAssetType::EType file_ty mMode = mode; } -LLDiskCache::~LLDiskCache() +LLFileSystem::~LLFileSystem() { } @@ -124,7 +123,7 @@ const std::string idToFilepath(const std::string id, LLAssetType::EType at) } // static -bool LLDiskCache::getExists(const LLUUID &file_id, const LLAssetType::EType file_type) +bool LLFileSystem::getExists(const LLUUID &file_id, const LLAssetType::EType file_type) { std::string id_str; file_id.toString(id_str); @@ -140,7 +139,7 @@ bool LLDiskCache::getExists(const LLUUID &file_id, const LLAssetType::EType file } // static -bool LLDiskCache::removeFile(const LLUUID &file_id, const LLAssetType::EType file_type) +bool LLFileSystem::removeFile(const LLUUID &file_id, const LLAssetType::EType file_type) { std::string id_str; file_id.toString(id_str); @@ -152,7 +151,7 @@ bool LLDiskCache::removeFile(const LLUUID &file_id, const LLAssetType::EType fil } // static -bool LLDiskCache::renameFile(const LLUUID &old_file_id, const LLAssetType::EType old_file_type, +bool LLFileSystem::renameFile(const LLUUID &old_file_id, const LLAssetType::EType old_file_type, const LLUUID &new_file_id, const LLAssetType::EType new_file_type) { std::string old_id_str; @@ -175,7 +174,7 @@ bool LLDiskCache::renameFile(const LLUUID &old_file_id, const LLAssetType::EType } // static -S32 LLDiskCache::getFileSize(const LLUUID &file_id, const LLAssetType::EType file_type) +S32 LLFileSystem::getFileSize(const LLUUID &file_id, const LLAssetType::EType file_type) { std::string id_str; file_id.toString(id_str); @@ -192,7 +191,7 @@ S32 LLDiskCache::getFileSize(const LLUUID &file_id, const LLAssetType::EType fil return file_size; } -BOOL LLDiskCache::read(U8 *buffer, S32 bytes, BOOL async, F32 priority) +BOOL LLFileSystem::read(U8 *buffer, S32 bytes, BOOL async, F32 priority) { BOOL success = TRUE; @@ -232,7 +231,7 @@ BOOL LLDiskCache::read(U8 *buffer, S32 bytes, BOOL async, F32 priority) return success; } -BOOL LLDiskCache::isReadComplete() +BOOL LLFileSystem::isReadComplete() { if (mReadComplete) { @@ -242,17 +241,17 @@ BOOL LLDiskCache::isReadComplete() return FALSE; } -S32 LLDiskCache::getLastBytesRead() +S32 LLFileSystem::getLastBytesRead() { return mBytesRead; } -BOOL LLDiskCache::eof() +BOOL LLFileSystem::eof() { return mPosition >= getSize(); } -BOOL LLDiskCache::write(const U8 *buffer, S32 bytes) +BOOL LLFileSystem::write(const U8 *buffer, S32 bytes) { std::string id_str; mFileID.toString(id_str); @@ -287,14 +286,14 @@ BOOL LLDiskCache::write(const U8 *buffer, S32 bytes) } //static -BOOL LLDiskCache::writeFile(const U8 *buffer, S32 bytes, const LLUUID &uuid, LLAssetType::EType type) +BOOL LLFileSystem::writeFile(const U8 *buffer, S32 bytes, const LLUUID &uuid, LLAssetType::EType type) { - LLDiskCache file(uuid, type, LLDiskCache::WRITE); + LLFileSystem file(uuid, type, LLFileSystem::WRITE); file.setMaxSize(bytes); return file.write(buffer, bytes); } -BOOL LLDiskCache::seek(S32 offset, S32 origin) +BOOL LLFileSystem::seek(S32 offset, S32 origin) { if (-1 == origin) { @@ -324,32 +323,32 @@ BOOL LLDiskCache::seek(S32 offset, S32 origin) return TRUE; } -S32 LLDiskCache::tell() const +S32 LLFileSystem::tell() const { return mPosition; } -S32 LLDiskCache::getSize() +S32 LLFileSystem::getSize() { - return LLDiskCache::getFileSize(mFileID, mFileType); + return LLFileSystem::getFileSize(mFileID, mFileType); } -S32 LLDiskCache::getMaxSize() +S32 LLFileSystem::getMaxSize() { // offer up a huge size since we don't care what the max is return INT_MAX; } -BOOL LLDiskCache::setMaxSize(S32 size) +BOOL LLFileSystem::setMaxSize(S32 size) { // we don't care what the max size is so we do nothing // and return true to indicate all was okay return TRUE; } -BOOL LLDiskCache::rename(const LLUUID &new_id, const LLAssetType::EType new_type) +BOOL LLFileSystem::rename(const LLUUID &new_id, const LLAssetType::EType new_type) { - LLDiskCache::renameFile(mFileID, mFileType, new_id, new_type); + LLFileSystem::renameFile(mFileID, mFileType, new_id, new_type); mFileID = new_id; mFileType = new_type; @@ -357,31 +356,31 @@ BOOL LLDiskCache::rename(const LLUUID &new_id, const LLAssetType::EType new_type return TRUE; } -BOOL LLDiskCache::remove() +BOOL LLFileSystem::remove() { - LLDiskCache::removeFile(mFileID, mFileType); + LLFileSystem::removeFile(mFileID, mFileType); return TRUE; } // static -void LLDiskCache::initClass() +void LLFileSystem::initClass() { } // static -void LLDiskCache::cleanupClass() +void LLFileSystem::cleanupClass() { } -bool LLDiskCache::isLocked() +bool LLFileSystem::isLocked() { // I don't think we care about this test since there is no locking // TODO: remove this function and calling sites? return FALSE; } -void LLDiskCache::waitForLock() +void LLFileSystem::waitForLock() { // TODO: remove this function and calling sites? } diff --git a/indra/llfilesystem/llfilesystem.h b/indra/llfilesystem/llfilesystem.h index 7ad06a8689..9cddef74a7 100644 --- a/indra/llfilesystem/llfilesystem.h +++ b/indra/llfilesystem/llfilesystem.h @@ -1,6 +1,9 @@ /** - * @file lldiskcacke.h - * @brief Definition of virtual file + * @file filesystem.h + * @brief Simulate local file system operations. + * @Note The initial implementation does actually use standard C++ + * file operations but eventually, there will be another + * layer that caches and manages file meta data too. * * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * Second Life Viewer Source Code @@ -24,17 +27,17 @@ * $/LicenseInfo$ */ -#ifndef LL_LLDISKCACHE_H -#define LL_LLDISKCACHE_H +#ifndef LL_FILESYSTEM_H +#define LL_FILESYSTEM_H #include "lluuid.h" #include "llassettype.h" -class LLDiskCache +class LLFileSystem { public: - LLDiskCache(const LLUUID &file_id, const LLAssetType::EType file_type, S32 mode = LLDiskCache::READ); - ~LLDiskCache(); + LLFileSystem(const LLUUID &file_id, const LLAssetType::EType file_type, S32 mode = LLFileSystem::READ); + ~LLFileSystem(); BOOL read(U8 *buffer, S32 bytes, BOOL async = FALSE, F32 priority = 128.f); /* Flawfinder: ignore */ BOOL isReadComplete(); @@ -79,4 +82,4 @@ protected: S32 mBytesRead; }; -#endif // LL_LLDISKCACHE_H +#endif // LL_FILESYSTEM_H diff --git a/indra/llmessage/llassetstorage.cpp b/indra/llmessage/llassetstorage.cpp index 31c1edd75e..f38a5e663e 100644 --- a/indra/llmessage/llassetstorage.cpp +++ b/indra/llmessage/llassetstorage.cpp @@ -42,7 +42,7 @@ // this library includes #include "message.h" #include "llxfermanager.h" -#include "lldiskcache.h" +#include "llfilesystem.h" #include "lldbstrings.h" #include "lltransfersourceasset.h" @@ -438,7 +438,7 @@ void LLAssetStorage::_cleanupRequests(BOOL all, S32 error) BOOL LLAssetStorage::hasLocalAsset(const LLUUID &uuid, const LLAssetType::EType type) { - return LLDiskCache::getExists(uuid, type); + return LLFileSystem::getExists(uuid, type); } bool LLAssetStorage::findInCacheAndInvokeCallback(const LLUUID& uuid, LLAssetType::EType type, @@ -450,10 +450,10 @@ bool LLAssetStorage::findInCacheAndInvokeCallback(const LLUUID& uuid, LLAssetTyp llassert(callback != NULL); } - BOOL exists = LLDiskCache::getExists(uuid, type); + BOOL exists = LLFileSystem::getExists(uuid, type); if (exists) { - LLDiskCache file(uuid, type); + LLFileSystem file(uuid, type); U32 size = file.getSize(); if (size > 0) { @@ -523,8 +523,8 @@ void LLAssetStorage::getAssetData(const LLUUID uuid, return; } - BOOL exists = LLDiskCache::getExists(uuid, type); - LLDiskCache file(uuid, type); + BOOL exists = LLFileSystem::getExists(uuid, type); + LLFileSystem file(uuid, type); U32 size = exists ? file.getSize() : 0; if (size > 0) @@ -664,7 +664,7 @@ void LLAssetStorage::downloadCompleteCallback( if (LL_ERR_NOERR == result) { // we might have gotten a zero-size file - LLDiskCache vfile(callback_id, callback_type); + LLFileSystem vfile(callback_id, callback_type); if (vfile.getSize() <= 0) { LL_WARNS("AssetStorage") << "downloadCompleteCallback has non-existent or zero-size asset " << callback_id << LL_ENDL; @@ -724,8 +724,8 @@ void LLAssetStorage::getEstateAsset( return; } - BOOL exists = LLDiskCache::getExists(asset_id, atype); - LLDiskCache file(asset_id, atype); + BOOL exists = LLFileSystem::getExists(asset_id, atype); + LLFileSystem file(asset_id, atype); U32 size = exists ? file.getSize() : 0; if (size > 0) @@ -818,7 +818,7 @@ void LLAssetStorage::downloadEstateAssetCompleteCallback( if (LL_ERR_NOERR == result) { // we might have gotten a zero-size file - LLDiskCache vfile(req->getUUID(), req->getAType()); + LLFileSystem vfile(req->getUUID(), req->getAType()); if (vfile.getSize() <= 0) { LL_WARNS("AssetStorage") << "downloadCompleteCallback has non-existent or zero-size asset!" << LL_ENDL; @@ -860,8 +860,8 @@ void LLAssetStorage::getInvItemAsset( return; } - exists = LLDiskCache::getExists(asset_id, atype); - LLDiskCache file(asset_id, atype); + exists = LLFileSystem::getExists(asset_id, atype); + LLFileSystem file(asset_id, atype); size = exists ? file.getSize() : 0; if(exists && size < 1) { @@ -961,7 +961,7 @@ void LLAssetStorage::downloadInvItemCompleteCallback( if (LL_ERR_NOERR == result) { // we might have gotten a zero-size file - LLDiskCache vfile(req->getUUID(), req->getType()); + LLFileSystem vfile(req->getUUID(), req->getType()); if (vfile.getSize() <= 0) { LL_WARNS("AssetStorage") << "downloadCompleteCallback has non-existent or zero-size asset!" << LL_ENDL; @@ -1396,7 +1396,7 @@ void LLAssetStorage::legacyGetDataCallback(const LLUUID &uuid, if ( !status && !toxic ) { - LLDiskCache file(uuid, type); + LLFileSystem file(uuid, type); std::string uuid_str; diff --git a/indra/llmessage/llcorehttputil.cpp b/indra/llmessage/llcorehttputil.cpp index 376558400c..7031f1aa8c 100644 --- a/indra/llmessage/llcorehttputil.cpp +++ b/indra/llmessage/llcorehttputil.cpp @@ -37,7 +37,7 @@ #include "llsdserialize.h" #include "reader.h" // JSON #include "writer.h" // JSON -#include "lldiskcache.h" +#include "llfilesystem.h" #include "message.h" // for getting the port @@ -784,7 +784,7 @@ LLSD HttpCoroutineAdapter::postFileAndSuspend(LLCore::HttpRequest::ptr_t request // scoping for our streams so that they go away when we no longer need them. { LLCore::BufferArrayStream outs(fileData.get()); - LLDiskCache vfile(assetId, assetType, LLDiskCache::READ); + LLFileSystem vfile(assetId, assetType, LLFileSystem::READ); S32 fileSize = vfile.getSize(); U8* fileBuffer; diff --git a/indra/llmessage/lltransfersourceasset.cpp b/indra/llmessage/lltransfersourceasset.cpp index 7b00a95b00..027283232d 100644 --- a/indra/llmessage/lltransfersourceasset.cpp +++ b/indra/llmessage/lltransfersourceasset.cpp @@ -32,7 +32,7 @@ #include "message.h" #include "lldatapacker.h" #include "lldir.h" -#include "lldiskcache.h" +#include "llfilesystem.h" LLTransferSourceAsset::LLTransferSourceAsset(const LLUUID &request_id, const F32 priority) : LLTransferSource(LLTST_ASSET, request_id, priority), @@ -99,7 +99,7 @@ LLTSCode LLTransferSourceAsset::dataCallback(const S32 packet_id, return LLTS_SKIP; } - LLDiskCache vf(mParams.getAssetID(), mParams.getAssetType(), LLDiskCache::READ); + LLFileSystem vf(mParams.getAssetID(), mParams.getAssetType(), LLFileSystem::READ); if (!vf.getSize()) { @@ -198,7 +198,7 @@ void LLTransferSourceAsset::responderCallback(const LLUUID& uuid, LLAssetType::E if (LL_ERR_NOERR == result) { // Everything's OK. - LLDiskCache vf(uuid, type, LLDiskCache::READ); + LLFileSystem vf(uuid, type, LLFileSystem::READ); tsap->mSize = vf.getSize(); status = LLTS_OK; } diff --git a/indra/llmessage/lltransfersourceasset.h b/indra/llmessage/lltransfersourceasset.h index d9055202ec..585e683cb3 100644 --- a/indra/llmessage/lltransfersourceasset.h +++ b/indra/llmessage/lltransfersourceasset.h @@ -30,7 +30,7 @@ #include "lltransfermanager.h" #include "llassetstorage.h" -class LLDiskCache; +class LLFileSystem; class LLTransferSourceParamsAsset : public LLTransferSourceParams { diff --git a/indra/llmessage/lltransfertargetvfile.cpp b/indra/llmessage/lltransfertargetvfile.cpp index f4a5e71d08..471d687d67 100644 --- a/indra/llmessage/lltransfertargetvfile.cpp +++ b/indra/llmessage/lltransfertargetvfile.cpp @@ -30,7 +30,7 @@ #include "lldatapacker.h" #include "llerror.h" -#include "lldiskcache.h" +#include "llfilesystem.h" //static void LLTransferTargetVFile::updateQueue(bool shutdown) @@ -138,7 +138,7 @@ LLTSCode LLTransferTargetVFile::dataCallback(const S32 packet_id, U8 *in_datap, //LL_INFOS() << "LLTransferTargetFile::dataCallback" << LL_ENDL; //LL_INFOS() << "Packet: " << packet_id << LL_ENDL; - LLDiskCache vf(mTempID, mParams.getAssetType(), LLDiskCache::APPEND); + LLFileSystem vf(mTempID, mParams.getAssetType(), LLFileSystem::APPEND); if (mNeedsCreate) { vf.setMaxSize(mSize); @@ -176,7 +176,7 @@ void LLTransferTargetVFile::completionCallback(const LLTSCode status) case LLTS_DONE: if (!mNeedsCreate) { - LLDiskCache file(mTempID, mParams.getAssetType(), LLDiskCache::WRITE); + LLFileSystem file(mTempID, mParams.getAssetType(), LLFileSystem::WRITE); if (!file.rename(mParams.getAssetID(), mParams.getAssetType())) { LL_ERRS() << "LLTransferTargetVFile: rename failed" << LL_ENDL; @@ -195,7 +195,7 @@ void LLTransferTargetVFile::completionCallback(const LLTSCode status) { // We're aborting this transfer, we don't want to keep this file. LL_WARNS() << "Aborting vfile transfer for " << mParams.getAssetID() << LL_ENDL; - LLDiskCache vf(mTempID, mParams.getAssetType(), LLDiskCache::APPEND); + LLFileSystem vf(mTempID, mParams.getAssetType(), LLFileSystem::APPEND); vf.remove(); } break; diff --git a/indra/llmessage/lltransfertargetvfile.h b/indra/llmessage/lltransfertargetvfile.h index 4c1bfe22c5..39a9125f1b 100644 --- a/indra/llmessage/lltransfertargetvfile.h +++ b/indra/llmessage/lltransfertargetvfile.h @@ -29,9 +29,9 @@ #include "lltransfermanager.h" #include "llassetstorage.h" -#include "lldiskcache.h" +#include "llfilesystem.h" -class LLDiskCache; +class LLFileSystem; // Lame, an S32 for now until I figure out the deal with how we want to do // error codes. diff --git a/indra/llmessage/llxfer_vfile.cpp b/indra/llmessage/llxfer_vfile.cpp index 95629d5fea..9de9ed379b 100644 --- a/indra/llmessage/llxfer_vfile.cpp +++ b/indra/llmessage/llxfer_vfile.cpp @@ -30,7 +30,7 @@ #include "lluuid.h" #include "llerror.h" #include "llmath.h" -#include "lldiskcache.h" +#include "llfilesystem.h" #include "lldir.h" // size of chunks read from/written to disk @@ -79,9 +79,9 @@ void LLXfer_VFile::cleanup () if (mTempID.notNull() && mDeleteTempFile) { - if (LLDiskCache::getExists(mTempID, mType)) + if (LLFileSystem::getExists(mTempID, mType)) { - LLDiskCache file(mTempID, mType, LLDiskCache::WRITE); + LLFileSystem file(mTempID, mType, LLFileSystem::WRITE); file.remove(); } else @@ -187,9 +187,9 @@ S32 LLXfer_VFile::startSend (U64 xfer_id, const LLHost &remote_host) delete mVFile; mVFile = NULL; - if(LLDiskCache::getExists(mLocalID, mType)) + if(LLFileSystem::getExists(mLocalID, mType)) { - mVFile = new LLDiskCache(mLocalID, mType, LLDiskCache::READ); + mVFile = new LLFileSystem(mLocalID, mType, LLFileSystem::READ); if (mVFile->getSize() <= 0) { @@ -235,9 +235,9 @@ S32 LLXfer_VFile::reopenFileHandle() if (mVFile == NULL) { - if (LLDiskCache::getExists(mLocalID, mType)) + if (LLFileSystem::getExists(mLocalID, mType)) { - mVFile = new LLDiskCache(mLocalID, mType, LLDiskCache::READ); + mVFile = new LLFileSystem(mLocalID, mType, LLFileSystem::READ); } else { @@ -260,7 +260,7 @@ void LLXfer_VFile::setXferSize (S32 xfer_size) // It would be nice if LLXFers could tell which end of the pipe they were if (! mVFile) { - LLDiskCache file(mTempID, mType, LLDiskCache::APPEND); + LLFileSystem file(mTempID, mType, LLFileSystem::APPEND); file.setMaxSize(xfer_size); } } @@ -315,7 +315,7 @@ S32 LLXfer_VFile::flush() S32 retval = 0; if (mBufferLength) { - LLDiskCache file(mTempID, mType, LLDiskCache::APPEND); + LLFileSystem file(mTempID, mType, LLFileSystem::APPEND); file.write((U8*)mBuffer, mBufferLength); @@ -335,9 +335,9 @@ S32 LLXfer_VFile::processEOF() if (!mCallbackResult) { - if (LLDiskCache::getExists(mTempID, mType)) + if (LLFileSystem::getExists(mTempID, mType)) { - LLDiskCache file(mTempID, mType, LLDiskCache::WRITE); + LLFileSystem file(mTempID, mType, LLFileSystem::WRITE); if (!file.rename(mLocalID, mType)) { LL_WARNS("Xfer") << "Cache rename of temp file failed: unable to rename " << mTempID << " to " << mLocalID << LL_ENDL; diff --git a/indra/llmessage/llxfer_vfile.h b/indra/llmessage/llxfer_vfile.h index d6ac6ff818..d82bab5f6c 100644 --- a/indra/llmessage/llxfer_vfile.h +++ b/indra/llmessage/llxfer_vfile.h @@ -30,7 +30,7 @@ #include "llxfer.h" #include "llassetstorage.h" -class LLDiskCache; +class LLFileSystem; class LLXfer_VFile : public LLXfer { @@ -40,7 +40,7 @@ class LLXfer_VFile : public LLXfer LLUUID mTempID; LLAssetType::EType mType; - LLDiskCache *mVFile; + LLFileSystem *mVFile; std::string mName; diff --git a/indra/llui/llviewereventrecorder.h b/indra/llui/llviewereventrecorder.h index fec0f9784f..94e66f5dc4 100644 --- a/indra/llui/llviewereventrecorder.h +++ b/indra/llui/llviewereventrecorder.h @@ -32,7 +32,6 @@ #include "lldir.h" #include "llsd.h" #include "llfile.h" -#include "lldiskcache.h" #include "lldate.h" #include "llsdserialize.h" #include "llkeyboard.h" diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 6181683b9e..12598d028c 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -114,7 +114,7 @@ #include "llprimitive.h" #include "llurlaction.h" #include "llurlentry.h" -#include "lldiskcache.h" +#include "llfilesystem.h" #include "llvolumemgr.h" #include "llxfermanager.h" #include "llphysicsextensions.h" @@ -1876,7 +1876,7 @@ bool LLAppViewer::cleanup() SUBSYSTEM_CLEANUP(LLFolderViewItem); LL_INFOS() << "Cleaning up disk cache" << LL_ENDL; - SUBSYSTEM_CLEANUP(LLDiskCache); + SUBSYSTEM_CLEANUP(LLFileSystem); LL_INFOS() << "Saving Data" << LL_ENDL; @@ -4160,7 +4160,7 @@ bool LLAppViewer::initCache() LLVOCache::getInstance()->initCache(LL_PATH_CACHE, gSavedSettings.getU32("CacheNumberOfRegionsForObjects"), getObjectCacheVersion()); - LLDiskCache::initClass(); + LLFileSystem::initClass(); return true; } diff --git a/indra/newview/llcompilequeue.cpp b/indra/newview/llcompilequeue.cpp index ad2f3b9f9a..5d010a6f1e 100644 --- a/indra/newview/llcompilequeue.cpp +++ b/indra/newview/llcompilequeue.cpp @@ -52,7 +52,7 @@ #include "lldir.h" #include "llnotificationsutil.h" #include "llviewerstats.h" -#include "lldiskcache.h" +#include "llfilesystem.h" #include "lluictrlfactory.h" #include "lltrans.h" diff --git a/indra/newview/llfloaterauction.cpp b/indra/newview/llfloaterauction.cpp index 42bcb86454..1b9814883e 100644 --- a/indra/newview/llfloaterauction.cpp +++ b/indra/newview/llfloaterauction.cpp @@ -32,7 +32,7 @@ #include "llimagej2c.h" #include "llimagetga.h" #include "llparcel.h" -#include "lldiskcache.h" +#include "llfilesystem.h" #include "llwindow.h" #include "message.h" @@ -201,7 +201,7 @@ void LLFloaterAuction::onClickSnapshot(void* data) LLPointer tga = new LLImageTGA; tga->encode(raw); - LLDiskCache::writeFile(tga->getData(), tga->getDataSize(), self->mImageID, LLAssetType::AT_IMAGE_TGA); + LLFileSystem::writeFile(tga->getData(), tga->getDataSize(), self->mImageID, LLAssetType::AT_IMAGE_TGA); raw->biasedScaleToPowerOfTwo(LLViewerTexture::MAX_IMAGE_SIZE_DEFAULT); @@ -209,7 +209,7 @@ void LLFloaterAuction::onClickSnapshot(void* data) LLPointer j2c = new LLImageJ2C; j2c->encode(raw, 0.0f); - LLDiskCache::writeFile(j2c->getData(), j2c->getDataSize(), self->mImageID, LLAssetType::AT_TEXTURE); + LLFileSystem::writeFile(j2c->getData(), j2c->getDataSize(), self->mImageID, LLAssetType::AT_TEXTURE); self->mImage = LLViewerTextureManager::getLocalTexture((LLImageRaw*)raw, FALSE); gGL.getTexUnit(0)->bind(self->mImage); diff --git a/indra/newview/llfloaterbvhpreview.cpp b/indra/newview/llfloaterbvhpreview.cpp index 303b4836e4..08f3b577b4 100644 --- a/indra/newview/llfloaterbvhpreview.cpp +++ b/indra/newview/llfloaterbvhpreview.cpp @@ -32,7 +32,7 @@ #include "lldatapacker.h" #include "lldir.h" #include "llnotificationsutil.h" -#include "lldiskcache.h" +#include "llfilesystem.h" #include "llapr.h" #include "llstring.h" @@ -997,7 +997,7 @@ void LLFloaterBvhPreview::onBtnOK(void* userdata) LLDataPackerBinaryBuffer dp(buffer, file_size); if (motionp->serialize(dp)) { - LLDiskCache file(motionp->getID(), LLAssetType::AT_ANIMATION, LLDiskCache::APPEND); + LLFileSystem file(motionp->getID(), LLAssetType::AT_ANIMATION, LLFileSystem::APPEND); S32 size = dp.getCurrentSize(); file.setMaxSize(size); diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp index 057c4d0d5c..0fa5f8e4df 100644 --- a/indra/newview/llfloatermodelpreview.cpp +++ b/indra/newview/llfloatermodelpreview.cpp @@ -77,7 +77,7 @@ #include "llspinctrl.h" #include "lltoggleablemenu.h" #include "lltrans.h" -#include "lldiskcache.h" +#include "llfilesystem.h" #include "llcallbacklist.h" #include "llviewerobjectlist.h" #include "llanimationstates.h" diff --git a/indra/newview/llfloaterregioninfo.cpp b/indra/newview/llfloaterregioninfo.cpp index d2ab15a9b4..0375c15467 100644 --- a/indra/newview/llfloaterregioninfo.cpp +++ b/indra/newview/llfloaterregioninfo.cpp @@ -36,7 +36,7 @@ #include "llglheaders.h" #include "llregionflags.h" #include "llstl.h" -#include "lldiskcache.h" +#include "llfilesystem.h" #include "llxfermanager.h" #include "indra_constants.h" #include "message.h" @@ -2239,7 +2239,7 @@ void LLPanelEstateCovenant::onLoadComplete(const LLUUID& asset_uuid, { if(0 == status) { - LLDiskCache file(asset_uuid, type, LLDiskCache::READ); + LLFileSystem file(asset_uuid, type, LLFileSystem::READ); S32 file_length = file.getSize(); diff --git a/indra/newview/llfloaterreporter.cpp b/indra/newview/llfloaterreporter.cpp index 97db905467..3ef80300ef 100644 --- a/indra/newview/llfloaterreporter.cpp +++ b/indra/newview/llfloaterreporter.cpp @@ -44,7 +44,7 @@ #include "llnotificationsutil.h" #include "llstring.h" #include "llsys.h" -#include "lldiskcache.h" +#include "llfilesystem.h" #include "mean_collision_data.h" #include "message.h" #include "v3math.h" @@ -899,7 +899,7 @@ void LLFloaterReporter::takeScreenshot(bool use_prev_screenshot) mResourceDatap->mAssetInfo.setDescription("screenshot_descr"); // store in cache - LLDiskCache::writeFile(upload_data->getData(), + LLFileSystem::writeFile(upload_data->getData(), upload_data->getDataSize(), mResourceDatap->mAssetInfo.mUuid, mResourceDatap->mAssetInfo.mType); diff --git a/indra/newview/llfloatertos.cpp b/indra/newview/llfloatertos.cpp index 96da13915c..1aeb727172 100644 --- a/indra/newview/llfloatertos.cpp +++ b/indra/newview/llfloatertos.cpp @@ -40,7 +40,7 @@ #include "lltextbox.h" #include "llui.h" #include "lluictrlfactory.h" -#include "lldiskcache.h" +#include "llfilesystem.h" #include "message.h" #include "llstartup.h" // login_alert_done #include "llcorehttputil.h" diff --git a/indra/newview/llgesturemgr.cpp b/indra/newview/llgesturemgr.cpp index 82feb891bc..9f2119281d 100644 --- a/indra/newview/llgesturemgr.cpp +++ b/indra/newview/llgesturemgr.cpp @@ -42,7 +42,7 @@ #include "llnotificationsutil.h" #include "llstl.h" #include "llstring.h" // todo: remove -#include "lldiskcache.h" +#include "llfilesystem.h" #include "message.h" // newview @@ -1055,7 +1055,7 @@ void LLGestureMgr::onLoadComplete(const LLUUID& asset_uuid, if (0 == status) { - LLDiskCache file(asset_uuid, type, LLDiskCache::READ); + LLFileSystem file(asset_uuid, type, LLFileSystem::READ); S32 size = file.getSize(); std::vector buffer(size+1); diff --git a/indra/newview/lllandmarklist.cpp b/indra/newview/lllandmarklist.cpp index 5661b2525b..747212ba55 100644 --- a/indra/newview/lllandmarklist.cpp +++ b/indra/newview/lllandmarklist.cpp @@ -33,7 +33,7 @@ #include "llappviewer.h" #include "llagent.h" -#include "lldiskcache.h" +#include "llfilesystem.h" #include "llviewerstats.h" // Globals @@ -122,7 +122,7 @@ void LLLandmarkList::processGetAssetReply( { if( status == 0 ) { - LLDiskCache file(uuid, type); + LLFileSystem file(uuid, type); S32 file_length = file.getSize(); std::vector buffer(file_length + 1); diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 011b187b58..a0d591dc47 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -49,7 +49,7 @@ #include "llsdutil_math.h" #include "llsdserialize.h" #include "llthread.h" -#include "lldiskcache.h" +#include "llfilesystem.h" #include "llviewercontrol.h" #include "llviewerinventory.h" #include "llviewermenufile.h" @@ -1335,7 +1335,7 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id) if (version <= MAX_MESH_VERSION && offset >= 0 && size > 0) { //check cache for mesh skin info - LLDiskCache file(mesh_id, LLAssetType::AT_MESH); + LLFileSystem file(mesh_id, LLAssetType::AT_MESH); if (file.getSize() >= offset+size) { U8* buffer = new(std::nothrow) U8[size]; @@ -1431,7 +1431,7 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id) if (version <= MAX_MESH_VERSION && offset >= 0 && size > 0) { //check cache for mesh skin info - LLDiskCache file(mesh_id, LLAssetType::AT_MESH); + LLFileSystem file(mesh_id, LLAssetType::AT_MESH); if (file.getSize() >= offset+size) { U8* buffer = new(std::nothrow) U8[size]; @@ -1528,7 +1528,7 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id) if (version <= MAX_MESH_VERSION && offset >= 0 && size > 0) { //check cache for mesh physics shape info - LLDiskCache file(mesh_id, LLAssetType::AT_MESH); + LLFileSystem file(mesh_id, LLAssetType::AT_MESH); if (file.getSize() >= offset+size) { LLMeshRepository::sCacheBytesRead += size; @@ -1633,7 +1633,7 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params, bool c { //look for mesh in asset in cache - LLDiskCache file(mesh_params.getSculptID(), LLAssetType::AT_MESH); + LLFileSystem file(mesh_params.getSculptID(), LLAssetType::AT_MESH); S32 size = file.getSize(); @@ -1712,7 +1712,7 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod, { //check cache for mesh asset - LLDiskCache file(mesh_id, LLAssetType::AT_MESH); + LLFileSystem file(mesh_id, LLAssetType::AT_MESH); if (file.getSize() >= offset+size) { U8* buffer = new(std::nothrow) U8[size]; @@ -3240,7 +3240,7 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* b // only allocate as much space in the cache as is needed for the local cache data_size = llmin(data_size, bytes); - LLDiskCache file(mesh_id, LLAssetType::AT_MESH, LLDiskCache::WRITE); + LLFileSystem file(mesh_id, LLAssetType::AT_MESH, LLFileSystem::WRITE); if (file.getMaxSize() >= bytes || file.setMaxSize(bytes)) { LLMeshRepository::sCacheBytesWritten += data_size; @@ -3312,7 +3312,7 @@ void LLMeshLODHandler::processData(LLCore::BufferArray * /* body */, S32 /* body if (result == MESH_OK) { // good fetch from sim, write to cache - LLDiskCache file(mMeshParams.getSculptID(), LLAssetType::AT_MESH, LLDiskCache::WRITE); + LLFileSystem file(mMeshParams.getSculptID(), LLAssetType::AT_MESH, LLFileSystem::WRITE); S32 offset = mOffset; S32 size = mRequestedBytes; @@ -3376,7 +3376,7 @@ void LLMeshSkinInfoHandler::processData(LLCore::BufferArray * /* body */, S32 /* && gMeshRepo.mThread->skinInfoReceived(mMeshID, data, data_size)) { // good fetch from sim, write to cache - LLDiskCache file(mMeshID, LLAssetType::AT_MESH, LLDiskCache::WRITE); + LLFileSystem file(mMeshID, LLAssetType::AT_MESH, LLFileSystem::WRITE); S32 offset = mOffset; S32 size = mRequestedBytes; @@ -3424,7 +3424,7 @@ void LLMeshDecompositionHandler::processData(LLCore::BufferArray * /* body */, S && gMeshRepo.mThread->decompositionReceived(mMeshID, data, data_size)) { // good fetch from sim, write to cache - LLDiskCache file(mMeshID, LLAssetType::AT_MESH, LLDiskCache::WRITE); + LLFileSystem file(mMeshID, LLAssetType::AT_MESH, LLFileSystem::WRITE); S32 offset = mOffset; S32 size = mRequestedBytes; @@ -3471,7 +3471,7 @@ void LLMeshPhysicsShapeHandler::processData(LLCore::BufferArray * /* body */, S3 && gMeshRepo.mThread->physicsShapeReceived(mMeshID, data, data_size) == MESH_OK) { // good fetch from sim, write to cache for caching - LLDiskCache file(mMeshID, LLAssetType::AT_MESH, LLDiskCache::WRITE); + LLFileSystem file(mMeshID, LLAssetType::AT_MESH, LLFileSystem::WRITE); S32 offset = mOffset; S32 size = mRequestedBytes; diff --git a/indra/newview/lloutfitgallery.cpp b/indra/newview/lloutfitgallery.cpp index ac2d449621..90f6d23a61 100644 --- a/indra/newview/lloutfitgallery.cpp +++ b/indra/newview/lloutfitgallery.cpp @@ -32,7 +32,7 @@ // llcommon #include "llcommonutils.h" -#include "lldiskcache.h" +#include "llfilesystem.h" #include "llaccordionctrltab.h" #include "llappearancemgr.h" diff --git a/indra/newview/llpostcard.cpp b/indra/newview/llpostcard.cpp index 1fd57ef555..071fc31d27 100644 --- a/indra/newview/llpostcard.cpp +++ b/indra/newview/llpostcard.cpp @@ -28,7 +28,7 @@ #include "llpostcard.h" -#include "lldiskcache.h" +#include "llfilesystem.h" #include "llviewerregion.h" #include "message.h" diff --git a/indra/newview/llpreviewgesture.cpp b/indra/newview/llpreviewgesture.cpp index c7f8f790f4..371153aac3 100644 --- a/indra/newview/llpreviewgesture.cpp +++ b/indra/newview/llpreviewgesture.cpp @@ -47,7 +47,7 @@ #include "llradiogroup.h" #include "llresmgr.h" #include "lltrans.h" -#include "lldiskcache.h" +#include "llfilesystem.h" #include "llviewerobjectlist.h" #include "llviewerregion.h" #include "llviewerstats.h" @@ -852,7 +852,7 @@ void LLPreviewGesture::onLoadComplete(const LLUUID& asset_uuid, { if (0 == status) { - LLDiskCache file(asset_uuid, type, LLDiskCache::READ); + LLFileSystem file(asset_uuid, type, LLFileSystem::READ); S32 size = file.getSize(); std::vector buffer(size+1); @@ -1137,7 +1137,7 @@ void LLPreviewGesture::saveIfNeeded() tid.generate(); assetId = tid.makeAssetID(gAgent.getSecureSessionID()); - LLDiskCache file(assetId, LLAssetType::AT_GESTURE, LLDiskCache::APPEND); + LLFileSystem file(assetId, LLAssetType::AT_GESTURE, LLFileSystem::APPEND); S32 size = dp.getCurrentSize(); file.setMaxSize(size); diff --git a/indra/newview/llpreviewnotecard.cpp b/indra/newview/llpreviewnotecard.cpp index 0b21ff5047..0bccf1d06f 100644 --- a/indra/newview/llpreviewnotecard.cpp +++ b/indra/newview/llpreviewnotecard.cpp @@ -46,7 +46,7 @@ #include "llselectmgr.h" #include "lltrans.h" #include "llviewertexteditor.h" -#include "lldiskcache.h" +#include "llfilesystem.h" #include "llviewerinventory.h" #include "llviewerobject.h" #include "llviewerobjectlist.h" @@ -337,7 +337,7 @@ void LLPreviewNotecard::onLoadComplete(const LLUUID& asset_uuid, { if(0 == status) { - LLDiskCache file(asset_uuid, type, LLDiskCache::READ); + LLFileSystem file(asset_uuid, type, LLFileSystem::READ); S32 file_length = file.getSize(); @@ -452,7 +452,7 @@ void LLPreviewNotecard::finishInventoryUpload(LLUUID itemId, LLUUID newAssetId, // script actually changed the asset. if (nc->hasEmbeddedInventory()) { - LLDiskCache::removeFile(newAssetId, LLAssetType::AT_NOTECARD); + LLFileSystem::removeFile(newAssetId, LLAssetType::AT_NOTECARD); } if (newItemId.isNull()) { @@ -477,7 +477,7 @@ void LLPreviewNotecard::finishTaskUpload(LLUUID itemId, LLUUID newAssetId, LLUUI { if (nc->hasEmbeddedInventory()) { - LLDiskCache::removeFile(newAssetId, LLAssetType::AT_NOTECARD); + LLFileSystem::removeFile(newAssetId, LLAssetType::AT_NOTECARD); } nc->setAssetId(newAssetId); nc->refreshFromInventory(); @@ -556,7 +556,7 @@ bool LLPreviewNotecard::saveIfNeeded(LLInventoryItem* copyitem, bool sync) tid.generate(); asset_id = tid.makeAssetID(gAgent.getSecureSessionID()); - LLDiskCache file(asset_id, LLAssetType::AT_NOTECARD, LLDiskCache::APPEND); + LLFileSystem file(asset_id, LLAssetType::AT_NOTECARD, LLFileSystem::APPEND); LLSaveNotecardInfo* info = new LLSaveNotecardInfo(this, mItemUUID, mObjectUUID, diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp index c2b687cf3b..eae6c28e35 100644 --- a/indra/newview/llpreviewscript.cpp +++ b/indra/newview/llpreviewscript.cpp @@ -51,7 +51,7 @@ #include "llsdserialize.h" #include "llslider.h" #include "lltooldraganddrop.h" -#include "lldiskcache.h" +#include "llfilesystem.h" #include "llagent.h" #include "llmenugl.h" @@ -1715,7 +1715,7 @@ void LLPreviewLSL::onLoadComplete(const LLUUID& asset_uuid, LLAssetType::EType t { if(0 == status) { - LLDiskCache file(asset_uuid, type); + LLFileSystem file(asset_uuid, type); S32 file_length = file.getSize(); std::vector buffer(file_length+1); @@ -2020,7 +2020,7 @@ void LLLiveLSLEditor::onLoadComplete(const LLUUID& asset_id, void LLLiveLSLEditor::loadScriptText(const LLUUID &uuid, LLAssetType::EType type) { - LLDiskCache file(uuid, type); + LLFileSystem file(uuid, type); S32 file_length = file.getSize(); std::vector buffer(file_length + 1); file.read((U8*)&buffer[0], file_length); diff --git a/indra/newview/llsettingsvo.cpp b/indra/newview/llsettingsvo.cpp index 62dc9f24bd..2db0fe6fa4 100644 --- a/indra/newview/llsettingsvo.cpp +++ b/indra/newview/llsettingsvo.cpp @@ -57,7 +57,7 @@ #include "llinventorymodel.h" #include "llassetstorage.h" -#include "lldiskcache.h" +#include "llfilesystem.h" #include "lldrawpoolwater.h" #include @@ -303,7 +303,7 @@ void LLSettingsVOBase::onAssetDownloadComplete(const LLUUID &asset_id, S32 statu LLSettingsBase::ptr_t settings; if (!status) { - LLDiskCache file(asset_id, LLAssetType::AT_SETTINGS, LLDiskCache::READ); + LLFileSystem file(asset_id, LLAssetType::AT_SETTINGS, LLFileSystem::READ); S32 size = file.getSize(); std::string buffer(size + 1, '\0'); diff --git a/indra/newview/llsnapshotlivepreview.cpp b/indra/newview/llsnapshotlivepreview.cpp index 50523e981a..795563e295 100644 --- a/indra/newview/llsnapshotlivepreview.cpp +++ b/indra/newview/llsnapshotlivepreview.cpp @@ -31,6 +31,7 @@ #include "llagentbenefits.h" #include "llagentcamera.h" #include "llagentui.h" +#include "llfilesystem.h" #include "llcombobox.h" #include "llfloaterperms.h" #include "llfloaterreg.h" @@ -50,7 +51,6 @@ #include "llviewercontrol.h" #include "llviewermenufile.h" // upload_new_resource() #include "llviewerstats.h" -#include "lldiskcache.h" #include "llwindow.h" #include "llworld.h" #include @@ -1005,7 +1005,7 @@ void LLSnapshotLivePreview::saveTexture(BOOL outfit_snapshot, std::string name) if (formatted->encode(scaled, 0.0f)) { - LLDiskCache::writeFile(formatted->getData(), formatted->getDataSize(), new_asset_id, LLAssetType::AT_TEXTURE); + LLFileSystem::writeFile(formatted->getData(), formatted->getDataSize(), new_asset_id, LLAssetType::AT_TEXTURE); std::string pos_string; LLAgentUI::buildLocationString(pos_string, LLAgentUI::LOCATION_FORMAT_FULL); std::string who_took_it; diff --git a/indra/newview/llviewerassetstorage.cpp b/indra/newview/llviewerassetstorage.cpp index 3b86f3910d..df3ff1a3c7 100644 --- a/indra/newview/llviewerassetstorage.cpp +++ b/indra/newview/llviewerassetstorage.cpp @@ -28,7 +28,7 @@ #include "llviewerassetstorage.h" -#include "lldiskcache.h" +#include "llfilesystem.h" #include "message.h" #include "llagent.h" @@ -152,13 +152,13 @@ void LLViewerAssetStorage::storeAssetData( if (mUpstreamHost.isOk()) { - if (LLDiskCache::getExists(asset_id, asset_type)) + if (LLFileSystem::getExists(asset_id, asset_type)) { // Pack data into this packet if we can fit it. U8 buffer[MTUBYTES]; buffer[0] = 0; - LLDiskCache vfile(asset_id, asset_type, LLDiskCache::READ); + LLFileSystem vfile(asset_id, asset_type, LLFileSystem::READ); S32 asset_size = vfile.getSize(); LLAssetRequest *req = new LLAssetRequest(asset_id, asset_type); @@ -180,7 +180,7 @@ void LLViewerAssetStorage::storeAssetData( else { // LLAssetStorage metric: Successful Request - S32 size = LLDiskCache::getFileSize(asset_id, asset_type); + S32 size = LLFileSystem::getFileSize(asset_id, asset_type); const char *message = "Added to upload queue"; reportMetric( asset_id, asset_type, LLStringUtil::null, LLUUID::null, size, MR_OKAY, __FILE__, __LINE__, message ); @@ -291,7 +291,7 @@ void LLViewerAssetStorage::storeAssetData( legacy->mUpCallback = callback; legacy->mUserData = user_data; - LLDiskCache file(asset_id, asset_type, LLDiskCache::WRITE); + LLFileSystem file(asset_id, asset_type, LLFileSystem::WRITE); file.setMaxSize(size); @@ -527,7 +527,7 @@ void LLViewerAssetStorage::assetRequestCoro( // case. LLUUID temp_id; temp_id.generate(); - LLDiskCache vf(temp_id, atype, LLDiskCache::WRITE); + LLFileSystem vf(temp_id, atype, LLFileSystem::WRITE); vf.setMaxSize(size); req->mBytesFetched = size; if (!vf.write(raw.data(),size)) diff --git a/indra/newview/llviewerassetstorage.h b/indra/newview/llviewerassetstorage.h index e65bdc1aea..972c89de34 100644 --- a/indra/newview/llviewerassetstorage.h +++ b/indra/newview/llviewerassetstorage.h @@ -30,7 +30,7 @@ #include "llassetstorage.h" #include "llcorehttputil.h" -class LLDiskCache; +class LLFileSystem; class LLViewerAssetRequest; diff --git a/indra/newview/llviewerassetupload.cpp b/indra/newview/llviewerassetupload.cpp index d62962514c..fb3ca69d5d 100644 --- a/indra/newview/llviewerassetupload.cpp +++ b/indra/newview/llviewerassetupload.cpp @@ -45,7 +45,7 @@ #include "llviewerassetupload.h" #include "llappviewer.h" #include "llviewerstats.h" -#include "lldiskcache.h" +#include "llfilesystem.h" #include "llgesturemgr.h" #include "llpreviewnotecard.h" #include "llpreviewgesture.h" @@ -472,7 +472,7 @@ LLSD LLNewFileResourceUploadInfo::exportTempFile() infile.open(filename, LL_APR_RB, NULL, &file_size); if (infile.getFileHandle()) { - LLDiskCache file(getAssetId(), assetType, LLDiskCache::WRITE); + LLFileSystem file(getAssetId(), assetType, LLFileSystem::WRITE); file.setMaxSize(file_size); @@ -565,7 +565,7 @@ LLSD LLBufferedAssetUploadInfo::prepareUpload() if (getAssetId().isNull()) generateNewAssetId(); - LLDiskCache file(getAssetId(), getAssetType(), LLDiskCache::APPEND); + LLFileSystem file(getAssetId(), getAssetType(), LLFileSystem::APPEND); S32 size = mContents.length() + 1; file.setMaxSize(size); @@ -597,7 +597,7 @@ LLUUID LLBufferedAssetUploadInfo::finishUpload(LLSD &result) if (mStoredToCache) { LLAssetType::EType assetType(getAssetType()); - LLDiskCache::renameFile(getAssetId(), assetType, newAssetId, assetType); + LLFileSystem::renameFile(getAssetId(), assetType, newAssetId, assetType); } if (mTaskUpload) diff --git a/indra/newview/llviewermenufile.cpp b/indra/newview/llviewermenufile.cpp index 6d4e12528d..b34f4b5016 100644 --- a/indra/newview/llviewermenufile.cpp +++ b/indra/newview/llviewermenufile.cpp @@ -53,7 +53,6 @@ #include "llviewercontrol.h" // gSavedSettings #include "llviewertexturelist.h" #include "lluictrlfactory.h" -#include "lldiskcache.h" #include "llviewerinventory.h" #include "llviewermenu.h" // gMenuHolder #include "llviewerparcelmgr.h" diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 10ee92c130..4ab6fcc580 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -44,7 +44,7 @@ #include "llteleportflags.h" #include "lltoastnotifypanel.h" #include "lltransactionflags.h" -#include "lldiskcache.h" +#include "llfilesystem.h" #include "llxfermanager.h" #include "mean_collision_data.h" @@ -6816,7 +6816,7 @@ void onCovenantLoadComplete(const LLUUID& asset_uuid, std::string covenant_text; if(0 == status) { - LLDiskCache file(asset_uuid, type, LLDiskCache::READ); + LLFileSystem file(asset_uuid, type, LLFileSystem::READ); S32 file_length = file.getSize(); diff --git a/indra/newview/llviewerstats.cpp b/indra/newview/llviewerstats.cpp index 874982ed60..7ea702b0c9 100644 --- a/indra/newview/llviewerstats.cpp +++ b/indra/newview/llviewerstats.cpp @@ -33,7 +33,6 @@ #include "llfloaterreg.h" #include "llmemory.h" #include "lltimer.h" -#include "lldiskcache.h" #include "llappviewer.h" diff --git a/indra/newview/llviewertexlayer.cpp b/indra/newview/llviewertexlayer.cpp index 199f7f710a..4c2fbcf837 100644 --- a/indra/newview/llviewertexlayer.cpp +++ b/indra/newview/llviewertexlayer.cpp @@ -31,7 +31,6 @@ #include "llagent.h" #include "llimagej2c.h" #include "llnotificationsutil.h" -#include "lldiskcache.h" #include "llviewerregion.h" #include "llglslshader.h" #include "llvoavatarself.h" diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp index f12ab59e2b..d7377a2983 100644 --- a/indra/newview/llviewertexture.cpp +++ b/indra/newview/llviewertexture.cpp @@ -39,7 +39,6 @@ #include "llimagej2c.h" #include "llimagetga.h" #include "llstl.h" -#include "lldiskcache.h" #include "message.h" #include "lltimer.h" diff --git a/indra/newview/llviewertexture.h b/indra/newview/llviewertexture.h index 57a2421065..07997e02a5 100644 --- a/indra/newview/llviewertexture.h +++ b/indra/newview/llviewertexture.h @@ -54,7 +54,7 @@ class LLTexturePipelineTester ; typedef void (*loaded_callback_func)( BOOL success, LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* src_aux, S32 discard_level, BOOL final, void* userdata ); -class LLDiskCache; +class LLFileSystem; class LLMessageSystem; class LLViewerMediaImpl ; class LLVOVolume ; diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp index e0727d51ba..38fccba169 100644 --- a/indra/newview/llviewertexturelist.cpp +++ b/indra/newview/llviewertexturelist.cpp @@ -41,7 +41,7 @@ #include "llsdserialize.h" #include "llsys.h" -#include "lldiskcache.h" +#include "llfilesystem.h" #include "llxmltree.h" #include "message.h" From 3092aa8aae496803707980eb456cddbb9960ef1c Mon Sep 17 00:00:00 2001 From: Callum Prentice Date: Tue, 6 Oct 2020 17:16:53 -0700 Subject: [PATCH 12/85] Add in the C++ filesystem based cache and clean up some indempotent functions in llfilesystem --- indra/llfilesystem/lldiskcache.cpp | 857 ++++------------------ indra/llfilesystem/lldiskcache.h | 148 ++-- indra/llfilesystem/llfilesystem.cpp | 32 +- indra/llfilesystem/llfilesystem.h | 10 +- indra/llmessage/lltransfertargetvfile.cpp | 1 - indra/llmessage/llxfer_vfile.cpp | 1 - indra/newview/app_settings/settings.xml | 11 + indra/newview/llfloaterbvhpreview.cpp | 1 - indra/newview/llmeshrepository.cpp | 2 +- indra/newview/llpreviewgesture.cpp | 1 - indra/newview/llpreviewnotecard.cpp | 1 - indra/newview/llviewerassetstorage.cpp | 3 - indra/newview/llviewerassetupload.cpp | 3 - 13 files changed, 221 insertions(+), 850 deletions(-) diff --git a/indra/llfilesystem/lldiskcache.cpp b/indra/llfilesystem/lldiskcache.cpp index 72982db069..4b2ba0dd79 100644 --- a/indra/llfilesystem/lldiskcache.cpp +++ b/indra/llfilesystem/lldiskcache.cpp @@ -1,9 +1,6 @@ /** * @file lldiskcache.cpp - * @brief The SQLite based disk cache implementation. - * @Note Eventually, this component might split into an interface - * file and multiple implemtations but for now, this is the - * only one so I think it's okay to combine everything. + * @brief The disk cache implementation. * * $LicenseInfo:firstyear=2009&license=viewerlgpl$ * Second Life Viewer Source Code @@ -27,747 +24,167 @@ * $/LicenseInfo$ */ -#if (defined(LL_WINDOWS) || defined(LL_LINUX) || defined(LL_DARWIN)) #include "linden_common.h" -#endif +#include "lluuid.h" +#include "lldir.h" #include "lldiskcache.h" -#include -#include -#include +#include +#include + +#include +#include #include -#include -/////////////////////////////////////////////////////////////////////////////// -// -llDiskCache::llDiskCache() : - mDataStorePath("") +LLDiskCache::LLDiskCache(const std::string cache_dir) : + mCacheDir(cache_dir), + mMaxSizeBytes(mDefaultSizeBytes) +{ + // no access to LLControlGroup / gSavedSettings so use the system environment + // to trigger additional debugging on top of the default "we purged the cache" + mCacheDebugInfo = true; + if (getenv("LL_CACHE_DEBUGGING") != nullptr) + { + mCacheDebugInfo = true; + } + + // create cache dir if it does not exist + boost::filesystem::create_directory(cache_dir); +} + +LLDiskCache::~LLDiskCache() { } -llDiskCache::~llDiskCache() +void LLDiskCache::purge() { -} - -/////////////////////////////////////////////////////////////////////////////// -// Opens the database - typically done when the application starts -// and is complementary to close() which is called when the application -// is finisahed and exits. -// Pass in the folder and filename of the SQLite database you want to -// use or create (file doesn't have to exist but the folder must) -// Returns true or false and writes a message to console on error -bool llDiskCache::open(const std::string db_folder, const std::string db_filename) -{ - mDataStorePath = db_folder; - std::string db_pathname = makeFullPath(db_filename); - - // simple flags for the moment - these will likely be extended - // later on to support the SQLite mutex model for reading/writing - // simultaneously - perhaps when we look at supporting textures too - int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE; - - if (sqlite3_open_v2( - db_pathname.c_str(), - &mDb, - flags, - nullptr // Name of VFS module to use - ) != SQLITE_OK) + if (mCacheDebugInfo) { - printError(__FUNCDNAME__ , "open_v2", true); - close(); - return false; + LL_INFOS() << "Total dir size before purge is " << dirFileSize(mCacheDir) << LL_ENDL; } - // I elected to store the code that generates the statement - // in sepsrate functions throughout - this seemed like a cleaner - // approach than having hundreds of stmt << "REPLACE AS" lines - // interspersed in the logic code. They are all prefixed with - // 'sqlCompose' and followed by a short description. - const std::string stmt = sqlComposeCreateTable(); - if (! sqliteExec(stmt, __FUNCDNAME__ )) + auto start_time = std::chrono::high_resolution_clock::now(); + + typedef std::pair> file_info_t; + std::vector file_info; + + if (boost::filesystem::is_directory(mCacheDir)) { - // Not sure if we need close here - if open fails, then we - // perhaps don't need to close it - TODO: look in SQLite docs - close(); - return false; + for (auto& entry : boost::make_iterator_range(boost::filesystem::directory_iterator(mCacheDir), {})) + { + if (boost::filesystem::is_regular_file(entry)) + { + uintmax_t file_size = boost::filesystem::file_size(entry); + const std::string file_path = entry.path().string(); + const std::time_t file_time = boost::filesystem::last_write_time(entry); + + file_info.push_back(file_info_t(file_time, { file_size, file_path })); + } + } } - return true; -} - -/////////////////////////////////////////////////////////////////////////////// -// Determines if an entry exists. -// Pass in the id and a variable that will indicate existance -// Returns true/false if function succeeded and the boolean -// you pass in will be set to true/false if entry exists or not -bool llDiskCache::exists(const std::string id, bool& exists) -{ - if (!mDb) + std::sort(file_info.begin(), file_info.end(), [](file_info_t& x, file_info_t& y) { - printError(__FUNCDNAME__ , "mDb is invalid", false); - return false; + return x.first > y.first; + }); + + LL_INFOS() << "Purging cache to a maximum of " << mMaxSizeBytes << " bytes" << LL_ENDL; + + uintmax_t file_size_total = 0; + for (file_info_t& entry : file_info) + { + file_size_total += entry.second.first; + + std::string action = ""; + if (file_size_total > mMaxSizeBytes) + { + action = "DELETE:"; + boost::filesystem::remove(entry.second.second); + } + else + { + action = " KEEP:"; + } + + if (mCacheDebugInfo) + { + // have to do this because of LL_INFO/LL_END weirdness + std::ostringstream line; + + line << action << " "; + line << entry.first << " "; + line << entry.second.first << " "; + line << entry.second.second; + line << " (" << file_size_total << "/" << mMaxSizeBytes << ")"; + LL_INFOS() << line.str() << LL_ENDL; + } } - if (id.empty()) + if (mCacheDebugInfo) { - printError(__FUNCDNAME__ , "id is empty", false); - return false; - } - - // As well as the separate function to compose the SQL statement, - // worth pointing out that the code to 'prepare' and 'step' the - // SQLite "prepared statement" has been factored out into its own - // function and used in several other functions. - const std::string stmt = sqlComposeExists(id); - sqlite3_stmt* prepared_statement = sqlitePrepareStep(__FUNCDNAME__ , stmt, SQLITE_ROW); - if (! prepared_statement) - { - return false; - } - - int result_column_index = 0; - int result_count = sqlite3_column_int(prepared_statement, result_column_index); - if (sqlite3_finalize(prepared_statement) != SQLITE_OK) - { - printError(__FUNCDNAME__ , "sqlite3_finalize()", true); - return false; - } - - // given the uniqueness of the ID, this can only ever be - // either 1 or 0 so this might be a bit confusing - exists = result_count > 0 ? true : false; - - return true; -} - -/////////////////////////////////////////////////////////////////////////////// -// Given an id (likely a UUID + decoration but can be any string), a -// pointer to a blob of binary data along with its length, write the -// entry to the cache -// Returns true/false for success/failure -bool llDiskCache::put(const std::string id, - char* binary_data, - int binary_data_size) -{ - if (!mDb) - { - printError(__FUNCDNAME__ , "mDb is invalid", false); - return false; - } - - if (id.empty()) - { - printError(__FUNCDNAME__ , "id is empty", false); - return false; - } - - // < 0 is obvious but we assert the data must be at least 1 byte long - if (binary_data_size <= 0) - { - printError(__FUNCDNAME__ , "size of binary file to write is invalid", false); - return false; - } - - // we generate a unique filename for the actual data itself - // which is stored on disk directly and not in the database. - // TODO: consider making the filename more like the ID passed in - // although the problem with that is we would have to parse the id - // to remove invalid filename chars, consider length etc. As it - // stands, we can write a simple SQL statement to return the filename - // given the ID. - const std::string filename = makeUniqueFilename(); - const std::string filepath = makeFullPath(filename); - std::ofstream file(filepath, std::ios::out | std::ios::binary); - if (! file.is_open()) - { - std::ostringstream error; - error << "Unable to open " << filepath << " for writing"; - printError(__FUNCDNAME__ , error.str(), false); - return false; - } - - file.write((char*)binary_data, binary_data_size); - file.close(); - - // I think is a catchall "wasn't able to write the file to disk" - // conditional but should revisit when we hook this up to viewer - // code to make sure - we never want to write bad/no data to the - // disk and not indicate something has gone wrong - if (file.bad()) - { - std::ostringstream error; - error << "Unable to write " << binary_data_size; - error << " bytes to " << filepath; - printError(__FUNCDNAME__ , error.str(), false); - - return false; - } - - // this is where the filename/size is written to the database along - // with the current date/time for the created/last access times - const std::string stmt = sqlComposePut(id, filename, binary_data_size); - if (! sqlitePrepareStep(__FUNCDNAME__ , stmt, SQLITE_DONE)) - { - return false; - } - - return true; -} - -/////////////////////////////////////////////////////////////////////////////// -// Given an id (likely a UUID + decoration but can be any string), the -// address of a pointer that will be used to allocate memory and a -// varialble that will contain the length of the data, get an item from -// the cache. Note that the memory buffer returned belongs to the calling -// function and it is its responsiblity to clean it up when it's no -// longer needed. -// Returns true/false for success/failure -const bool llDiskCache::get(const std::string id, - char** mem_buffer, - int& mem_buffer_size) -{ - // Check if the entry exists first to avoid dealing with a bunch - // of conditions that look like failure but aren't in the main code. - // Note exists() is a public method and also tests for mDB and id - // being valid so we can safely put this about the same tests - // in this function - bool get_exists = false; - if (! exists(id, get_exists)) - { - return false; - } - if (! get_exists) - { - return false; - } - - if (!mDb) - { - printError(__FUNCDNAME__ , "mDb is invalid", false); - return false; - } - - if (id.empty()) - { - printError(__FUNCDNAME__ , "id is empty", false); - return false; - } - - const std::string stmt_select = sqlComposeGetSelect(id); - sqlite3_stmt* prepared_statement = sqlitePrepareStep(__FUNCDNAME__ , stmt_select, SQLITE_ROW); - if (! prepared_statement) - { - return false; - } - - int result_column_index = 0; - const unsigned char* text = sqlite3_column_text(prepared_statement, result_column_index); - if (text == nullptr) - { - printError(__FUNCDNAME__ , "filename is nullptr", true); - return false; - } - const std::string filename = std::string(reinterpret_cast(text)); - const std::string filepath = makeFullPath(filename); - - result_column_index = 1; - int filesize_db = sqlite3_column_int(prepared_statement, result_column_index); - if (filesize_db <= 0) - { - printError(__FUNCDNAME__ , "filesize is invalid", true); - return false; - } - - if (sqlite3_finalize(prepared_statement) != SQLITE_OK) - { - printError(__FUNCDNAME__ , "sqlite3_finalize()", true); - return false; - } - - // Now we have the fiename, we can read the file from disk - std::ifstream file(filepath, std::ios::in | std::ios::binary | std::ios::ate); - if (! file.is_open()) - { - std::ostringstream error; - error << "Unable to open " << filepath << " for reading"; - printError(__FUNCDNAME__ , error.str(), false); - return false; - } - - // we get the expected filesize from the database but we can also - // get it (easily) when we read the file from the disk. We compare - // the two and return false if they don't match - std::streampos filesize_file = file.tellg(); - if (filesize_db != filesize_file) - { - std::ostringstream error; - error << "File size from DB (" << filesize_db << ")"; - error << " and "; - error << "file size from file (" << filesize_file << ")"; - error << " in file " << filepath << " are different"; - printError(__FUNCDNAME__ , error.str(), false); - - return false; - } - - // doest matter if we choose DB or file version - they must be - // identical if we get this far - just used for clarity - int filesize = filesize_db; - - // allocate a block of memory that we pass back for the calling - // function to use then delete when it's no longer needed - *mem_buffer = new char[filesize]; - mem_buffer_size = filesize; - - file.seekg(0, std::ios::beg); - file.read(*mem_buffer, filesize); - file.close(); - - if (file.bad()) - { - std::ostringstream error; - error << "Unable to read " << filesize; - error << " bytes from " << filepath; - printError(__FUNCDNAME__ , error.str(), false); - - return false; - } - - // here we update the count of times the file is accessed so - // we can keep track of how many times it's been requested. - // This will be useful for metrics and perhaps determining - // if a file should not be purged even though its age - // might suggest that it should. - // In addition, this is where the time of last access is updated - // in the database and that us used to determine what is purged - // in an LRU fashion when the purge function is called. - const std::string stmt_update = sqlComposeGetUpdate(id); - if (! sqliteExec(stmt_update, __FUNCDNAME__ )) - { - return false; - } - - return true; -} - -/////////////////////////////////////////////////////////////////////////////// -// Purges the database of older entries using an LRU approach. -// Pass in the number of entries to retain. -// This is called now after open to "clean up" the cache when the -// application starts. -// TODO: IMPORTANT: compose a list of files that will be deleted -// and delete them from disk too - not just from the DB -bool llDiskCache::purge(int num_entries) -{ - if (num_entries < 0) - { - printError(__FUNCDNAME__ , "number of entries to purge is invalid", false); - return false; - } - - // find the rows affected and get the filenames for them -//swww - - // delete oldest entries leaving the correct number in place - const std::string stmt = sqlComposePurge(num_entries); - if (! sqliteExec(stmt, __FUNCDNAME__ )) - { - return false; - } - - return true; -} - -/////////////////////////////////////////////////////////////////////////////// -// Call at application shutdown -void llDiskCache::close() -{ - sqlite3_close(mDb); -} - -/////////////////////////////////////////////////////////////////////////////// -// Determine the version of SQLite in use -// TODO: make this a static so we can get to it from the Viewer About -// box without instantiating the whole thing. -const std::string llDiskCache::dbVersion() -{ - std::ostringstream version; - - version << sqlite3_libversion(); - - return version.str(); -} - -/////////////////////////////////////////////////////////////////////////////// -// Given an id, return the matching filename -const std::string llDiskCache::getFilenameById(const std::string id) -{ - // TODO: - return std::string(); -} - -/////////////////////////////////////////////////////////////////////////////// -// Given an id, return the number of times that entry has been -// accessed from the cache -const int llDiskCache::getAccessCountById(const std::string id) -{ - // TODO: - return -1; -} - -/////////////////////////////////////////////////////////////////////////////// -// Return the number of entries currently in the cache as well as -// the maximum possible entries. -void llDiskCache::getNumEntries(int& num_entries, int& max_entries) -{ - num_entries = -1; - max_entries = -1; -} - -/////////////////////////////////////////////////////////////////////////////// -// Wraps the sqlite3_exec(..) used in many places -bool llDiskCache::sqliteExec(const std::string stmt, - const std::string funcname) -{ - if (sqlite3_exec( - mDb, - stmt.c_str(), - nullptr, // Callback function (unused) - nullptr, // 1st argument to callback (unused) - nullptr // Error msg written here (unused) - ) != SQLITE_OK) - { - printError(funcname, "sqlite3_exec", true); - return false; - } - - return true; -} - -/////////////////////////////////////////////////////////////////////////////// -// Wraps the sqlite3_prepare_v2 and sqlite3_step calls used in many places -sqlite3_stmt* llDiskCache::sqlitePrepareStep(const std::string funcname, - const std::string stmt, - int sqlite_success_condition) -{ - sqlite3_stmt* prepared_statement; - - if (sqlite3_prepare_v2( - mDb, - stmt.c_str(), - -1, // Maximum length of zSql in bytes. - &prepared_statement, - 0 // OUT: Pointer to unused portion of zSql - ) != SQLITE_OK) - { - printError(funcname, "sqlite3_prepare_v2", true); - return nullptr; - } - - if (sqlite3_step(prepared_statement) != sqlite_success_condition) - { - printError(funcname, "sqlite3_step", true); - sqlite3_finalize(prepared_statement); - return nullptr; - } - return prepared_statement; -} - -/////////////////////////////////////////////////////////////////////////////// -// When an "error" occurss - e.g. database cannot be found, file cannot be -// written, invalid argument passed into a function etc. a message is -// written to stderr that should end up in the viewer log -// TODO: Set the verbosity using the usual Viewer mechanism -void llDiskCache::printError(const std::string funcname, - const std::string desc, - bool is_sqlite_err) -{ - std::ostringstream err_msg; - - err_msg << "llDiskCache error in "; - err_msg << __FUNCDNAME__ << "(...) "; - err_msg << desc; - - if (is_sqlite_err) - { - err_msg << " - "; - err_msg << std::string(sqlite3_errmsg(mDb)); - } - - // TODO: set via viewer verbosity level - const int verbosity = 1; - if (verbosity > 0) - { - std::cerr << err_msg.str() << std::endl; + auto end_time = std::chrono::high_resolution_clock::now(); + auto execute_time = std::chrono::duration_cast(end_time - start_time).count(); + LL_INFOS() << "Total dir size after purge is " << dirFileSize(mCacheDir) << LL_ENDL; + LL_INFOS() << "Cache purge took " << execute_time << " ms to execute for " << file_info.size() << " files" << LL_ENDL; } } -/////////////////////////////////////////////////////////////////////////////// -// wrapper for SQLite code to begin an SQL transaction - not used yet but -// it will be eventually -bool llDiskCache::beginTransaction() +/** + * Update the "last write time" of a file to "now". This must be called whenever a + * file in the cache is read (not written) so that the last time the file was + * accessed which is used in the mechanism for purging the cache, is up to date. + */ +void LLDiskCache::updateFileAccessTime(const std::string file_path) { - const std::string stmt("BEGIN TRANSACTION"); - if (! sqliteExec(stmt, __FUNCDNAME__ )) + const std::time_t file_time = std::time(nullptr); + boost::filesystem::last_write_time(file_path, file_time); +} + +/** + * Clear the cache by removing all the files in the cache directory + * individually. It's important to maintain control of which directory + * if passed in and not let the user inadvertently (or maliciously) set + * it to an random location like your project source or OS system directory + */ +void LLDiskCache::clearCache(const std::string cache_dir) +{ + if (boost::filesystem::is_directory(cache_dir)) { - return false; + for (auto& entry : boost::make_iterator_range(boost::filesystem::directory_iterator(cache_dir), {})) + { + if (boost::filesystem::is_regular_file(entry)) + { + boost::filesystem::remove(entry); + } + } + } +} + +/** + * Utility function to get the total filesize of all files in a directory. It + * used to test file extensions to only check cache files but that was removed. + * There may be a better way that works directly on the folder (similar to + * right clicking on a folder in the OS and asking for size vs right clicking + * on all files and adding up manually) but this is very fast - less than 100ms + * in my testing so, so long as it's not called frequently, it should be okay. + * Note that's it's only currently used for logging/debugging so if performance + * is ever an issue, optimizing this or removing it altogether, is an easy win. + */ +uintmax_t LLDiskCache::dirFileSize(const std::string dir) +{ + uintmax_t total_file_size = 0; + + if (boost::filesystem::is_directory(dir)) + { + for (auto& entry : boost::make_iterator_range(boost::filesystem::directory_iterator(dir), {})) + { + if (boost::filesystem::is_regular_file(entry)) + { + total_file_size += boost::filesystem::file_size(entry); + } + } } - return true; -} - -/////////////////////////////////////////////////////////////////////////////// -// wrapper for SQLite code to end an SQL transaction - not used yet but -// it will be eventually -bool llDiskCache::endTransaction() -{ - const std::string stmt("COMMIT"); - if (! sqliteExec(stmt, __FUNCDNAME__ )) - { - return false; - } - - return true; -} - -/////////////////////////////////////////////////////////////////////////////// -// Build a unique filename that will be used to store the actual file -// on disk (as opposed to the meta data in the database) -// TODO: I think this needs more work once we move it to the viewer -// and espcially to make it cross platform - see 'std::tmpnam' -// depreciation comments in compiler output for example -std::string llDiskCache::makeUniqueFilename() -{ - // TODO: replace with boost - this is marked as deprecated? - std::string base = std::tmpnam(nullptr); - - // C++11 random number generation!!! - static std::random_device dev; - static std::mt19937 rng(dev()); - std::uniform_int_distribution dist(100000, 999999); - - // currently the tmp filename from std::tmpnam() on macOS - // is of the form `/tmp/foo/bar.12345 and the following code - // strips all the preceding dirs - we likely want a more - // robust (and cross platform solution) when we move to the - // viewer code - std::size_t found = base.rfind(systemSeparator()); - if (found != std::string::npos && found < base.size() - 2) - { - base = base.substr(found + 1); - } - else - { - base = ""; - } - - // we mix in a random number for some more entropy.. - // (i know, i know...) - std::ostringstream unique_filename; - unique_filename << base; - unique_filename << "."; - unique_filename << dist(rng); - - return unique_filename.str(); -} - -/////////////////////////////////////////////////////////////////////////////// -// Return system file/path separator - likely replaced by the version -// in the viewer -const std::string llDiskCache::systemSeparator() -{ -// TODO: replace in viewer with relevant call -#ifdef _WIN32 - return "\\"; -#else - return "/"; -#endif -} - -/////////////////////////////////////////////////////////////////////////////// -// Given a filename, compose a full path based on the path name passed -// in when the database was opened and the separator in play. -const std::string llDiskCache::makeFullPath(const std::string filename) -{ - std::string pathname = mDataStorePath + systemSeparator() + filename; - - return pathname; -} - -/////////////////////////////////////////////////////////////////////////////// -// -const std::string llDiskCache::sqlComposeCreateTable() -{ - std::ostringstream stmt; - stmt << "CREATE TABLE IF NOT EXISTS "; - stmt << mTableName; - stmt << "("; - stmt << mIdFieldName; - stmt << " TEXT PRIMARY KEY, "; - stmt << mFilenameFieldName; - stmt << " TEXT, "; - stmt << mFilesizeFieldName; - stmt << " INTEGER DEFAULT 0, "; - stmt << mInsertionDateTimeFieldName; - stmt << " TEXT, "; - stmt << mLastAccessDateTimeFieldName; - stmt << " TEXT, "; - stmt << mAccessCountFieldName; - stmt << " INTEGER DEFAULT 0"; - stmt << ")"; - -#ifdef SHOW_STATEMENTS - std::cout << stmt.str() << std::endl; -#endif - - return stmt.str(); -} - -/////////////////////////////////////////////////////////////////////////////// -// -const std::string llDiskCache::sqlComposeExists(const std::string id) -{ - std::ostringstream stmt; - stmt << "SELECT COUNT(*) FROM "; - stmt << mTableName; - stmt << " WHERE "; - stmt << mIdFieldName; - stmt << "='"; - stmt << id; - stmt << "'"; - -#ifdef SHOW_STATEMENTS - std::cout << stmt.str() << std::endl; -#endif - - return stmt.str(); -} - -/////////////////////////////////////////////////////////////////////////////// -// SQL statement to write an entry to the DB -// Saves id, filename (generated), file size and create/last access date -const std::string llDiskCache::sqlComposePut(const std::string id, - const std::string filename, - int binary_data_size) -{ - std::ostringstream stmt; - stmt << "REPLACE INTO "; - stmt << mTableName; - stmt << "("; - stmt << mIdFieldName; - stmt << ","; - stmt << mFilenameFieldName; - stmt << ","; - stmt << mFilesizeFieldName; - stmt << ","; - stmt << mInsertionDateTimeFieldName; - stmt << ","; - stmt << mLastAccessDateTimeFieldName; - stmt << ") "; - stmt << "VALUES("; - stmt << "'"; - stmt << id; - stmt << "', "; - stmt << "'"; - stmt << filename; - stmt << "', "; - stmt << binary_data_size; - stmt << ", "; - stmt << "strftime('%Y-%m-%d %H:%M:%f', 'now')"; - stmt << ", "; - stmt << "strftime('%Y-%m-%d %H:%M:%f', 'now')"; - stmt << ")"; - -#ifdef SHOW_STATEMENTS - std::cout << stmt.str() << std::endl; -#endif - - return stmt.str(); -} - -/////////////////////////////////////////////////////////////////////////////// -// SQL statement used in get() to look up the filename and file size -const std::string llDiskCache::sqlComposeGetSelect(const std::string id) -{ - std::ostringstream stmt; - stmt << "SELECT "; - stmt << mFilenameFieldName; - stmt << ", "; - stmt << mFilesizeFieldName; - stmt << " FROM "; - stmt << mTableName; - stmt << " WHERE "; - stmt << mIdFieldName; - stmt << "='"; - stmt << id; - stmt << "'"; - -#ifdef SHOW_STATEMENTS - std::cout << stmt.str() << std::endl; -#endif - - return stmt.str(); -} - -/////////////////////////////////////////////////////////////////////////////// -// SQL statement to update the date/time of last access as well as the -// count of number of times the file has been accessed. -// Note: the more accurate representation of date/time is used to -// ensure ms accuracy vs the standard INTEGER days since epoch approach -const std::string llDiskCache::sqlComposeGetUpdate(const std::string id) -{ - std::ostringstream stmt; - stmt << "UPDATE "; - stmt << mTableName; - stmt << " SET "; - stmt << mAccessCountFieldName; - stmt << "="; - stmt << mAccessCountFieldName; - stmt << "+1"; - stmt << ", "; - stmt << mLastAccessDateTimeFieldName; - stmt << "="; - stmt << "strftime('%Y-%m-%d %H:%M:%f', 'now')"; - stmt << " WHERE "; - stmt << mIdFieldName; - stmt << "='"; - stmt << id; - stmt << "'"; - -#ifdef SHOW_STATEMENTS - std::cout << stmt.str() << std::endl; -#endif - - return stmt.str(); -} - -/////////////////////////////////////////////////////////////////////////////// -// SQL statement to remove items from the database that are older -// than the newest num_elements entries -const std::string llDiskCache::sqlComposePurge(int num_entries) -{ - std::ostringstream stmt; - stmt << "DELETE FROM "; - stmt << mTableName; - stmt << " WHERE "; - stmt << mLastAccessDateTimeFieldName; - stmt << " NOT IN "; - stmt << "("; - stmt << "SELECT "; - stmt << mLastAccessDateTimeFieldName; - stmt << " FROM "; - stmt << mTableName; - stmt << " ORDER BY "; - stmt << mLastAccessDateTimeFieldName; - stmt << " DESC"; - stmt << " LIMIT "; - stmt << num_entries; - stmt << ")"; - -//#ifdef SHOW_STATEMENTS - std::cout << stmt.str() << std::endl; -//#endif - - return stmt.str(); + return total_file_size; } diff --git a/indra/llfilesystem/lldiskcache.h b/indra/llfilesystem/lldiskcache.h index 39b8f7ef48..1618cec6a6 100644 --- a/indra/llfilesystem/lldiskcache.h +++ b/indra/llfilesystem/lldiskcache.h @@ -1,11 +1,6 @@ /** * @file lldiskcache.h - * @brief Declaration SQLite meta data / file storage API - * @brief Declaration of the generic disk cache interface - as well the SQLite header/implementation. - * @Note Eventually, this component might split into an interface - * file and multiple implemtations but for now, this is the - * only one so I think it's okay to combine everything. + * @brief The disk cache implementation. * * $LicenseInfo:firstyear=2009&license=viewerlgpl$ * Second Life Viewer Source Code @@ -32,99 +27,68 @@ #ifndef _LLDISKCACHE #define _LLDISKCACHE -#include -#include - -#include "sqlite3.h" - -// Enable this line to display each SQL statement when it is executed -// It can lead to a lot of spam but useful for debugging -//#define SHOW_STATEMENTS - -// I toyed with the idea of a variety of approaches and thought have -// an abstract base class with which to hang different implementations -// off was a good idea but I think we settled on a single approach -// so I will likely remove this level of indirection if not other -// interesting implementation ideas present themselves. -class llDiskCacheBase +class LLDiskCache { public: - llDiskCacheBase() {}; - virtual ~llDiskCacheBase() {}; - virtual bool open(const std::string db_folder, - const std::string db_filename) = 0; - virtual bool exists(const std::string id, bool& exists) = 0; - virtual bool put(const std::string id, - char* binary_data, - int binary_data_size) = 0; - virtual const bool get(const std::string id, - char** mem_buffer, - int& mem_buffer_size) = 0; - virtual bool purge(int num_entries) = 0; - virtual void close() = 0; - virtual const std::string dbVersion() = 0; - virtual const std::string getFilenameById(const std::string id) = 0; - virtual const int getAccessCountById(const std::string id) = 0; - virtual void getNumEntries(int& num_entries, int& max_entries) = 0; -}; + LLDiskCache(const std::string cache_dir); + ~LLDiskCache(); -// implementation for the SQLite/disk blended case -// see source file for detailed comments -class llDiskCache : - public llDiskCacheBase -{ - public: - llDiskCache(); - virtual ~llDiskCache(); - virtual bool open(const std::string db_folder, - const std::string db_filename) override; - virtual bool exists(const std::string id, bool& exists) override; - virtual bool put(const std::string id, - char* binary_data, - int binary_data_size) override; - virtual const bool get(const std::string id, - char** mem_buffer, - int& mem_buffer_size) override; - virtual bool purge(int num_entries) override; - virtual void close() override; - virtual const std::string dbVersion() override; - virtual const std::string getFilenameById(const std::string id) override; - virtual const int getAccessCountById(const std::string id) override; - virtual void getNumEntries(int& num_entries, int& max_entries) override; + /** + * Update the "last write time" of a file to "now". This must be called whenever a + * file in the cache is read (not written) so that the last time the file was + * accessed which is used in the mechanism for purging the cache, is up to date. + */ + void updateFileAccessTime(const std::string file_path); + + /** + * Purge the oldest items in the cache so that the combined size of all files + * is no bigger than mMaxSizeBytes. + */ + void purge(); + + /** + * Clear the cache by removing the files in the cache directory individually + */ + void clearCache(const std::string cache_dir); + + /** + * Manage max size in bytes of cache - use a discrete setter/getter so the value can + * be changed in the preferences and cache cleared/purged without restarting viewer + * to instantiate this class again. + */ + void setMaxSizeBytes(const uintmax_t size_bytes) { mMaxSizeBytes = size_bytes; } + uintmax_t getMaxSizeBytes() const { return mMaxSizeBytes; } private: - sqlite3* mDb; - std::string mDataStorePath; - const std::string mTableName = "lldiskcache"; - const std::string mIdFieldName = "id"; - const std::string mFilenameFieldName = "filename"; - const std::string mFilesizeFieldName = "filesize"; - const std::string mInsertionDateTimeFieldName = "insert_datetime"; - const std::string mLastAccessDateTimeFieldName = "last_access_datetime"; - const std::string mAccessCountFieldName = "access_count"; + /** + * Utility function to gather the total size the files in a given + * directory. Primarily used here to determine the directory size + * before and after the cache purge + */ + uintmax_t dirFileSize(const std::string dir); private: - void printError(const std::string funcname, - const std::string desc, - bool is_sqlite_err); - bool beginTransaction(); - bool endTransaction(); - std::string makeUniqueFilename(); - const std::string systemSeparator(); - const std::string makeFullPath(const std::string filename); - bool sqliteExec(const std::string stmt, - const std::string funcname); - sqlite3_stmt* sqlitePrepareStep(const std::string funcname, - const std::string stmt, - int sqlite_success_condition); - const std::string sqlComposeCreateTable(); - const std::string sqlComposeExists(const std::string id); - const std::string sqlComposePut(const std::string id, - const std::string filename, - int binary_data_size); - const std::string sqlComposeGetSelect(const std::string id); - const std::string sqlComposeGetUpdate(const std::string id); - const std::string sqlComposePurge(int num_entries); + /** + * Default of 20MB seems reasonable - it will likely be reset + * immediately anyway using a value from the Viewer settings + */ + const uintmax_t mDefaultSizeBytes = 20 * 1024 * 1024; + uintmax_t mMaxSizeBytes; + + /** + * The folder that holds the cached files. The consumer of this + * class must avoid letting the user set this location as a malicious + * setting could potentially point it at a non-cache directory (for example, + * the Windows System dir) with disastrous results. + */ + std::string mCacheDir; + + /** + * This is set from the CacheDebugInfo global setting and + * when enabled, displays additional debugging information in + * various parts of the code + */ + bool mCacheDebugInfo; }; #endif // _LLDISKCACHE diff --git a/indra/llfilesystem/llfilesystem.cpp b/indra/llfilesystem/llfilesystem.cpp index f0037c9a22..ffc3dee12b 100644 --- a/indra/llfilesystem/llfilesystem.cpp +++ b/indra/llfilesystem/llfilesystem.cpp @@ -42,6 +42,8 @@ const S32 LLFileSystem::READ_WRITE = 0x00000003; // LLFileSystem::READ & LLFile const S32 LLFileSystem::APPEND = 0x00000006; // 0x00000004 & LLFileSystem::WRITE static LLTrace::BlockTimerStatHandle FTM_VFILE_WAIT("VFile Wait"); +LLDiskCache* LLFileSystem::mDiskCache = 0; +std::string LLFileSystem::mCacheDirName = "cache"; LLFileSystem::LLFileSystem(const LLUUID &file_id, const LLAssetType::EType file_type, S32 mode) { @@ -104,7 +106,7 @@ const std::string assetTypeToString(LLAssetType::EType at) return std::string("UNKNOWN"); } -const std::string idToFilepath(const std::string id, LLAssetType::EType at) +const std::string LLFileSystem::idToFilepath(const std::string id, LLAssetType::EType at) { /** * For the moment this is just {UUID}_{ASSET_TYPE}.txt but of @@ -117,7 +119,7 @@ const std::string idToFilepath(const std::string id, LLAssetType::EType at) ss << assetTypeToString(at); ss << ".txt"; - const std::string filepath = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, ss.str()); + const std::string filepath = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, mCacheDirName, ss.str()); return filepath; } @@ -289,7 +291,6 @@ BOOL LLFileSystem::write(const U8 *buffer, S32 bytes) BOOL LLFileSystem::writeFile(const U8 *buffer, S32 bytes, const LLUUID &uuid, LLAssetType::EType type) { LLFileSystem file(uuid, type, LLFileSystem::WRITE); - file.setMaxSize(bytes); return file.write(buffer, bytes); } @@ -339,13 +340,6 @@ S32 LLFileSystem::getMaxSize() return INT_MAX; } -BOOL LLFileSystem::setMaxSize(S32 size) -{ - // we don't care what the max size is so we do nothing - // and return true to indicate all was okay - return TRUE; -} - BOOL LLFileSystem::rename(const LLUUID &new_id, const LLAssetType::EType new_type) { LLFileSystem::renameFile(mFileID, mFileType, new_id, new_type); @@ -366,21 +360,15 @@ BOOL LLFileSystem::remove() // static void LLFileSystem::initClass() { + const std::string cache_dir = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, mCacheDirName); + + LLFileSystem::mDiskCache = new LLDiskCache(cache_dir); + + mDiskCache->purge(); } // static void LLFileSystem::cleanupClass() { -} - -bool LLFileSystem::isLocked() -{ - // I don't think we care about this test since there is no locking - // TODO: remove this function and calling sites? - return FALSE; -} - -void LLFileSystem::waitForLock() -{ - // TODO: remove this function and calling sites? + delete LLFileSystem::mDiskCache; } diff --git a/indra/llfilesystem/llfilesystem.h b/indra/llfilesystem/llfilesystem.h index 9cddef74a7..789f9456c7 100644 --- a/indra/llfilesystem/llfilesystem.h +++ b/indra/llfilesystem/llfilesystem.h @@ -32,6 +32,7 @@ #include "lluuid.h" #include "llassettype.h" +#include "lldiskcache.h" class LLFileSystem { @@ -51,13 +52,9 @@ public: S32 getSize(); S32 getMaxSize(); - BOOL setMaxSize(S32 size); BOOL rename(const LLUUID &new_id, const LLAssetType::EType new_type); BOOL remove(); - bool isLocked(); - void waitForLock(); - static bool getExists(const LLUUID &file_id, const LLAssetType::EType file_type); static bool removeFile(const LLUUID &file_id, const LLAssetType::EType file_type); static bool renameFile(const LLUUID &old_file_id, const LLAssetType::EType old_file_type, @@ -80,6 +77,11 @@ protected: S32 mPosition; S32 mMode; S32 mBytesRead; + +private: + static const std::string idToFilepath(const std::string id, LLAssetType::EType at); + static std::string mCacheDirName; + static LLDiskCache* mDiskCache; }; #endif // LL_FILESYSTEM_H diff --git a/indra/llmessage/lltransfertargetvfile.cpp b/indra/llmessage/lltransfertargetvfile.cpp index 471d687d67..f6faadf87f 100644 --- a/indra/llmessage/lltransfertargetvfile.cpp +++ b/indra/llmessage/lltransfertargetvfile.cpp @@ -141,7 +141,6 @@ LLTSCode LLTransferTargetVFile::dataCallback(const S32 packet_id, U8 *in_datap, LLFileSystem vf(mTempID, mParams.getAssetType(), LLFileSystem::APPEND); if (mNeedsCreate) { - vf.setMaxSize(mSize); mNeedsCreate = FALSE; } diff --git a/indra/llmessage/llxfer_vfile.cpp b/indra/llmessage/llxfer_vfile.cpp index 9de9ed379b..12419b342d 100644 --- a/indra/llmessage/llxfer_vfile.cpp +++ b/indra/llmessage/llxfer_vfile.cpp @@ -261,7 +261,6 @@ void LLXfer_VFile::setXferSize (S32 xfer_size) if (! mVFile) { LLFileSystem file(mTempID, mType, LLFileSystem::APPEND); - file.setMaxSize(xfer_size); } } diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index c45d6dd7af..0123bc32af 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -1351,6 +1351,17 @@ Value 23 + CacheDebugInfo + + Comment + When enabled, display additional cache debugging information + Persist + 1 + Type + Boolean + Value + 0 + CacheLocation Comment diff --git a/indra/newview/llfloaterbvhpreview.cpp b/indra/newview/llfloaterbvhpreview.cpp index 08f3b577b4..687d820a18 100644 --- a/indra/newview/llfloaterbvhpreview.cpp +++ b/indra/newview/llfloaterbvhpreview.cpp @@ -1000,7 +1000,6 @@ void LLFloaterBvhPreview::onBtnOK(void* userdata) LLFileSystem file(motionp->getID(), LLAssetType::AT_ANIMATION, LLFileSystem::APPEND); S32 size = dp.getCurrentSize(); - file.setMaxSize(size); if (file.write((U8*)buffer, size)) { std::string name = floaterp->getChild("name_form")->getValue().asString(); diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index a0d591dc47..3183e6d8fd 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -3241,7 +3241,7 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* b data_size = llmin(data_size, bytes); LLFileSystem file(mesh_id, LLAssetType::AT_MESH, LLFileSystem::WRITE); - if (file.getMaxSize() >= bytes || file.setMaxSize(bytes)) + if (file.getMaxSize() >= bytes) { LLMeshRepository::sCacheBytesWritten += data_size; ++LLMeshRepository::sCacheWrites; diff --git a/indra/newview/llpreviewgesture.cpp b/indra/newview/llpreviewgesture.cpp index 371153aac3..4318a55704 100644 --- a/indra/newview/llpreviewgesture.cpp +++ b/indra/newview/llpreviewgesture.cpp @@ -1140,7 +1140,6 @@ void LLPreviewGesture::saveIfNeeded() LLFileSystem file(assetId, LLAssetType::AT_GESTURE, LLFileSystem::APPEND); S32 size = dp.getCurrentSize(); - file.setMaxSize(size); file.write((U8*)buffer, size); LLLineEditor* descEditor = getChild("desc"); diff --git a/indra/newview/llpreviewnotecard.cpp b/indra/newview/llpreviewnotecard.cpp index 0bccf1d06f..32e1a4a186 100644 --- a/indra/newview/llpreviewnotecard.cpp +++ b/indra/newview/llpreviewnotecard.cpp @@ -563,7 +563,6 @@ bool LLPreviewNotecard::saveIfNeeded(LLInventoryItem* copyitem, bool sync) tid, copyitem); S32 size = buffer.length() + 1; - file.setMaxSize(size); file.write((U8*)buffer.c_str(), size); gAssetStorage->storeAssetData(tid, LLAssetType::AT_NOTECARD, diff --git a/indra/newview/llviewerassetstorage.cpp b/indra/newview/llviewerassetstorage.cpp index df3ff1a3c7..5b76d57196 100644 --- a/indra/newview/llviewerassetstorage.cpp +++ b/indra/newview/llviewerassetstorage.cpp @@ -293,8 +293,6 @@ void LLViewerAssetStorage::storeAssetData( LLFileSystem file(asset_id, asset_type, LLFileSystem::WRITE); - file.setMaxSize(size); - const S32 buf_size = 65536; U8 copy_buf[buf_size]; while ((size = (S32)fread(copy_buf, 1, buf_size, fp))) @@ -528,7 +526,6 @@ void LLViewerAssetStorage::assetRequestCoro( LLUUID temp_id; temp_id.generate(); LLFileSystem vf(temp_id, atype, LLFileSystem::WRITE); - vf.setMaxSize(size); req->mBytesFetched = size; if (!vf.write(raw.data(),size)) { diff --git a/indra/newview/llviewerassetupload.cpp b/indra/newview/llviewerassetupload.cpp index fb3ca69d5d..67ee06e255 100644 --- a/indra/newview/llviewerassetupload.cpp +++ b/indra/newview/llviewerassetupload.cpp @@ -474,8 +474,6 @@ LLSD LLNewFileResourceUploadInfo::exportTempFile() { LLFileSystem file(getAssetId(), assetType, LLFileSystem::WRITE); - file.setMaxSize(file_size); - const S32 buf_size = 65536; U8 copy_buf[buf_size]; while ((file_size = infile.read(copy_buf, buf_size))) @@ -568,7 +566,6 @@ LLSD LLBufferedAssetUploadInfo::prepareUpload() LLFileSystem file(getAssetId(), getAssetType(), LLFileSystem::APPEND); S32 size = mContents.length() + 1; - file.setMaxSize(size); file.write((U8*)mContents.c_str(), size); mStoredToCache = true; From 56e30615530bf5d1c86fbafee89c9998a079e88f Mon Sep 17 00:00:00 2001 From: Callum Prentice Date: Tue, 6 Oct 2020 17:20:08 -0700 Subject: [PATCH 13/85] Remove SQLite from project and we are now not going to use it for the cache --- indra/cmake/SQLite.cmake | 11 ----------- indra/llfilesystem/CMakeLists.txt | 3 --- indra/newview/CMakeLists.txt | 2 -- indra/newview/llappviewer.cpp | 10 ---------- indra/newview/skins/default/xui/en/strings.xml | 1 - 5 files changed, 27 deletions(-) delete mode 100644 indra/cmake/SQLite.cmake diff --git a/indra/cmake/SQLite.cmake b/indra/cmake/SQLite.cmake deleted file mode 100644 index 3571ca7d1e..0000000000 --- a/indra/cmake/SQLite.cmake +++ /dev/null @@ -1,11 +0,0 @@ -# -*- cmake -*- -include(Prebuilt) - -if (USESYSTEMLIBS) - include(FindPkgConfig) - pkg_check_modules(SQLITE REQUIRED sqlite3) -else (USESYSTEMLIBS) - use_prebuilt_binary(sqlite) - set(SQLITE_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/include/sqlite/) - set(SQLITE_LIBRARIES sqlite) -endif (USESYSTEMLIBS) diff --git a/indra/llfilesystem/CMakeLists.txt b/indra/llfilesystem/CMakeLists.txt index d1dece5bba..09c4c33ebf 100644 --- a/indra/llfilesystem/CMakeLists.txt +++ b/indra/llfilesystem/CMakeLists.txt @@ -4,13 +4,11 @@ project(llfilesystem) include(00-Common) include(LLCommon) -include(SQLite) include(UnixInstall) include_directories( ${LLCOMMON_INCLUDE_DIRS} ${LLCOMMON_SYSTEM_INCLUDE_DIRS} - ${SQLITE_INCLUDE_DIR} ) set(llfilesystem_SOURCE_FILES @@ -70,7 +68,6 @@ set(cache_BOOST_LIBRARIES target_link_libraries(llfilesystem ${LLCOMMON_LIBRARIES} ${cache_BOOST_LIBRARIES} - ${SQLITE_LIBRARIES} ) if (DARWIN) diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 129e436d5f..9fe89c1a19 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -48,7 +48,6 @@ include(OPENAL) include(OpenGL) include(OpenSSL) include(PNG) -include(SQLite) include(TemplateCheck) include(UI) include(UnixInstall) @@ -71,7 +70,6 @@ include_directories( ${DBUSGLIB_INCLUDE_DIRS} ${JSONCPP_INCLUDE_DIR} ${GLOD_INCLUDE_DIR} - ${SQLITE_INCLUDE_DIR} ${LLAUDIO_INCLUDE_DIRS} ${LLCHARACTER_INCLUDE_DIRS} ${LLCOMMON_INCLUDE_DIRS} diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 3a99c5a464..0d25cb6dc8 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -131,7 +131,6 @@ #if !LL_LINUX #include "cef/dullahan_version.h" #include "vlc/libvlc_version.h" -#include "sqlite3.h" #endif // LL_LINUX // Third party library includes @@ -3160,15 +3159,6 @@ LLSD LLAppViewer::getViewerInfo() const info["LIBCEF_VERSION"] = "Undefined"; #endif -#if !LL_LINUX - std::ostringstream sqlite_ver_codec; - sqlite_ver_codec << "SQLite: "; - sqlite_ver_codec << SQLITE_VERSION; - info["SQLITE_VERSION"] = sqlite_ver_codec.str(); -#else - info["SQLITE_VERSION"] = "Undefined"; -#endif - #if !LL_LINUX std::ostringstream vlc_ver_codec; vlc_ver_codec << LIBVLC_VERSION_MAJOR; diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml index bca577c48a..03aed8aa7e 100644 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -62,7 +62,6 @@ HiDPI display mode: [HIDPI] J2C Decoder Version: [J2C_VERSION] Audio Driver Version: [AUDIO_DRIVER_VERSION] -[SQLITE_VERSION] [LIBCEF_VERSION] LibVLC Version: [LIBVLC_VERSION] Voice Server Version: [VOICE_VERSION] From a0ea119623b8bda445f86afdb0ea7b5833c8e171 Mon Sep 17 00:00:00 2001 From: Callum Prentice Date: Tue, 6 Oct 2020 18:18:18 -0700 Subject: [PATCH 14/85] Replace references to static writefile with write so we end up with only a single read and a single write function --- indra/llfilesystem/llfilesystem.cpp | 13 ++++++------- indra/llfilesystem/llfilesystem.h | 2 +- indra/newview/llfloaterauction.cpp | 8 ++++++-- indra/newview/llfloaterreporter.cpp | 6 ++---- indra/newview/llsnapshotlivepreview.cpp | 3 ++- 5 files changed, 17 insertions(+), 15 deletions(-) diff --git a/indra/llfilesystem/llfilesystem.cpp b/indra/llfilesystem/llfilesystem.cpp index ffc3dee12b..6d6ff3d7e1 100644 --- a/indra/llfilesystem/llfilesystem.cpp +++ b/indra/llfilesystem/llfilesystem.cpp @@ -230,6 +230,12 @@ BOOL LLFileSystem::read(U8 *buffer, S32 bytes, BOOL async, F32 priority) mReadComplete = TRUE; } + // update the last access time for the file - this is required + // even though we are reading and not writing because this is the + // way the cache works - it relies on a valid "last accessed time" for + // each file so it knows how to remove the oldest, unused files + LLFileSystem::mDiskCache->updateFileAccessTime(filename); + return success; } @@ -287,13 +293,6 @@ BOOL LLFileSystem::write(const U8 *buffer, S32 bytes) return success; } -//static -BOOL LLFileSystem::writeFile(const U8 *buffer, S32 bytes, const LLUUID &uuid, LLAssetType::EType type) -{ - LLFileSystem file(uuid, type, LLFileSystem::WRITE); - return file.write(buffer, bytes); -} - BOOL LLFileSystem::seek(S32 offset, S32 origin) { if (-1 == origin) diff --git a/indra/llfilesystem/llfilesystem.h b/indra/llfilesystem/llfilesystem.h index 789f9456c7..5d87de9bf8 100644 --- a/indra/llfilesystem/llfilesystem.h +++ b/indra/llfilesystem/llfilesystem.h @@ -1,3 +1,4 @@ +/** /** * @file filesystem.h * @brief Simulate local file system operations. @@ -46,7 +47,6 @@ public: BOOL eof(); BOOL write(const U8 *buffer, S32 bytes); - static BOOL writeFile(const U8 *buffer, S32 bytes, const LLUUID &uuid, LLAssetType::EType type); BOOL seek(S32 offset, S32 origin = -1); S32 tell() const; diff --git a/indra/newview/llfloaterauction.cpp b/indra/newview/llfloaterauction.cpp index 1b9814883e..9813156bf2 100644 --- a/indra/newview/llfloaterauction.cpp +++ b/indra/newview/llfloaterauction.cpp @@ -201,7 +201,9 @@ void LLFloaterAuction::onClickSnapshot(void* data) LLPointer tga = new LLImageTGA; tga->encode(raw); - LLFileSystem::writeFile(tga->getData(), tga->getDataSize(), self->mImageID, LLAssetType::AT_IMAGE_TGA); + + LLFileSystem tga_file(self->mImageID, LLAssetType::AT_IMAGE_TGA, LLFileSystem::WRITE); + tga_file.write(tga->getData(), tga->getDataSize()); raw->biasedScaleToPowerOfTwo(LLViewerTexture::MAX_IMAGE_SIZE_DEFAULT); @@ -209,7 +211,9 @@ void LLFloaterAuction::onClickSnapshot(void* data) LLPointer j2c = new LLImageJ2C; j2c->encode(raw, 0.0f); - LLFileSystem::writeFile(j2c->getData(), j2c->getDataSize(), self->mImageID, LLAssetType::AT_TEXTURE); + + LLFileSystem j2c_file(self->mImageID, LLAssetType::AT_TEXTURE, LLFileSystem::WRITE); + j2c_file.write(j2c->getData(), j2c->getDataSize()); self->mImage = LLViewerTextureManager::getLocalTexture((LLImageRaw*)raw, FALSE); gGL.getTexUnit(0)->bind(self->mImage); diff --git a/indra/newview/llfloaterreporter.cpp b/indra/newview/llfloaterreporter.cpp index 3ef80300ef..5d0e2bbc55 100644 --- a/indra/newview/llfloaterreporter.cpp +++ b/indra/newview/llfloaterreporter.cpp @@ -899,10 +899,8 @@ void LLFloaterReporter::takeScreenshot(bool use_prev_screenshot) mResourceDatap->mAssetInfo.setDescription("screenshot_descr"); // store in cache - LLFileSystem::writeFile(upload_data->getData(), - upload_data->getDataSize(), - mResourceDatap->mAssetInfo.mUuid, - mResourceDatap->mAssetInfo.mType); + LLFileSystem j2c_file(mResourceDatap->mAssetInfo.mUuid, mResourceDatap->mAssetInfo.mType, LLFileSystem::WRITE); + j2c_file.write(upload_data->getData(), upload_data->getDataSize()); // store in the image list so it doesn't try to fetch from the server LLPointer image_in_list = diff --git a/indra/newview/llsnapshotlivepreview.cpp b/indra/newview/llsnapshotlivepreview.cpp index 795563e295..7255ab77a4 100644 --- a/indra/newview/llsnapshotlivepreview.cpp +++ b/indra/newview/llsnapshotlivepreview.cpp @@ -1005,7 +1005,8 @@ void LLSnapshotLivePreview::saveTexture(BOOL outfit_snapshot, std::string name) if (formatted->encode(scaled, 0.0f)) { - LLFileSystem::writeFile(formatted->getData(), formatted->getDataSize(), new_asset_id, LLAssetType::AT_TEXTURE); + LLFileSystem fmt_file(new_asset_id, LLAssetType::AT_TEXTURE, LLFileSystem::WRITE); + fmt_file.write(formatted->getData(), formatted->getDataSize()); std::string pos_string; LLAgentUI::buildLocationString(pos_string, LLAgentUI::LOCATION_FORMAT_FULL); std::string who_took_it; From 08dfc0836fb12855d0c07d811e2909400d5b74f3 Mon Sep 17 00:00:00 2001 From: Callum Prentice Date: Wed, 7 Oct 2020 15:25:12 -0700 Subject: [PATCH 15/85] This changeset hooks up many things that have been in progress and moves things about between llfilesystem and lldiskcache - there is still some bookkeeping work left but this is the first version that appears to work and actively manage the cache --- indra/llfilesystem/lldiskcache.cpp | 136 +++++++++---- indra/llfilesystem/lldiskcache.h | 82 ++++++-- indra/llfilesystem/llfilesystem.cpp | 248 ++++++++---------------- indra/llfilesystem/llfilesystem.h | 82 ++++---- indra/newview/app_settings/settings.xml | 26 ++- indra/newview/llappviewer.cpp | 15 +- 6 files changed, 313 insertions(+), 276 deletions(-) diff --git a/indra/llfilesystem/lldiskcache.cpp b/indra/llfilesystem/lldiskcache.cpp index 4b2ba0dd79..4b82cf3cce 100644 --- a/indra/llfilesystem/lldiskcache.cpp +++ b/indra/llfilesystem/lldiskcache.cpp @@ -25,41 +25,31 @@ */ #include "linden_common.h" -#include "lluuid.h" +#include "llassettype.h" #include "lldir.h" +#include +#include +#include #include "lldiskcache.h" -#include -#include - -#include -#include -#include - -LLDiskCache::LLDiskCache(const std::string cache_dir) : +LLDiskCache::LLDiskCache(const std::string cache_dir, + const int max_size_bytes, + const bool enable_cache_debug_info) : mCacheDir(cache_dir), - mMaxSizeBytes(mDefaultSizeBytes) + mMaxSizeBytes(max_size_bytes), + mEnableCacheDebugInfo(enable_cache_debug_info) { - // no access to LLControlGroup / gSavedSettings so use the system environment - // to trigger additional debugging on top of the default "we purged the cache" - mCacheDebugInfo = true; - if (getenv("LL_CACHE_DEBUGGING") != nullptr) - { - mCacheDebugInfo = true; - } + // the prefix used for cache filenames to disambiguate them from other files + mCacheFilenamePrefix = "sl_cache"; // create cache dir if it does not exist boost::filesystem::create_directory(cache_dir); } -LLDiskCache::~LLDiskCache() -{ -} - void LLDiskCache::purge() { - if (mCacheDebugInfo) + if (mEnableCacheDebugInfo) { LL_INFOS() << "Total dir size before purge is " << dirFileSize(mCacheDir) << LL_ENDL; } @@ -75,11 +65,14 @@ void LLDiskCache::purge() { if (boost::filesystem::is_regular_file(entry)) { - uintmax_t file_size = boost::filesystem::file_size(entry); - const std::string file_path = entry.path().string(); - const std::time_t file_time = boost::filesystem::last_write_time(entry); + if (entry.path().string().find(mCacheFilenamePrefix) != std::string::npos) + { + uintmax_t file_size = boost::filesystem::file_size(entry); + const std::string file_path = entry.path().string(); + const std::time_t file_time = boost::filesystem::last_write_time(entry); - file_info.push_back(file_info_t(file_time, { file_size, file_path })); + file_info.push_back(file_info_t(file_time, { file_size, file_path })); + } } } } @@ -107,7 +100,7 @@ void LLDiskCache::purge() action = " KEEP:"; } - if (mCacheDebugInfo) + if (mEnableCacheDebugInfo) { // have to do this because of LL_INFO/LL_END weirdness std::ostringstream line; @@ -121,7 +114,7 @@ void LLDiskCache::purge() } } - if (mCacheDebugInfo) + if (mEnableCacheDebugInfo) { auto end_time = std::chrono::high_resolution_clock::now(); auto execute_time = std::chrono::duration_cast(end_time - start_time).count(); @@ -130,9 +123,78 @@ void LLDiskCache::purge() } } +const std::string LLDiskCache::assetTypeToString(LLAssetType::EType at) +{ + /** + * Make use of the C++17 (or is it 14) feature that allows + * for inline initialization of an std::map<> + */ + typedef std::map asset_type_to_name_t; + asset_type_to_name_t asset_type_to_name = + { + { LLAssetType::AT_TEXTURE, "TEXTURE" }, + { LLAssetType::AT_SOUND, "SOUND" }, + { LLAssetType::AT_CALLINGCARD, "CALLINGCARD" }, + { LLAssetType::AT_LANDMARK, "LANDMARK" }, + { LLAssetType::AT_SCRIPT, "SCRIPT" }, + { LLAssetType::AT_CLOTHING, "CLOTHING" }, + { LLAssetType::AT_OBJECT, "OBJECT" }, + { LLAssetType::AT_NOTECARD, "NOTECARD" }, + { LLAssetType::AT_CATEGORY, "CATEGORY" }, + { LLAssetType::AT_LSL_TEXT, "LSL_TEXT" }, + { LLAssetType::AT_LSL_BYTECODE, "LSL_BYTECODE" }, + { LLAssetType::AT_TEXTURE_TGA, "TEXTURE_TGA" }, + { LLAssetType::AT_BODYPART, "BODYPART" }, + { LLAssetType::AT_SOUND_WAV, "SOUND_WAV" }, + { LLAssetType::AT_IMAGE_TGA, "IMAGE_TGA" }, + { LLAssetType::AT_IMAGE_JPEG, "IMAGE_JPEG" }, + { LLAssetType::AT_ANIMATION, "ANIMATION" }, + { LLAssetType::AT_GESTURE, "GESTURE" }, + { LLAssetType::AT_SIMSTATE, "SIMSTATE" }, + { LLAssetType::AT_LINK, "LINK" }, + { LLAssetType::AT_LINK_FOLDER, "LINK_FOLDER" }, + { LLAssetType::AT_MARKETPLACE_FOLDER, "MARKETPLACE_FOLDER" }, + { LLAssetType::AT_WIDGET, "WIDGET" }, + { LLAssetType::AT_PERSON, "PERSON" }, + { LLAssetType::AT_MESH, "MESH" }, + { LLAssetType::AT_SETTINGS, "SETTINGS" }, + { LLAssetType::AT_UNKNOWN, "UNKNOWN" } + }; + + asset_type_to_name_t::iterator iter = asset_type_to_name.find(at); + if (iter != asset_type_to_name.end()) + { + return iter->second; + } + + return std::string("UNKNOWN"); +} + +const std::string LLDiskCache::metaDataToFilepath(const std::string id, + LLAssetType::EType at, + const std::string extra_info) +{ + std::ostringstream file_path; + + file_path << mCacheDir; + file_path << gDirUtilp->getDirDelimiter(); + file_path << mCacheFilenamePrefix; + file_path << "_"; + file_path << id; + file_path << "_"; + file_path << (extra_info.empty() ? "0" : extra_info); + file_path << "_"; + file_path << assetTypeToString(at); + file_path << ".asset"; + + LL_INFOS() << "filepath.str() = " << file_path.str() << LL_ENDL; + + return file_path.str(); +} + /** - * Update the "last write time" of a file to "now". This must be called whenever a - * file in the cache is read (not written) so that the last time the file was + * Update the "last write time" of a file to "now". This must be called whenever a + * file in the cache is read (not written) so that the last time the file was * accessed which is used in the mechanism for purging the cache, is up to date. */ void LLDiskCache::updateFileAccessTime(const std::string file_path) @@ -144,8 +206,8 @@ void LLDiskCache::updateFileAccessTime(const std::string file_path) /** * Clear the cache by removing all the files in the cache directory * individually. It's important to maintain control of which directory - * if passed in and not let the user inadvertently (or maliciously) set - * it to an random location like your project source or OS system directory + * if passed in and not let the user inadvertently (or maliciously) set + * it to an random location like your project source or OS system directory */ void LLDiskCache::clearCache(const std::string cache_dir) { @@ -155,7 +217,10 @@ void LLDiskCache::clearCache(const std::string cache_dir) { if (boost::filesystem::is_regular_file(entry)) { - boost::filesystem::remove(entry); + if (entry.path().string().find(mCacheFilenamePrefix) != std::string::npos) + { + boost::filesystem::remove(entry); + } } } } @@ -181,7 +246,10 @@ uintmax_t LLDiskCache::dirFileSize(const std::string dir) { if (boost::filesystem::is_regular_file(entry)) { - total_file_size += boost::filesystem::file_size(entry); + if (entry.path().string().find(mCacheFilenamePrefix) != std::string::npos) + { + total_file_size += boost::filesystem::file_size(entry); + } } } } diff --git a/indra/llfilesystem/lldiskcache.h b/indra/llfilesystem/lldiskcache.h index 1618cec6a6..12599a132e 100644 --- a/indra/llfilesystem/lldiskcache.h +++ b/indra/llfilesystem/lldiskcache.h @@ -27,15 +27,50 @@ #ifndef _LLDISKCACHE #define _LLDISKCACHE -class LLDiskCache +#include "llsingleton.h" + +class LLDiskCache : + public LLParamSingleton { public: - LLDiskCache(const std::string cache_dir); - ~LLDiskCache(); + LLSINGLETON(LLDiskCache, + const std::string cache_dir, + const int max_size_bytes, + const bool enable_cache_debug_info); + virtual ~LLDiskCache() = default; + + public: + ///** + // * The location of the cache dir is set in llappviewer at startup via the + // * saved settings parameters. We do not have access to those saved settings + // * here in LLCommon so we must provide an accessor for other classes to use + // * when they need it - e.g. LLFilesystem needs the path so it can write files + // * to it. + // */ + //const std::string getCacheDirName() { return mCacheDir; } + + ///** + // * Each cache filename has a prefix inserted (see definition of the + // * mCacheFilenamePrefix variable below for why) but the LLFileSystem + // * component needs access to it to in order to create the file so we + // * expose it by a getter here. + // */ + //const std::string getCacheFilenamePrefix() { return mCacheFilenamePrefix; } /** - * Update the "last write time" of a file to "now". This must be called whenever a - * file in the cache is read (not written) so that the last time the file was + * Construct a filename and path to it based on the file meta data + * (id, asset type, additional 'extra' info like discard level perhaps) + * Worth pointing out that this function used to be in LLFileSystem but + * so many things had to be pushed back there to accomodate it, that I + * decided to move it here. Still not sure that's completely right. + */ + const std::string metaDataToFilepath(const std::string id, + LLAssetType::EType at, + const std::string extra_info); + + /** + * Update the "last write time" of a file to "now". This must be called whenever a + * file in the cache is read (not written) so that the last time the file was * accessed which is used in the mechanism for purging the cache, is up to date. */ void updateFileAccessTime(const std::string file_path); @@ -51,28 +86,26 @@ class LLDiskCache */ void clearCache(const std::string cache_dir); - /** - * Manage max size in bytes of cache - use a discrete setter/getter so the value can - * be changed in the preferences and cache cleared/purged without restarting viewer - * to instantiate this class again. - */ - void setMaxSizeBytes(const uintmax_t size_bytes) { mMaxSizeBytes = size_bytes; } - uintmax_t getMaxSizeBytes() const { return mMaxSizeBytes; } - private: /** * Utility function to gather the total size the files in a given - * directory. Primarily used here to determine the directory size + * directory. Primarily used here to determine the directory size * before and after the cache purge */ uintmax_t dirFileSize(const std::string dir); + /** + * Utility function to convert an LLAssetType enum into a + * string that we use as part of the cache file filename + */ + const std::string assetTypeToString(LLAssetType::EType at); + private: /** - * Default of 20MB seems reasonable - it will likely be reset - * immediately anyway using a value from the Viewer settings + * The maximum size of the cache in bytes. After purge is called, the + * total size of the cache files in the cache directory will be + * less than this value */ - const uintmax_t mDefaultSizeBytes = 20 * 1024 * 1024; uintmax_t mMaxSizeBytes; /** @@ -84,11 +117,20 @@ class LLDiskCache std::string mCacheDir; /** - * This is set from the CacheDebugInfo global setting and - * when enabled, displays additional debugging information in + * The prefix inserted at the start of a cache file filename to + * help identify it as a cache file. It's probably not required + * (just the presence in the cache folder is enough) but I am + * paranoid about the cache folder being set to something bad + * like the users' OS system dir by mistake or maliciously and + * this will help to offset any damage if that happens. + */ + std::string mCacheFilenamePrefix; + + /** + * When enabled, displays additional debugging information in * various parts of the code */ - bool mCacheDebugInfo; + bool mEnableCacheDebugInfo; }; #endif // _LLDISKCACHE diff --git a/indra/llfilesystem/llfilesystem.cpp b/indra/llfilesystem/llfilesystem.cpp index 6d6ff3d7e1..c6b20caa69 100644 --- a/indra/llfilesystem/llfilesystem.cpp +++ b/indra/llfilesystem/llfilesystem.cpp @@ -1,4 +1,4 @@ -/** +/** * @file filesystem.h * @brief Simulate local file system operations. * @Note The initial implementation does actually use standard C++ @@ -8,21 +8,21 @@ * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -36,139 +36,74 @@ #include -const S32 LLFileSystem::READ = 0x00000001; -const S32 LLFileSystem::WRITE = 0x00000002; -const S32 LLFileSystem::READ_WRITE = 0x00000003; // LLFileSystem::READ & LLFileSystem::WRITE -const S32 LLFileSystem::APPEND = 0x00000006; // 0x00000004 & LLFileSystem::WRITE +const S32 LLFileSystem::READ = 0x00000001; +const S32 LLFileSystem::WRITE = 0x00000002; +const S32 LLFileSystem::READ_WRITE = 0x00000003; // LLFileSystem::READ & LLFileSystem::WRITE +const S32 LLFileSystem::APPEND = 0x00000006; // 0x00000004 & LLFileSystem::WRITE static LLTrace::BlockTimerStatHandle FTM_VFILE_WAIT("VFile Wait"); -LLDiskCache* LLFileSystem::mDiskCache = 0; -std::string LLFileSystem::mCacheDirName = "cache"; -LLFileSystem::LLFileSystem(const LLUUID &file_id, const LLAssetType::EType file_type, S32 mode) +LLFileSystem::LLFileSystem(const LLUUID& file_id, const LLAssetType::EType file_type, S32 mode) { - mFileType = file_type; - mFileID = file_id; - mPosition = 0; + mFileType = file_type; + mFileID = file_id; + mPosition = 0; mBytesRead = 0; - mReadComplete = FALSE; - mMode = mode; + mMode = mode; } LLFileSystem::~LLFileSystem() { } -const std::string assetTypeToString(LLAssetType::EType at) -{ - /** - * Make use of the C++17 (or is it 14) feature that allows - * for inline initialization of an std::map<> - */ - typedef std::map asset_type_to_name_t; - asset_type_to_name_t asset_type_to_name = - { - { LLAssetType::AT_TEXTURE, "TEXTURE" }, - { LLAssetType::AT_SOUND, "SOUND" }, - { LLAssetType::AT_CALLINGCARD, "CALLINGCARD" }, - { LLAssetType::AT_LANDMARK, "LANDMARK" }, - { LLAssetType::AT_SCRIPT, "SCRIPT" }, - { LLAssetType::AT_CLOTHING, "CLOTHING" }, - { LLAssetType::AT_OBJECT, "OBJECT" }, - { LLAssetType::AT_NOTECARD, "NOTECARD" }, - { LLAssetType::AT_CATEGORY, "CATEGORY" }, - { LLAssetType::AT_LSL_TEXT, "LSL_TEXT" }, - { LLAssetType::AT_LSL_BYTECODE, "LSL_BYTECODE" }, - { LLAssetType::AT_TEXTURE_TGA, "TEXTURE_TGA" }, - { LLAssetType::AT_BODYPART, "BODYPART" }, - { LLAssetType::AT_SOUND_WAV, "SOUND_WAV" }, - { LLAssetType::AT_IMAGE_TGA, "IMAGE_TGA" }, - { LLAssetType::AT_IMAGE_JPEG, "IMAGE_JPEG" }, - { LLAssetType::AT_ANIMATION, "ANIMATION" }, - { LLAssetType::AT_GESTURE, "GESTURE" }, - { LLAssetType::AT_SIMSTATE, "SIMSTATE" }, - { LLAssetType::AT_LINK, "LINK" }, - { LLAssetType::AT_LINK_FOLDER, "LINK_FOLDER" }, - { LLAssetType::AT_MARKETPLACE_FOLDER, "MARKETPLACE_FOLDER" }, - { LLAssetType::AT_WIDGET, "WIDGET" }, - { LLAssetType::AT_PERSON, "PERSON" }, - { LLAssetType::AT_MESH, "MESH" }, - { LLAssetType::AT_SETTINGS, "SETTINGS" }, - { LLAssetType::AT_UNKNOWN, "UNKNOWN" } - }; - - asset_type_to_name_t::iterator iter = asset_type_to_name.find(at); - if (iter != asset_type_to_name.end()) - { - return iter->second; - } - - return std::string("UNKNOWN"); -} - -const std::string LLFileSystem::idToFilepath(const std::string id, LLAssetType::EType at) -{ - /** - * For the moment this is just {UUID}_{ASSET_TYPE}.txt but of - * course, will be greatly expanded upon - */ - std::ostringstream ss; - ss << "00cache_"; - ss << id; - ss << "_"; - ss << assetTypeToString(at); - ss << ".txt"; - - const std::string filepath = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, mCacheDirName, ss.str()); - - return filepath; -} - // static -bool LLFileSystem::getExists(const LLUUID &file_id, const LLAssetType::EType file_type) -{ - std::string id_str; - file_id.toString(id_str); - const std::string filename = idToFilepath(id_str, file_type); - - std::ifstream file(filename, std::ios::binary); - if (file.is_open()) - { - file.seekg(0, std::ios::end); - return file.tellg() > 0; - } - return false; -} - -// static -bool LLFileSystem::removeFile(const LLUUID &file_id, const LLAssetType::EType file_type) +bool LLFileSystem::getExists(const LLUUID& file_id, const LLAssetType::EType file_type) { std::string id_str; file_id.toString(id_str); - const std::string filename = idToFilepath(id_str, file_type); - + const std::string extra_info = ""; + const std::string filename = LLDiskCache::getInstance()->metaDataToFilepath(id_str, file_type, extra_info); + + std::ifstream file(filename, std::ios::binary); + if (file.is_open()) + { + file.seekg(0, std::ios::end); + return file.tellg() > 0; + } + return false; +} + +// static +bool LLFileSystem::removeFile(const LLUUID& file_id, const LLAssetType::EType file_type) +{ + std::string id_str; + file_id.toString(id_str); + const std::string extra_info = ""; + const std::string filename = LLDiskCache::getInstance()->metaDataToFilepath(id_str, file_type, extra_info); + std::remove(filename.c_str()); return true; } // static -bool LLFileSystem::renameFile(const LLUUID &old_file_id, const LLAssetType::EType old_file_type, - const LLUUID &new_file_id, const LLAssetType::EType new_file_type) +bool LLFileSystem::renameFile(const LLUUID& old_file_id, const LLAssetType::EType old_file_type, + const LLUUID& new_file_id, const LLAssetType::EType new_file_type) { std::string old_id_str; old_file_id.toString(old_id_str); - const std::string old_filename = idToFilepath(old_id_str, old_file_type); + const std::string extra_info = ""; + const std::string old_filename = LLDiskCache::getInstance()->metaDataToFilepath(old_id_str, old_file_type, extra_info); std::string new_id_str; new_file_id.toString(new_id_str); - const std::string new_filename = idToFilepath(new_id_str, new_file_type); + const std::string new_filename = LLDiskCache::getInstance()->metaDataToFilepath(new_id_str, new_file_type, extra_info); if (std::rename(old_filename.c_str(), new_filename.c_str())) { // We would like to return FALSE here indicating the operation // failed but the original code does not and doing so seems to - // break a lot of things so we go with the flow... + // break a lot of things so we go with the flow... //return FALSE; } @@ -176,11 +111,12 @@ bool LLFileSystem::renameFile(const LLUUID &old_file_id, const LLAssetType::ETyp } // static -S32 LLFileSystem::getFileSize(const LLUUID &file_id, const LLAssetType::EType file_type) +S32 LLFileSystem::getFileSize(const LLUUID& file_id, const LLAssetType::EType file_type) { std::string id_str; file_id.toString(id_str); - const std::string filename = idToFilepath(id_str, file_type); + const std::string extra_info = ""; + const std::string filename = LLDiskCache::getInstance()->metaDataToFilepath(id_str, file_type, extra_info); S32 file_size = 0; std::ifstream file(filename, std::ios::binary); @@ -193,15 +129,14 @@ S32 LLFileSystem::getFileSize(const LLUUID &file_id, const LLAssetType::EType fi return file_size; } -BOOL LLFileSystem::read(U8 *buffer, S32 bytes, BOOL async, F32 priority) +BOOL LLFileSystem::read(U8* buffer, S32 bytes) { - BOOL success = TRUE; - - mReadComplete = FALSE; + BOOL success = TRUE; std::string id; mFileID.toString(id); - const std::string filename = idToFilepath(id, mFileType); + const std::string extra_info = ""; + const std::string filename = LLDiskCache::getInstance()->metaDataToFilepath(id, mFileType, extra_info); std::ifstream file(filename, std::ios::binary); if (file.is_open()) @@ -226,44 +161,33 @@ BOOL LLFileSystem::read(U8 *buffer, S32 bytes, BOOL async, F32 priority) { success = FALSE; } - - mReadComplete = TRUE; } - // update the last access time for the file - this is required + // update the last access time for the file - this is required // even though we are reading and not writing because this is the // way the cache works - it relies on a valid "last accessed time" for // each file so it knows how to remove the oldest, unused files - LLFileSystem::mDiskCache->updateFileAccessTime(filename); + LLDiskCache::getInstance()->updateFileAccessTime(filename); return success; } -BOOL LLFileSystem::isReadComplete() -{ - if (mReadComplete) - { - return TRUE; - } - - return FALSE; -} - S32 LLFileSystem::getLastBytesRead() { - return mBytesRead; + return mBytesRead; } BOOL LLFileSystem::eof() { - return mPosition >= getSize(); + return mPosition >= getSize(); } -BOOL LLFileSystem::write(const U8 *buffer, S32 bytes) +BOOL LLFileSystem::write(const U8* buffer, S32 bytes) { std::string id_str; mFileID.toString(id_str); - const std::string filename = idToFilepath(id_str, mFileType); + const std::string extra_info = ""; + const std::string filename = LLDiskCache::getInstance()->metaDataToFilepath(id_str, mFileType, extra_info); BOOL success = FALSE; @@ -295,37 +219,37 @@ BOOL LLFileSystem::write(const U8 *buffer, S32 bytes) BOOL LLFileSystem::seek(S32 offset, S32 origin) { - if (-1 == origin) - { - origin = mPosition; - } + if (-1 == origin) + { + origin = mPosition; + } - S32 new_pos = origin + offset; + S32 new_pos = origin + offset; - S32 size = getSize(); + S32 size = getSize(); - if (new_pos > size) - { - LL_WARNS() << "Attempt to seek past end of file" << LL_ENDL; + if (new_pos > size) + { + LL_WARNS() << "Attempt to seek past end of file" << LL_ENDL; - mPosition = size; - return FALSE; - } - else if (new_pos < 0) - { - LL_WARNS() << "Attempt to seek past beginning of file" << LL_ENDL; + mPosition = size; + return FALSE; + } + else if (new_pos < 0) + { + LL_WARNS() << "Attempt to seek past beginning of file" << LL_ENDL; - mPosition = 0; - return FALSE; - } + mPosition = 0; + return FALSE; + } - mPosition = new_pos; - return TRUE; + mPosition = new_pos; + return TRUE; } S32 LLFileSystem::tell() const { - return mPosition; + return mPosition; } S32 LLFileSystem::getSize() @@ -335,11 +259,11 @@ S32 LLFileSystem::getSize() S32 LLFileSystem::getMaxSize() { - // offer up a huge size since we don't care what the max is + // offer up a huge size since we don't care what the max is return INT_MAX; } -BOOL LLFileSystem::rename(const LLUUID &new_id, const LLAssetType::EType new_type) +BOOL LLFileSystem::rename(const LLUUID& new_id, const LLAssetType::EType new_type) { LLFileSystem::renameFile(mFileID, mFileType, new_id, new_type); @@ -355,19 +279,3 @@ BOOL LLFileSystem::remove() return TRUE; } - -// static -void LLFileSystem::initClass() -{ - const std::string cache_dir = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, mCacheDirName); - - LLFileSystem::mDiskCache = new LLDiskCache(cache_dir); - - mDiskCache->purge(); -} - -// static -void LLFileSystem::cleanupClass() -{ - delete LLFileSystem::mDiskCache; -} diff --git a/indra/llfilesystem/llfilesystem.h b/indra/llfilesystem/llfilesystem.h index 5d87de9bf8..46bf1bd775 100644 --- a/indra/llfilesystem/llfilesystem.h +++ b/indra/llfilesystem/llfilesystem.h @@ -1,5 +1,5 @@ -/** -/** +/** +/** * @file filesystem.h * @brief Simulate local file system operations. * @Note The initial implementation does actually use standard C++ @@ -9,21 +9,21 @@ * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -37,51 +37,43 @@ class LLFileSystem { -public: - LLFileSystem(const LLUUID &file_id, const LLAssetType::EType file_type, S32 mode = LLFileSystem::READ); - ~LLFileSystem(); + public: + LLFileSystem(const LLUUID& file_id, const LLAssetType::EType file_type, S32 mode = LLFileSystem::READ); + ~LLFileSystem(); - BOOL read(U8 *buffer, S32 bytes, BOOL async = FALSE, F32 priority = 128.f); /* Flawfinder: ignore */ - BOOL isReadComplete(); - S32 getLastBytesRead(); - BOOL eof(); + BOOL read(U8* buffer, S32 bytes); + S32 getLastBytesRead(); + BOOL eof(); - BOOL write(const U8 *buffer, S32 bytes); - BOOL seek(S32 offset, S32 origin = -1); - S32 tell() const; + BOOL write(const U8* buffer, S32 bytes); + BOOL seek(S32 offset, S32 origin = -1); + S32 tell() const; - S32 getSize(); - S32 getMaxSize(); - BOOL rename(const LLUUID &new_id, const LLAssetType::EType new_type); - BOOL remove(); + S32 getSize(); + S32 getMaxSize(); + BOOL rename(const LLUUID& new_id, const LLAssetType::EType new_type); + BOOL remove(); - static bool getExists(const LLUUID &file_id, const LLAssetType::EType file_type); - static bool removeFile(const LLUUID &file_id, const LLAssetType::EType file_type); - static bool renameFile(const LLUUID &old_file_id, const LLAssetType::EType old_file_type, - const LLUUID &new_file_id, const LLAssetType::EType new_file_type); - static S32 getFileSize(const LLUUID &file_id, const LLAssetType::EType file_type); - - static void initClass(); - static void cleanupClass(); + static bool getExists(const LLUUID& file_id, const LLAssetType::EType file_type); + static bool removeFile(const LLUUID& file_id, const LLAssetType::EType file_type); + static bool renameFile(const LLUUID& old_file_id, const LLAssetType::EType old_file_type, + const LLUUID& new_file_id, const LLAssetType::EType new_file_type); + static S32 getFileSize(const LLUUID& file_id, const LLAssetType::EType file_type); -public: - static const S32 READ; - static const S32 WRITE; - static const S32 READ_WRITE; - static const S32 APPEND; - -protected: - LLAssetType::EType mFileType; - BOOL mReadComplete; - LLUUID mFileID; - S32 mPosition; - S32 mMode; - S32 mBytesRead; + public: + static const S32 READ; + static const S32 WRITE; + static const S32 READ_WRITE; + static const S32 APPEND; -private: - static const std::string idToFilepath(const std::string id, LLAssetType::EType at); - static std::string mCacheDirName; - static LLDiskCache* mDiskCache; + protected: + LLAssetType::EType mFileType; + LLUUID mFileID; + S32 mPosition; + S32 mMode; + S32 mBytesRead; +//private: +// static const std::string idToFilepath(const std::string id, LLAssetType::EType at); }; #endif // LL_FILESYSTEM_H diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 0123bc32af..2e65aef9a2 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -1351,10 +1351,10 @@ Value 23 - CacheDebugInfo + EnableCacheDebugInfo Comment - When enabled, display additional cache debugging information + When set, display additional cache debugging information Persist 1 Type @@ -1362,6 +1362,28 @@ Value 0 + DiskCacheMaxSizeMB + + Comment + The maximum number of MB to use for the new disk cache + Persist + 1 + Type + U32 + Value + 10 + + DiskCacheDirName + + Comment + The name of the disk cache (within the standard Viewer disk cache directory) + Persist + 1 + Type + String + Value + dcache + CacheLocation Comment diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 0d25cb6dc8..1914ca89de 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -98,6 +98,7 @@ #include "lllogininstance.h" #include "llprogressview.h" #include "llvocache.h" +#include "lldiskcache.h" #include "llvopartgroup.h" #include "llweb.h" #include "llfloatertexturefetchdebugger.h" @@ -115,7 +116,6 @@ #include "llprimitive.h" #include "llurlaction.h" #include "llurlentry.h" -#include "llfilesystem.h" #include "llvolumemgr.h" #include "llxfermanager.h" #include "llphysicsextensions.h" @@ -1855,9 +1855,6 @@ bool LLAppViewer::cleanup() SUBSYSTEM_CLEANUP(LLWorldMapView); SUBSYSTEM_CLEANUP(LLFolderViewItem); - LL_INFOS() << "Cleaning up disk cache" << LL_ENDL; - SUBSYSTEM_CLEANUP(LLFileSystem); - LL_INFOS() << "Saving Data" << LL_ENDL; // Store the time of our current logoff @@ -4111,6 +4108,9 @@ bool LLAppViewer::initCache() { LLSplashScreen::update(LLTrans::getString("StartupClearingCache")); purgeCache(); + + // purge the new C++ file system based cache + LLDiskCache::getInstance()->purge(); } LLSplashScreen::update(LLTrans::getString("StartupInitializingTextureCache")); @@ -4131,7 +4131,12 @@ bool LLAppViewer::initCache() LLVOCache::getInstance()->initCache(LL_PATH_CACHE, gSavedSettings.getU32("CacheNumberOfRegionsForObjects"), getObjectCacheVersion()); - LLFileSystem::initClass(); + // initialize the new disk cache using saved settings + const std::string cache_dir_name = gSavedSettings.getString("DiskCacheDirName"); + const unsigned int cache_max_bytes = gSavedSettings.getU32("DiskCacheMaxSizeMB") * 1024 * 1024; + const bool enable_cache_debug_info = gSavedSettings.getBOOL("EnableDiskCacheDebugInfo"); + const std::string cache_dir = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, cache_dir_name); + LLDiskCache::initParamSingleton(cache_dir, cache_max_bytes, enable_cache_debug_info); return true; } From 5fdc4a1fb4a1788b20ca7a2e7764fd1391c35785 Mon Sep 17 00:00:00 2001 From: Callum Prentice Date: Wed, 7 Oct 2020 16:03:28 -0700 Subject: [PATCH 16/85] Add in some cache stats for the about box --- indra/llfilesystem/lldiskcache.cpp | 20 +++++++++++++++-- indra/llfilesystem/lldiskcache.h | 22 +++++-------------- indra/newview/llappviewer.cpp | 3 +++ .../newview/skins/default/xui/en/strings.xml | 3 ++- 4 files changed, 28 insertions(+), 20 deletions(-) diff --git a/indra/llfilesystem/lldiskcache.cpp b/indra/llfilesystem/lldiskcache.cpp index 4b82cf3cce..455e27221e 100644 --- a/indra/llfilesystem/lldiskcache.cpp +++ b/indra/llfilesystem/lldiskcache.cpp @@ -187,8 +187,6 @@ const std::string LLDiskCache::metaDataToFilepath(const std::string id, file_path << assetTypeToString(at); file_path << ".asset"; - LL_INFOS() << "filepath.str() = " << file_path.str() << LL_ENDL; - return file_path.str(); } @@ -203,6 +201,24 @@ void LLDiskCache::updateFileAccessTime(const std::string file_path) boost::filesystem::last_write_time(file_path, file_time); } +/** + * + */ +const std::string LLDiskCache::getCacheInfo() +{ + std::ostringstream cache_info; + + F32 max_in_mb = (F32)mMaxSizeBytes / (1024.0 * 1024.0); + F32 percent_used = ((F32)dirFileSize(mCacheDir) / (F32)mMaxSizeBytes) * 100.0; + + cache_info << std::fixed; + cache_info << std::setprecision(1); + cache_info << "Max size " << max_in_mb << " MB "; + cache_info << "(" << percent_used << "% used)"; + + return cache_info.str(); +} + /** * Clear the cache by removing all the files in the cache directory * individually. It's important to maintain control of which directory diff --git a/indra/llfilesystem/lldiskcache.h b/indra/llfilesystem/lldiskcache.h index 12599a132e..34a4fda756 100644 --- a/indra/llfilesystem/lldiskcache.h +++ b/indra/llfilesystem/lldiskcache.h @@ -40,23 +40,6 @@ class LLDiskCache : virtual ~LLDiskCache() = default; public: - ///** - // * The location of the cache dir is set in llappviewer at startup via the - // * saved settings parameters. We do not have access to those saved settings - // * here in LLCommon so we must provide an accessor for other classes to use - // * when they need it - e.g. LLFilesystem needs the path so it can write files - // * to it. - // */ - //const std::string getCacheDirName() { return mCacheDir; } - - ///** - // * Each cache filename has a prefix inserted (see definition of the - // * mCacheFilenamePrefix variable below for why) but the LLFileSystem - // * component needs access to it to in order to create the file so we - // * expose it by a getter here. - // */ - //const std::string getCacheFilenamePrefix() { return mCacheFilenamePrefix; } - /** * Construct a filename and path to it based on the file meta data * (id, asset type, additional 'extra' info like discard level perhaps) @@ -86,6 +69,11 @@ class LLDiskCache : */ void clearCache(const std::string cache_dir); + /** + * Return some information about the cache for use in About Box etc. + */ + const std::string getCacheInfo(); + private: /** * Utility function to gather the total size the files in a given diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 1914ca89de..afafedf207 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -3196,6 +3196,9 @@ LLSD LLAppViewer::getViewerInfo() const info["SERVER_RELEASE_NOTES_URL"] = mServerReleaseNotesURL; } + // populate field for new local disk cache with some details + info["DISK_CACHE_INFO"] = LLDiskCache::getInstance()->getCacheInfo(); + return info; } diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml index 03aed8aa7e..619c869a3d 100644 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -55,8 +55,9 @@ LOD factor: [LOD_FACTOR] Render quality: [RENDER_QUALITY] Advanced Lighting Model: [GPU_SHADERS] Texture memory: [TEXTURE_MEMORY]MB +Disk cache: [DISK_CACHE_INFO] - + HiDPI display mode: [HIDPI] From 1a9942a51c2b5db51adb75356f342665743d1f16 Mon Sep 17 00:00:00 2001 From: Callum Prentice Date: Wed, 7 Oct 2020 16:43:01 -0700 Subject: [PATCH 17/85] Improve, rationalize and expand comments --- indra/llfilesystem/lldiskcache.cpp | 49 ++++++++++------------ indra/llfilesystem/lldiskcache.h | 65 ++++++++++++++++++++++++++++-- 2 files changed, 84 insertions(+), 30 deletions(-) diff --git a/indra/llfilesystem/lldiskcache.cpp b/indra/llfilesystem/lldiskcache.cpp index 455e27221e..e2e50c775d 100644 --- a/indra/llfilesystem/lldiskcache.cpp +++ b/indra/llfilesystem/lldiskcache.cpp @@ -2,6 +2,12 @@ * @file lldiskcache.cpp * @brief The disk cache implementation. * + * Note: Rather than keep the top level function comments up + * to date in both the source and header files, I elected to + * only have explicit comments about each function and variable + * in the header - look there for details. The same is true for + * description of how this code is supposed to work. + * * $LicenseInfo:firstyear=2009&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2020, Linden Research, Inc. @@ -40,10 +46,8 @@ LLDiskCache::LLDiskCache(const std::string cache_dir, mMaxSizeBytes(max_size_bytes), mEnableCacheDebugInfo(enable_cache_debug_info) { - // the prefix used for cache filenames to disambiguate them from other files mCacheFilenamePrefix = "sl_cache"; - // create cache dir if it does not exist boost::filesystem::create_directory(cache_dir); } @@ -126,7 +130,7 @@ void LLDiskCache::purge() const std::string LLDiskCache::assetTypeToString(LLAssetType::EType at) { /** - * Make use of the C++17 (or is it 14) feature that allows + * Make use of the handy C++17 feature that allows * for inline initialization of an std::map<> */ typedef std::map asset_type_to_name_t; @@ -190,20 +194,12 @@ const std::string LLDiskCache::metaDataToFilepath(const std::string id, return file_path.str(); } -/** - * Update the "last write time" of a file to "now". This must be called whenever a - * file in the cache is read (not written) so that the last time the file was - * accessed which is used in the mechanism for purging the cache, is up to date. - */ void LLDiskCache::updateFileAccessTime(const std::string file_path) { const std::time_t file_time = std::time(nullptr); boost::filesystem::last_write_time(file_path, file_time); } -/** - * - */ const std::string LLDiskCache::getCacheInfo() { std::ostringstream cache_info; @@ -219,14 +215,14 @@ const std::string LLDiskCache::getCacheInfo() return cache_info.str(); } -/** - * Clear the cache by removing all the files in the cache directory - * individually. It's important to maintain control of which directory - * if passed in and not let the user inadvertently (or maliciously) set - * it to an random location like your project source or OS system directory - */ void LLDiskCache::clearCache(const std::string cache_dir) { + /** + * See notes on performance in dirFileSize(..) - there may be + * a quicker way to do this by operating on the parent dir vs + * the component files but it's called infrequently so it's + * likely just fine + */ if (boost::filesystem::is_directory(cache_dir)) { for (auto& entry : boost::make_iterator_range(boost::filesystem::directory_iterator(cache_dir), {})) @@ -242,20 +238,19 @@ void LLDiskCache::clearCache(const std::string cache_dir) } } -/** - * Utility function to get the total filesize of all files in a directory. It - * used to test file extensions to only check cache files but that was removed. - * There may be a better way that works directly on the folder (similar to - * right clicking on a folder in the OS and asking for size vs right clicking - * on all files and adding up manually) but this is very fast - less than 100ms - * in my testing so, so long as it's not called frequently, it should be okay. - * Note that's it's only currently used for logging/debugging so if performance - * is ever an issue, optimizing this or removing it altogether, is an easy win. - */ uintmax_t LLDiskCache::dirFileSize(const std::string dir) { uintmax_t total_file_size = 0; + /** + * There may be a better way that works directly on the folder (similar to + * right clicking on a folder in the OS and asking for size vs right clicking + * on all files and adding up manually) but this is very fast - less than 100ms + * for 10,000 files in my testing so, so long as it's not called frequently, + * it should be okay. Note that's it's only currently used for logging/debugging + * so if performance is ever an issue, optimizing this or removing it altogether, + * is an easy win. + */ if (boost::filesystem::is_directory(dir)) { for (auto& entry : boost::make_iterator_range(boost::filesystem::directory_iterator(dir), {})) diff --git a/indra/llfilesystem/lldiskcache.h b/indra/llfilesystem/lldiskcache.h index 34a4fda756..f718b7a328 100644 --- a/indra/llfilesystem/lldiskcache.h +++ b/indra/llfilesystem/lldiskcache.h @@ -1,6 +1,41 @@ /** * @file lldiskcache.h - * @brief The disk cache implementation. + * @brief The disk cache implementation declarations. + * + * @Description: + * This code implements a disk cache using the following ideas: + * 1/ The metadata for a file can be encapsulated in the filename. + The filenames will be composed of the following fields: + Prefix: Used to identify the file as a part of the cache. + An additional reason for using a prefix is that it + might be possible, either accidentally or maliciously + to end up with the cache dir set to a non-cache + location such as your OS system dir or a work folder. + Purging files from that would obviously be a disaster + so this is an extra step to help avoid that scenario. + ID: Typically the asset ID (UUID) of the asset being + saved but can be anything valid for a filename + Extra Info: A field for use in the future that can be used + to store extra identifiers - e.g. the discard + level of a JPEG2000 file + Asset Type: A text string created from the LLAssetType enum + that identifies the type of asset being stored. + .asset A file extension of .asset is used to help + identify this as a Viewer asset file + * 2/ The time of last access for a file can be updated instantly + * for file reads and automatically as part of the file writes. + * 3/ The purge algorithm collects a list of all files in the + * directory, sorts them by date of last access (write) and then + * deletes any files based on age until the total size of all + * the files is less than the maximum size specified. + * 4/ An LLSingleton idiom is used since there will only ever be + * a single cache and we want to access it from numerous places. + * 5/ Performance on my modest system seems very acceptable. For + * example, in testing, I was able to purge a directory of + * 10,000 files, deleting about half of them in ~ 1700ms. For + * the same sized directory of files, writing the last updated + * time to each took less than 600ms indicating that this + * important part of the mechanism has almost no overhead. * * $LicenseInfo:firstyear=2009&license=viewerlgpl$ * Second Life Viewer Source Code @@ -33,10 +68,32 @@ class LLDiskCache : public LLParamSingleton { public: + /** + * Since this is using the LLSingleton pattern but we + * want to allow the constructor to be called first + * with various parameters, we also invoke the + * LLParamSingleton idiom and use it to initialize + * the class via a call in LLAppViewer. + */ LLSINGLETON(LLDiskCache, + /** + * The full name of the cache folder - typically a + * a child of the main Viewer cache directory. Defined + * by the setting at 'DiskCacheDirName' + */ const std::string cache_dir, + /** + * The maximum size of the cache in bytes - Defined by + * the setting at 'DiskCacheMaxSizeMB' (* 1024 * 1024) + */ const int max_size_bytes, + /** + * A flag that enables extra cache debugging so that + * if there are bugs, we can ask uses to enable this + * setting and send us their logs + */ const bool enable_cache_debug_info); + virtual ~LLDiskCache() = default; public: @@ -54,7 +111,7 @@ class LLDiskCache : /** * Update the "last write time" of a file to "now". This must be called whenever a * file in the cache is read (not written) so that the last time the file was - * accessed which is used in the mechanism for purging the cache, is up to date. + * accessed is up to date (This is used in the mechanism for purging the cache) */ void updateFileAccessTime(const std::string file_path); @@ -65,7 +122,9 @@ class LLDiskCache : void purge(); /** - * Clear the cache by removing the files in the cache directory individually + * Clear the cache by removing all the files in the specified cache + * directory individually. Only the files that contain a prefix defined + * by mCacheFilenamePrefix will be removed. */ void clearCache(const std::string cache_dir); From a11e8ffe4bc4dee79bf199592718837ef2429a29 Mon Sep 17 00:00:00 2001 From: Callum Prentice Date: Wed, 7 Oct 2020 16:50:33 -0700 Subject: [PATCH 18/85] remove the static cache files from the viewer and update viewer_manifest.py to not try to copy them into the installer bundle --- indra/newview/app_settings/static_data.db2 | Bin 576578 -> 0 bytes indra/newview/app_settings/static_index.db2 | Bin 9894 -> 0 bytes indra/newview/viewer_manifest.py | 1 - 3 files changed, 1 deletion(-) delete mode 100644 indra/newview/app_settings/static_data.db2 delete mode 100644 indra/newview/app_settings/static_index.db2 diff --git a/indra/newview/app_settings/static_data.db2 b/indra/newview/app_settings/static_data.db2 deleted file mode 100644 index f85aa81601787e0643162d85f1086f548577809d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 576578 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�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

iUO{A+_>y^r$H<>b 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�sEslb`{_o+p3MRGmyKk7StqW&>O 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 diff --git a/indra/newview/app_settings/static_index.db2 b/indra/newview/app_settings/static_index.db2 deleted file mode 100644 index a5440f96f2b16bd93bb9eeffe561ea1db8b93c25..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9894 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

KmtC<(>d4qd0zj` zNBA}wKNj4ZH;-@6%SnwtK3avL$9m(m%?PchW{s<8AOuQ)3X=FMu|7gCWpaB{xKn&Y zgN8f)-NikUnAaM*SEqewW0L7zN#bo~1J3nYNWcgAvB2o9eCFlUmg6mEdVFTz#|dyB zMXZ_B_~(4*J;l1^-{~W{7IWNZ9q?|el_VBd5Fd1%)$0f3Ja~V$eTH>?X;!w=5u$=( Tu1Tg_{HGecd?G%d`H%P?%$X-n diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 90a2af98f7..def73a3d51 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -69,7 +69,6 @@ class ViewerManifest(LLManifest): self.exclude("logcontrol-dev.xml") self.path("*.ini") self.path("*.xml") - self.path("*.db2") # include the entire shaders directory recursively self.path("shaders") From a818c44ef21cf6b4e4eeab0ee0325a780898fccc Mon Sep 17 00:00:00 2001 From: Callum Prentice Date: Wed, 7 Oct 2020 18:16:30 -0700 Subject: [PATCH 19/85] Tweak name of cache folder in the Viewer cache directory --- indra/newview/app_settings/settings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 2e65aef9a2..357ba4bc77 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -1382,7 +1382,7 @@ Type String Value - dcache + cache CacheLocation From 3051db7b61ee43fffd28f0a12c0714b11b6b7df7 Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Thu, 8 Oct 2020 15:26:54 +0300 Subject: [PATCH 20/85] Purge excessive files from disc cache each startup --- indra/llfilesystem/lldiskcache.cpp | 6 +++--- indra/llfilesystem/lldiskcache.h | 2 +- indra/newview/llappviewer.cpp | 32 +++++++++++++++++++----------- 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/indra/llfilesystem/lldiskcache.cpp b/indra/llfilesystem/lldiskcache.cpp index e2e50c775d..efe5e7092c 100644 --- a/indra/llfilesystem/lldiskcache.cpp +++ b/indra/llfilesystem/lldiskcache.cpp @@ -215,7 +215,7 @@ const std::string LLDiskCache::getCacheInfo() return cache_info.str(); } -void LLDiskCache::clearCache(const std::string cache_dir) +void LLDiskCache::clearCache() { /** * See notes on performance in dirFileSize(..) - there may be @@ -223,9 +223,9 @@ void LLDiskCache::clearCache(const std::string cache_dir) * the component files but it's called infrequently so it's * likely just fine */ - if (boost::filesystem::is_directory(cache_dir)) + if (boost::filesystem::is_directory(mCacheDir)) { - for (auto& entry : boost::make_iterator_range(boost::filesystem::directory_iterator(cache_dir), {})) + for (auto& entry : boost::make_iterator_range(boost::filesystem::directory_iterator(mCacheDir), {})) { if (boost::filesystem::is_regular_file(entry)) { diff --git a/indra/llfilesystem/lldiskcache.h b/indra/llfilesystem/lldiskcache.h index f718b7a328..b25eac8538 100644 --- a/indra/llfilesystem/lldiskcache.h +++ b/indra/llfilesystem/lldiskcache.h @@ -126,7 +126,7 @@ class LLDiskCache : * directory individually. Only the files that contain a prefix defined * by mCacheFilenamePrefix will be removed. */ - void clearCache(const std::string cache_dir); + void clearCache(); /** * Return some information about the cache for use in About Box etc. diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index afafedf207..9e50860064 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -4061,6 +4061,13 @@ bool LLAppViewer::initCache() LLAppViewer::getTextureCache()->setReadOnly(read_only) ; LLVOCache::initParamSingleton(read_only); + // initialize the new disk cache using saved settings + const std::string cache_dir_name = gSavedSettings.getString("DiskCacheDirName"); + const unsigned int cache_max_bytes = gSavedSettings.getU32("DiskCacheMaxSizeMB") * 1024 * 1024; + const bool enable_cache_debug_info = gSavedSettings.getBOOL("EnableDiskCacheDebugInfo"); + const std::string cache_dir = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, cache_dir_name); + LLDiskCache::initParamSingleton(cache_dir, cache_max_bytes, enable_cache_debug_info); + bool texture_cache_mismatch = false; if (gSavedSettings.getS32("LocalCacheVersion") != LLAppViewer::getTextureCacheVersion()) { @@ -4107,13 +4114,21 @@ bool LLAppViewer::initCache() gSavedSettings.setString("CacheLocationTopFolder", ""); } - if (mPurgeCache && !read_only) + if (!read_only) { - LLSplashScreen::update(LLTrans::getString("StartupClearingCache")); - purgeCache(); + if (mPurgeCache) + { + LLSplashScreen::update(LLTrans::getString("StartupClearingCache")); + purgeCache(); - // purge the new C++ file system based cache - LLDiskCache::getInstance()->purge(); + // clear the new C++ file system based cache + LLDiskCache::getInstance()->clearCache(); + } + else + { + // purge excessive files from the new file system based cache + LLDiskCache::getInstance()->purge(); + } } LLSplashScreen::update(LLTrans::getString("StartupInitializingTextureCache")); @@ -4134,13 +4149,6 @@ bool LLAppViewer::initCache() LLVOCache::getInstance()->initCache(LL_PATH_CACHE, gSavedSettings.getU32("CacheNumberOfRegionsForObjects"), getObjectCacheVersion()); - // initialize the new disk cache using saved settings - const std::string cache_dir_name = gSavedSettings.getString("DiskCacheDirName"); - const unsigned int cache_max_bytes = gSavedSettings.getU32("DiskCacheMaxSizeMB") * 1024 * 1024; - const bool enable_cache_debug_info = gSavedSettings.getBOOL("EnableDiskCacheDebugInfo"); - const std::string cache_dir = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, cache_dir_name); - LLDiskCache::initParamSingleton(cache_dir, cache_max_bytes, enable_cache_debug_info); - return true; } From 31d80c21b5e17e962c0fdb5a370d3ddea8694768 Mon Sep 17 00:00:00 2001 From: Mnikolenko ProductEngine Date: Thu, 8 Oct 2020 21:17:28 +0300 Subject: [PATCH 21/85] macos build fix --- indra/llfilesystem/llfilesystem.h | 1 - 1 file changed, 1 deletion(-) diff --git a/indra/llfilesystem/llfilesystem.h b/indra/llfilesystem/llfilesystem.h index 46bf1bd775..89bfff5798 100644 --- a/indra/llfilesystem/llfilesystem.h +++ b/indra/llfilesystem/llfilesystem.h @@ -1,4 +1,3 @@ -/** /** * @file filesystem.h * @brief Simulate local file system operations. From a350009614c0840e5535d5c7d2281ecb3104642d Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Tue, 13 Oct 2020 15:01:22 +0300 Subject: [PATCH 22/85] SL-14107 fixed crash due to missing animation when avatar hits the ground after falling down --- indra/llcharacter/llkeyframefallmotion.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/indra/llcharacter/llkeyframefallmotion.cpp b/indra/llcharacter/llkeyframefallmotion.cpp index 60ab2e9929..7842f0e5fb 100644 --- a/indra/llcharacter/llkeyframefallmotion.cpp +++ b/indra/llcharacter/llkeyframefallmotion.cpp @@ -70,6 +70,11 @@ LLMotion::LLMotionInitStatus LLKeyframeFallMotion::onInitialize(LLCharacter *cha // load keyframe data, setup pose and joint states LLMotion::LLMotionInitStatus result = LLKeyframeMotion::onInitialize(character); + if (result != LLMotion::STATUS_SUCCESS) + { + return result; + } + for (U32 jm=0; jmgetNumJointMotions(); jm++) { if (!mJointStates[jm]->getJoint()) From c66c426d6b5bfab3922471adf7402bee5d10f8ec Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Mon, 26 Oct 2020 20:01:12 +0200 Subject: [PATCH 23/85] SL-14148 remove redundant zeroing of the file --- indra/newview/app_settings/settings.xml | 2 +- indra/newview/llmeshrepository.cpp | 15 --------------- 2 files changed, 1 insertion(+), 16 deletions(-) diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index cb21adaebd..fc1437148a 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -1371,7 +1371,7 @@ Type U32 Value - 10 + 50 DiskCacheDirName diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 3183e6d8fd..bd4bebbf23 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -3247,21 +3247,6 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* b ++LLMeshRepository::sCacheWrites; file.write(data, data_size); - - // zero out the rest of the file - U8 block[MESH_HEADER_SIZE]; - memset(block, 0, sizeof(block)); - - while (bytes-file.tell() > sizeof(block)) - { - file.write(block, sizeof(block)); - } - - S32 remaining = bytes-file.tell(); - if (remaining > 0) - { - file.write(block, remaining); - } } } else From 3b4bd86a1de3fb1a9065024089fcfec2dae1da85 Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Tue, 27 Oct 2020 16:46:31 +0200 Subject: [PATCH 24/85] SL-14182 remove old script asset file after saving changes and allow renaming files if destination file exists --- indra/llfilesystem/llfilesystem.cpp | 4 ++++ indra/newview/llpreviewscript.cpp | 14 +++++++++++--- indra/newview/llpreviewscript.h | 4 ++++ 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/indra/llfilesystem/llfilesystem.cpp b/indra/llfilesystem/llfilesystem.cpp index c6b20caa69..932ef2a9c6 100644 --- a/indra/llfilesystem/llfilesystem.cpp +++ b/indra/llfilesystem/llfilesystem.cpp @@ -99,12 +99,16 @@ bool LLFileSystem::renameFile(const LLUUID& old_file_id, const LLAssetType::ETyp new_file_id.toString(new_id_str); const std::string new_filename = LLDiskCache::getInstance()->metaDataToFilepath(new_id_str, new_file_type, extra_info); + // Rename needs the new file to not exist. + LLFileSystem::removeFile(new_file_id, new_file_type); + if (std::rename(old_filename.c_str(), new_filename.c_str())) { // We would like to return FALSE here indicating the operation // failed but the original code does not and doing so seems to // break a lot of things so we go with the flow... //return FALSE; + LL_WARNS() << "Failed to rename " << old_file_id << " to " << new_id_str << " reason: " << strerror(errno) << LL_ENDL; } return TRUE; diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp index eae6c28e35..e92d85a7e8 100644 --- a/indra/newview/llpreviewscript.cpp +++ b/indra/newview/llpreviewscript.cpp @@ -1693,8 +1693,11 @@ void LLPreviewLSL::saveIfNeeded(bool sync /*= true*/) { std::string buffer(mScriptEd->mEditor->getText()); + LLUUID old_asset_id = inv_item->getAssetUUID().isNull() ? mScriptEd->getAssetID() : inv_item->getAssetUUID(); + LLResourceUploadInfo::ptr_t uploadInfo(std::make_shared(mItemUUID, buffer, - [](LLUUID itemId, LLUUID, LLUUID, LLSD response) { + [old_asset_id](LLUUID itemId, LLUUID, LLUUID, LLSD response) { + LLFileSystem::removeFile(old_asset_id, LLAssetType::AT_LSL_TEXT); LLPreviewLSL::finishedLSLUpload(itemId, response); })); @@ -1742,6 +1745,7 @@ void LLPreviewLSL::onLoadComplete(const LLUUID& asset_uuid, LLAssetType::EType t } preview->mScriptEd->setScriptName(script_name); preview->mScriptEd->setEnableEditing(is_modifiable); + preview->mScriptEd->setAssetID(asset_uuid); preview->mAssetStatus = PREVIEW_ASSET_LOADED; } else @@ -1995,6 +1999,7 @@ void LLLiveLSLEditor::onLoadComplete(const LLUUID& asset_id, instance->loadScriptText(asset_id, type); instance->mScriptEd->setEnableEditing(TRUE); instance->mAssetStatus = PREVIEW_ASSET_LOADED; + instance->mScriptEd->setAssetID(asset_id); } else { @@ -2174,6 +2179,7 @@ void LLLiveLSLEditor::finishLSLUpload(LLUUID itemId, LLUUID taskId, LLUUID newAs if (preview) { preview->mItem->setAssetUUID(newAssetId); + preview->mScriptEd->setAssetID(newAssetId); // Bytecode save completed if (response["compiled"]) @@ -2244,12 +2250,14 @@ void LLLiveLSLEditor::saveIfNeeded(bool sync /*= true*/) if (!url.empty()) { std::string buffer(mScriptEd->mEditor->getText()); + LLUUID old_asset_id = mScriptEd->getAssetID(); LLResourceUploadInfo::ptr_t uploadInfo(std::make_shared(mObjectUUID, mItemUUID, monoChecked() ? LLScriptAssetUpload::MONO : LLScriptAssetUpload::LSL2, isRunning, mScriptEd->getAssociatedExperience(), buffer, - [isRunning](LLUUID itemId, LLUUID taskId, LLUUID newAssetId, LLSD response) { - LLLiveLSLEditor::finishLSLUpload(itemId, taskId, newAssetId, response, isRunning); + [isRunning, old_asset_id](LLUUID itemId, LLUUID taskId, LLUUID newAssetId, LLSD response) { + LLFileSystem::removeFile(old_asset_id, LLAssetType::AT_LSL_TEXT); + LLLiveLSLEditor::finishLSLUpload(itemId, taskId, newAssetId, response, isRunning); })); LLViewerAssetUpload::EnqueueInventoryUpload(url, uploadInfo); diff --git a/indra/newview/llpreviewscript.h b/indra/newview/llpreviewscript.h index 40ab3a3dbb..4e192ecd04 100644 --- a/indra/newview/llpreviewscript.h +++ b/indra/newview/llpreviewscript.h @@ -142,6 +142,9 @@ public: void setItemRemoved(bool script_removed){mScriptRemoved = script_removed;}; + void setAssetID( const LLUUID& asset_id){ mAssetID = asset_id; }; + LLUUID getAssetID() { return mAssetID; } + private: void onBtnHelp(); void onBtnDynamicHelp(); @@ -188,6 +191,7 @@ private: LLUUID mAssociatedExperience; BOOL mScriptRemoved; BOOL mSaveDialogShown; + LLUUID mAssetID; LLScriptEdContainer* mContainer; // parent view From 391ada1150a861e899dcde8558e9efd4c5efc981 Mon Sep 17 00:00:00 2001 From: Callum Prentice Date: Tue, 27 Oct 2020 13:53:17 -0700 Subject: [PATCH 25/85] Fix for meta issue: SL-14211 Determine optimum cache size for VFS replacement cache --- indra/llfilesystem/lldiskcache.h | 4 ++-- indra/newview/app_settings/settings.xml | 8 ++++---- indra/newview/llappviewer.cpp | 10 ++++++++-- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/indra/llfilesystem/lldiskcache.h b/indra/llfilesystem/lldiskcache.h index b25eac8538..997884da31 100644 --- a/indra/llfilesystem/lldiskcache.h +++ b/indra/llfilesystem/lldiskcache.h @@ -83,8 +83,8 @@ class LLDiskCache : */ const std::string cache_dir, /** - * The maximum size of the cache in bytes - Defined by - * the setting at 'DiskCacheMaxSizeMB' (* 1024 * 1024) + * The maximum size of the cache in bytes - Based on the + * setting at 'CacheSize' and 'DiskCachePercentOfTotal' */ const int max_size_bytes, /** diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index fc1437148a..142a3098ec 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -1362,16 +1362,16 @@ Value 0 - DiskCacheMaxSizeMB + DiskCachePercentOfTotal Comment - The maximum number of MB to use for the new disk cache + The percent of total cache size (defined by CacheSize) to use for the disk cache Persist 1 Type - U32 + F32 Value - 50 + 20.0 DiskCacheDirName diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 9e50860064..20ca432279 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -4063,10 +4063,16 @@ bool LLAppViewer::initCache() // initialize the new disk cache using saved settings const std::string cache_dir_name = gSavedSettings.getString("DiskCacheDirName"); - const unsigned int cache_max_bytes = gSavedSettings.getU32("DiskCacheMaxSizeMB") * 1024 * 1024; + + // note that the maximum size of this cache is defined as a percentage of the + // total cache size - the 'CacheSize' pref - for all caches. + const unsigned int cache_total_size_mb = gSavedSettings.getU32("CacheSize"); + const double disk_cache_percent = gSavedSettings.getF32("DiskCachePercentOfTotal"); + const unsigned int disk_cache_mb = cache_total_size_mb * disk_cache_percent / 100; + const unsigned int disk_cache_bytes = disk_cache_mb * 1024 * 1024; const bool enable_cache_debug_info = gSavedSettings.getBOOL("EnableDiskCacheDebugInfo"); const std::string cache_dir = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, cache_dir_name); - LLDiskCache::initParamSingleton(cache_dir, cache_max_bytes, enable_cache_debug_info); + LLDiskCache::initParamSingleton(cache_dir, disk_cache_bytes, enable_cache_debug_info); bool texture_cache_mismatch = false; if (gSavedSettings.getS32("LocalCacheVersion") != LLAppViewer::getTextureCacheVersion()) From aae7259a0adaddb4b2d9a2a62a0d4ff95fe5e2b3 Mon Sep 17 00:00:00 2001 From: Callum Prentice Date: Tue, 27 Oct 2020 14:02:24 -0700 Subject: [PATCH 26/85] Fix for meta issue: SL-14210 Prune descriptive tag from new cache filenames --- indra/llfilesystem/lldiskcache.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/indra/llfilesystem/lldiskcache.cpp b/indra/llfilesystem/lldiskcache.cpp index efe5e7092c..91fc1b15d1 100644 --- a/indra/llfilesystem/lldiskcache.cpp +++ b/indra/llfilesystem/lldiskcache.cpp @@ -187,8 +187,12 @@ const std::string LLDiskCache::metaDataToFilepath(const std::string id, file_path << id; file_path << "_"; file_path << (extra_info.empty() ? "0" : extra_info); - file_path << "_"; - file_path << assetTypeToString(at); + //file_path << "_"; + //file_path << assetTypeToString(at); // see SL-14210 Prune descriptive tag from new cache filenames + // for details of why it was removed. Note that if you put it + // back or change the format of the filename, the cache files + // files will be invalidated (and perhaps, more importantly, + // never deleted unless you delete them manually). file_path << ".asset"; return file_path.str(); From f39b334dee1106a6c34553459412d04c0ee2fda3 Mon Sep 17 00:00:00 2001 From: Callum Prentice Date: Wed, 28 Oct 2020 09:39:57 -0700 Subject: [PATCH 27/85] Remove reference to SQLite 3P package since we are no longer using it --- autobuild.xml | 56 --------------------------------------------------- 1 file changed, 56 deletions(-) diff --git a/autobuild.xml b/autobuild.xml index 6abb089455..eacf11fb0f 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -3065,62 +3065,6 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors version 4.10.0000.32327.5fc3fe7c.539691 - sqlite - - canonical_repo - https://bitbucket.org/lindenlab/3p-sqlite - copyright - Copyright (C) 1995-2013 Jean-loup Gailly and Mark Adler - description - SQLite is a C-language library that implements a small, fast, self-contained, high-reliability, full-featured, SQL database engine. - license - Public Domain - license_file - LICENSES/sqlite_copyright.html - name - sqlite - platforms - - darwin64 - - archive - - hash - 31cb0e0b1557660691766441ba966f10 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/69032/665217/sqlite-3.33.0-darwin64-549465.tar.bz2 - - name - darwin64 - - windows - - archive - - hash - 4102b91b473812ba4619ed3bfefb7de9 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/69040/665286/sqlite-3.33.0-windows-549465.tar.bz2 - - name - windows - - windows64 - - archive - - hash - 0e9a0ae93d749dc8eeadf2edb293b291 - url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/69039/665277/sqlite-3.33.0-windows64-549465.tar.bz2 - - name - windows64 - - - version - 3.33.0 - tut copyright From 727b89e94cc5125c62b0eaebe304ba613f38225a Mon Sep 17 00:00:00 2001 From: Callum Prentice Date: Thu, 29 Oct 2020 12:21:11 -0700 Subject: [PATCH 28/85] fix for SL-14227 The 'Fatal error detected' message is displayed while downloading a new version of the SL Viewer --- indra/newview/app_settings/settings.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 142a3098ec..f7f54624d4 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -1371,7 +1371,7 @@ Type F32 Value - 20.0 + 20.0 DiskCacheDirName @@ -11569,7 +11569,7 @@ Boolean Value 0 - + NearbyListShowMap Comment @@ -16588,7 +16588,7 @@ Type Boolean Value - 1 + 1 From 27117cd8e4a94542994764ac23d555b919d270b8 Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Thu, 12 Nov 2020 20:44:17 +0200 Subject: [PATCH 29/85] SL-14269 FIXED Textures are corrupt after reducing the cache size. --- indra/newview/lltexturecache.cpp | 46 +++++++++++++------------------- 1 file changed, 18 insertions(+), 28 deletions(-) diff --git a/indra/newview/lltexturecache.cpp b/indra/newview/lltexturecache.cpp index 6211d0ce3b..963e942375 100644 --- a/indra/newview/lltexturecache.cpp +++ b/indra/newview/lltexturecache.cpp @@ -54,6 +54,7 @@ const F32 TEXTURE_CACHE_LRU_SIZE = .10f; // % amount for LRU list (low overhead const S32 TEXTURE_FAST_CACHE_ENTRY_OVERHEAD = sizeof(S32) * 4; //w, h, c, level const S32 TEXTURE_FAST_CACHE_ENTRY_SIZE = 16 * 16 * 4 + TEXTURE_FAST_CACHE_ENTRY_OVERHEAD; const F32 TEXTURE_LAZY_PURGE_TIME_LIMIT = .004f; // 4ms. Would be better to autoadjust, but there is a major cache rework in progress. +const F32 TEXTURE_PRUNING_MAX_TIME = 15.f; class LLTextureCacheWorker : public LLWorkerClass { @@ -1550,7 +1551,6 @@ void LLTextureCache::readHeaderCache() if (num_entries - empty_entries > sCacheMaxEntries) { // Special case: cache size was reduced, need to remove entries - // Note: After we prune entries, we will call this again and create the LRU U32 entries_to_purge = (num_entries - empty_entries) - sCacheMaxEntries; LL_INFOS() << "Texture Cache Entries: " << num_entries << " Max: " << sCacheMaxEntries << " Empty: " << empty_entries << " Purging: " << entries_to_purge << LL_ENDL; // We can exit the following loop with the given condition, since if we'd reach the end of the lru set we'd have: @@ -1563,7 +1563,7 @@ void LLTextureCache::readHeaderCache() ++iter; } } - else + { S32 lru_entries = (S32)((F32)sCacheMaxEntries * TEXTURE_CACHE_LRU_SIZE); for (std::set::iterator iter = lru.begin(); iter != lru.end(); ++iter) @@ -1577,30 +1577,19 @@ void LLTextureCache::readHeaderCache() if (purge_list.size() > 0) { + LLTimer timer; for (std::set::iterator iter = purge_list.begin(); iter != purge_list.end(); ++iter) { std::string tex_filename = getTextureFileName(entries[*iter].mID); removeEntry((S32)*iter, entries[*iter], tex_filename); - } - // If we removed any entries, we need to rebuild the entries list, - // write the header, and call this again - std::vector new_entries; - for (U32 i=0; i 0) + + //make sure that pruning entries doesn't take too much time + if (timer.getElapsedTimeF32() > TEXTURE_PRUNING_MAX_TIME) { - new_entries.push_back(entry); + break; } } - mFreeList.clear(); // recreating list, no longer valid. - llassert_always(new_entries.size() <= sCacheMaxEntries); - mHeaderEntriesInfo.mEntries = new_entries.size(); - writeEntriesHeader(); - writeEntriesAndClose(new_entries); - mHeaderMutex.unlock(); // unlock the mutex before calling again - readHeaderCache(); // repeat with new entries file - mHeaderMutex.lock(); + writeEntriesAndClose(entries); } else { @@ -1723,7 +1712,7 @@ void LLTextureCache::purgeTexturesLazy(F32 time_limit_sec) } S64 cache_size = mTexturesSizeTotal; - S64 purged_cache_size = (sCacheMaxTexturesSize * (S64)((1.f - TEXTURE_CACHE_PURGE_AMOUNT) * 100)) / 100; + S64 purged_cache_size = (llmax(cache_size, sCacheMaxTexturesSize) * (S64)((1.f - TEXTURE_CACHE_PURGE_AMOUNT) * 100)) / 100; for (time_idx_set_t::iterator iter = time_idx_set.begin(); iter != time_idx_set.end(); ++iter) { @@ -1819,21 +1808,26 @@ void LLTextureCache::purgeTextures(bool validate) } S64 cache_size = mTexturesSizeTotal; - S64 purged_cache_size = (sCacheMaxTexturesSize * (S64)((1.f-TEXTURE_CACHE_PURGE_AMOUNT)*100)) / 100; + S64 purged_cache_size = (llmax(cache_size, sCacheMaxTexturesSize) * (S64)((1.f - TEXTURE_CACHE_PURGE_AMOUNT) * 100)) / 100; S32 purge_count = 0; for (time_idx_set_t::iterator iter = time_idx_set.begin(); iter != time_idx_set.end(); ++iter) { S32 idx = iter->second; bool purge_entry = false; - if (validate) + + if (cache_size >= purged_cache_size) + { + purge_entry = true; + } + else if (validate) { // make sure file exists and is the correct size U32 uuididx = entries[idx].mID.mData[0]; if (uuididx == validate_idx) { - std::string filename = getTextureFileName(entries[idx].mID); - LL_DEBUGS("TextureCache") << "Validating: " << filename << "Size: " << entries[idx].mBodySize << LL_ENDL; + std::string filename = getTextureFileName(entries[idx].mID); + LL_DEBUGS("TextureCache") << "Validating: " << filename << "Size: " << entries[idx].mBodySize << LL_ENDL; // mHeaderAPRFilePoolp because this is under header mutex in main thread S32 bodysize = LLAPRFile::size(filename, mHeaderAPRFilePoolp); if (bodysize != entries[idx].mBodySize) @@ -1843,10 +1837,6 @@ void LLTextureCache::purgeTextures(bool validate) } } } - else if (cache_size >= purged_cache_size) - { - purge_entry = true; - } else { break; From 53cae8b21f0f77fbb1be22c64deee9b6a3f237f7 Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Fri, 11 Dec 2020 16:42:10 +0200 Subject: [PATCH 30/85] SL-14505 FIXED [Win10] The viewer isn't started on the non-English system locale --- indra/llfilesystem/lldiskcache.cpp | 34 +++++++++++++++++++++++------ indra/llfilesystem/llfilesystem.cpp | 16 ++++++-------- 2 files changed, 34 insertions(+), 16 deletions(-) diff --git a/indra/llfilesystem/lldiskcache.cpp b/indra/llfilesystem/lldiskcache.cpp index 91fc1b15d1..34ff80b250 100644 --- a/indra/llfilesystem/lldiskcache.cpp +++ b/indra/llfilesystem/lldiskcache.cpp @@ -48,7 +48,7 @@ LLDiskCache::LLDiskCache(const std::string cache_dir, { mCacheFilenamePrefix = "sl_cache"; - boost::filesystem::create_directory(cache_dir); + LLFile::mkdir(cache_dir); } void LLDiskCache::purge() @@ -63,9 +63,14 @@ void LLDiskCache::purge() typedef std::pair> file_info_t; std::vector file_info; - if (boost::filesystem::is_directory(mCacheDir)) +#if LL_WINDOWS + std::wstring cache_path(utf8str_to_utf16str(mCacheDir)); +#else + std::string cache_path(mCacheDir); +#endif + if (boost::filesystem::is_directory(cache_path)) { - for (auto& entry : boost::make_iterator_range(boost::filesystem::directory_iterator(mCacheDir), {})) + for (auto& entry : boost::make_iterator_range(boost::filesystem::directory_iterator(cache_path), {})) { if (boost::filesystem::is_regular_file(entry)) { @@ -201,7 +206,12 @@ const std::string LLDiskCache::metaDataToFilepath(const std::string id, void LLDiskCache::updateFileAccessTime(const std::string file_path) { const std::time_t file_time = std::time(nullptr); + +#if LL_WINDOWS + boost::filesystem::last_write_time(utf8str_to_utf16str(file_path), file_time); +#else boost::filesystem::last_write_time(file_path, file_time); +#endif } const std::string LLDiskCache::getCacheInfo() @@ -227,9 +237,14 @@ void LLDiskCache::clearCache() * the component files but it's called infrequently so it's * likely just fine */ - if (boost::filesystem::is_directory(mCacheDir)) +#if LL_WINDOWS + std::wstring cache_path(utf8str_to_utf16str(mCacheDir)); +#else + std::string cache_path(mCacheDir); +#endif + if (boost::filesystem::is_directory(cache_path)) { - for (auto& entry : boost::make_iterator_range(boost::filesystem::directory_iterator(mCacheDir), {})) + for (auto& entry : boost::make_iterator_range(boost::filesystem::directory_iterator(cache_path), {})) { if (boost::filesystem::is_regular_file(entry)) { @@ -255,9 +270,14 @@ uintmax_t LLDiskCache::dirFileSize(const std::string dir) * so if performance is ever an issue, optimizing this or removing it altogether, * is an easy win. */ - if (boost::filesystem::is_directory(dir)) +#if LL_WINDOWS + std::wstring dir_path(utf8str_to_utf16str(dir)); +#else + std::string dir_path(dir); +#endif + if (boost::filesystem::is_directory(dir_path)) { - for (auto& entry : boost::make_iterator_range(boost::filesystem::directory_iterator(dir), {})) + for (auto& entry : boost::make_iterator_range(boost::filesystem::directory_iterator(dir_path), {})) { if (boost::filesystem::is_regular_file(entry)) { diff --git a/indra/llfilesystem/llfilesystem.cpp b/indra/llfilesystem/llfilesystem.cpp index 932ef2a9c6..64e0b9f193 100644 --- a/indra/llfilesystem/llfilesystem.cpp +++ b/indra/llfilesystem/llfilesystem.cpp @@ -34,8 +34,6 @@ #include "llfasttimer.h" #include "lldiskcache.h" -#include - const S32 LLFileSystem::READ = 0x00000001; const S32 LLFileSystem::WRITE = 0x00000002; const S32 LLFileSystem::READ_WRITE = 0x00000003; // LLFileSystem::READ & LLFileSystem::WRITE @@ -64,7 +62,7 @@ bool LLFileSystem::getExists(const LLUUID& file_id, const LLAssetType::EType fil const std::string extra_info = ""; const std::string filename = LLDiskCache::getInstance()->metaDataToFilepath(id_str, file_type, extra_info); - std::ifstream file(filename, std::ios::binary); + llifstream file(filename, std::ios::binary); if (file.is_open()) { file.seekg(0, std::ios::end); @@ -81,7 +79,7 @@ bool LLFileSystem::removeFile(const LLUUID& file_id, const LLAssetType::EType fi const std::string extra_info = ""; const std::string filename = LLDiskCache::getInstance()->metaDataToFilepath(id_str, file_type, extra_info); - std::remove(filename.c_str()); + LLFile::remove(filename.c_str()); return true; } @@ -102,7 +100,7 @@ bool LLFileSystem::renameFile(const LLUUID& old_file_id, const LLAssetType::ETyp // Rename needs the new file to not exist. LLFileSystem::removeFile(new_file_id, new_file_type); - if (std::rename(old_filename.c_str(), new_filename.c_str())) + if (LLFile::rename(old_filename, new_filename) != 0) { // We would like to return FALSE here indicating the operation // failed but the original code does not and doing so seems to @@ -123,7 +121,7 @@ S32 LLFileSystem::getFileSize(const LLUUID& file_id, const LLAssetType::EType fi const std::string filename = LLDiskCache::getInstance()->metaDataToFilepath(id_str, file_type, extra_info); S32 file_size = 0; - std::ifstream file(filename, std::ios::binary); + llifstream file(filename, std::ios::binary); if (file.is_open()) { file.seekg(0, std::ios::end); @@ -142,7 +140,7 @@ BOOL LLFileSystem::read(U8* buffer, S32 bytes) const std::string extra_info = ""; const std::string filename = LLDiskCache::getInstance()->metaDataToFilepath(id, mFileType, extra_info); - std::ifstream file(filename, std::ios::binary); + llifstream file(filename, std::ios::binary); if (file.is_open()) { file.seekg(mPosition, std::ios::beg); @@ -197,7 +195,7 @@ BOOL LLFileSystem::write(const U8* buffer, S32 bytes) if (mMode == APPEND) { - std::ofstream ofs(filename, std::ios::app | std::ios::binary); + llofstream ofs(filename, std::ios::app | std::ios::binary); if (ofs) { ofs.write((const char*)buffer, bytes); @@ -207,7 +205,7 @@ BOOL LLFileSystem::write(const U8* buffer, S32 bytes) } else { - std::ofstream ofs(filename, std::ios::binary); + llofstream ofs(filename, std::ios::binary); if (ofs) { ofs.write((const char*)buffer, bytes); From 60e8f990fdbc5f00b69c1a7355330ff421133cea Mon Sep 17 00:00:00 2001 From: Callum Prentice Date: Fri, 8 Jan 2021 14:21:58 -0800 Subject: [PATCH 31/85] Addresses SL-14582: Add code to only write the file last access time occasionally --- indra/llfilesystem/lldiskcache.cpp | 40 +++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/indra/llfilesystem/lldiskcache.cpp b/indra/llfilesystem/lldiskcache.cpp index 34ff80b250..c9f7684b5a 100644 --- a/indra/llfilesystem/lldiskcache.cpp +++ b/indra/llfilesystem/lldiskcache.cpp @@ -205,12 +205,46 @@ const std::string LLDiskCache::metaDataToFilepath(const std::string id, void LLDiskCache::updateFileAccessTime(const std::string file_path) { - const std::time_t file_time = std::time(nullptr); + /** + * Threshold in time_t units that is used to decide if the last access time + * time of the file is updated or not. Added as a precaution for the concern + * outlined in SL-14582 about frequent writes on older SSDs reducing their + * lifespan. I think this is the right place for the threshold value - rather + * than it being a pref - do comment on that Jira if you disagree... + * + * Let's start with 1 hour in time_t units and see how that unfolds + */ + const std::time_t time_threshold = 1 * 60 * 60; + + // current time + const std::time_t cur_time = std::time(nullptr); #if LL_WINDOWS - boost::filesystem::last_write_time(utf8str_to_utf16str(file_path), file_time); + // file last write time + const std::time_t last_write_time = boost::filesystem::last_write_time(utf8str_to_utf16str(file_path)); + + // delta between cur time and last time the file was written + const std::time_t delta_time = cur_time - last_write_time; + + // we only write the new value if the time in time_threshold has elapsed + // before the last one + if (delta_time > time_threshold) + { + boost::filesystem::last_write_time(utf8str_to_utf16str(file_path), cur_time); + } #else - boost::filesystem::last_write_time(file_path, file_time); + // file last write time + const std::time_t last_write_time = boost::filesystem::last_write_time(file_path); + + // delta between cur time and last time the file was written + const std::time_t delta_time = cur_time - last_write_time; + + // we only write the new value if the time in time_threshold has elapsed + // before the last one + if (delta_time > time_threshold) + { + boost::filesystem::last_write_time(file_path, cur_time); + } #endif } From 7528855442a100cee379b9e409280a69caa78bba Mon Sep 17 00:00:00 2001 From: Callum Prentice Date: Mon, 25 Jan 2021 13:26:39 -0800 Subject: [PATCH 32/85] TeamCity not happy and only thing left to try is a dummy (whitespace) commit --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index e4078770f3..729c0ae368 100644 --- a/README.md +++ b/README.md @@ -13,3 +13,4 @@ To download the current default version, visit [the download page](https://secondlife.com/support/downloads). For even newer versions try [the Alternate Viewers page](https://wiki.secondlife.com/wiki/Linden_Lab_Official:Alternate_Viewers) + From f55bab800ac7b8fb7c319699d2dfdf6b3c48586b Mon Sep 17 00:00:00 2001 From: Ansariel Date: Fri, 19 Feb 2021 01:17:25 +0100 Subject: [PATCH 33/85] FIRE-30765: Fix more coproc pool creation threading crashes - yay! --- indra/llmessage/llcoproceduremanager.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/indra/llmessage/llcoproceduremanager.cpp b/indra/llmessage/llcoproceduremanager.cpp index 25c1c015f8..e5e0a7ab8a 100644 --- a/indra/llmessage/llcoproceduremanager.cpp +++ b/indra/llmessage/llcoproceduremanager.cpp @@ -196,6 +196,7 @@ void LLCoprocedureManager::setPropertyMethods(SettingQuery_t queryfn, SettingUpd // workaround until we get mutex into initializePool initializePool("VAssetStorage"); initializePool("Upload"); + initializePool("AIS"); initializePool("ExpCache"); // FIRE-30731: ExpCache coroutine pool crash } From ec97be1f667afff7cd5ee513ca5c36438d497ae6 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Fri, 19 Feb 2021 09:50:16 +0100 Subject: [PATCH 34/85] Don't try to create motions with null UUID --- indra/llcharacter/llmotioncontroller.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/indra/llcharacter/llmotioncontroller.cpp b/indra/llcharacter/llmotioncontroller.cpp index 43865d3ff0..93c5dac12a 100644 --- a/indra/llcharacter/llmotioncontroller.cpp +++ b/indra/llcharacter/llmotioncontroller.cpp @@ -347,6 +347,13 @@ void LLMotionController::removeMotionInstance(LLMotion* motionp) //----------------------------------------------------------------------------- LLMotion* LLMotionController::createMotion( const LLUUID &id ) { + // Don't try to create invalid motions + if (id.isNull()) + { + return NULL; + } + // + // do we have an instance of this motion for this character? LLMotion *motion = findMotion(id); From d07c89ee6de181210d3e3f85bf99f9d75ebf2e0d Mon Sep 17 00:00:00 2001 From: Ansariel Date: Fri, 19 Feb 2021 10:15:37 +0100 Subject: [PATCH 35/85] Squelch some coproc logspam --- indra/llmessage/llcoproceduremanager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/indra/llmessage/llcoproceduremanager.cpp b/indra/llmessage/llcoproceduremanager.cpp index e5e0a7ab8a..5775ab44e3 100644 --- a/indra/llmessage/llcoproceduremanager.cpp +++ b/indra/llmessage/llcoproceduremanager.cpp @@ -346,7 +346,7 @@ LLUUID LLCoprocedurePool::enqueueCoprocedure(const std::string &name, LLCoproced { LLUUID id(LLUUID::generateNewID()); - LL_INFOS("CoProcMgr") << "Coprocedure(" << name << ") enqueuing with id=" << id.asString() << " in pool \"" << mPoolName << "\" at " << mPending << LL_ENDL; + LL_DEBUGS("CoProcMgr") << "Coprocedure(" << name << ") enqueuing with id=" << id.asString() << " in pool \"" << mPoolName << "\" at " << mPending << LL_ENDL; auto pushed = mPendingCoprocs->try_push(boost::make_shared(name, id, proc)); if (pushed == boost::fibers::channel_op_status::success) { @@ -362,7 +362,7 @@ LLUUID LLCoprocedurePool::enqueueCoprocedure(const std::string &name, LLCoproced } // The queue should never fill up. - LL_ERRS("CoProcMgr") << "Enqueue into '" << name << "' failed (" << unsigned(pushed) << ")" << LL_ENDL; + LL_ERRS("CoProcMgr") << "Enqueue into '" << name << "' failed (" << unsigned(pushed) << "; pending: " << mPending <<")" << LL_ENDL; return {}; // never executed, pacify the compiler } From be546fc9a115867061659c9934067d54e6b940c4 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Fri, 19 Feb 2021 10:30:55 +0100 Subject: [PATCH 36/85] Fix some XUI parser warnings --- indra/llui/lltextbase.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp index 2eabf5eb54..d4b25fa637 100644 --- a/indra/llui/lltextbase.cpp +++ b/indra/llui/lltextbase.cpp @@ -2174,8 +2174,8 @@ void LLTextBase::createUrlContextMenu(S32 x, S32 y, const std::string &in_url) if (mIsFriendSignal) { bool isFriend = *(*mIsFriendSignal)(LLUUID(LLUrlAction::getUserID(url))); - LLView* addFriendButton = menu->getChild("add_friend"); - LLView* removeFriendButton = menu->getChild("remove_friend"); + LLView* addFriendButton = menu->findChild("add_friend"); + LLView* removeFriendButton = menu->findChild("remove_friend"); if (addFriendButton && removeFriendButton) { @@ -2187,8 +2187,8 @@ void LLTextBase::createUrlContextMenu(S32 x, S32 y, const std::string &in_url) if (mIsObjectBlockedSignal) { bool is_blocked = *(*mIsObjectBlockedSignal)(LLUUID(LLUrlAction::getObjectId(url)), LLUrlAction::getObjectName(url)); - LLView* blockButton = menu->getChild("block_object"); - LLView* unblockButton = menu->getChild("unblock_object"); + LLView* blockButton = menu->findChild("block_object"); + LLView* unblockButton = menu->findChild("unblock_object"); if (blockButton && unblockButton) { From b302a84758202f13c528409f74efaf5941d764ae Mon Sep 17 00:00:00 2001 From: PanteraPolnocy Date: Fri, 19 Feb 2021 16:45:25 +0100 Subject: [PATCH 37/85] FIRE-30767 Russian translation small update, by Romka Swallowtail --- indra/newview/skins/default/xui/ru/notifications.xml | 3 +++ .../newview/skins/default/xui/ru/panel_region_environment.xml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/indra/newview/skins/default/xui/ru/notifications.xml b/indra/newview/skins/default/xui/ru/notifications.xml index 04bab8a57b..be3eb75781 100644 --- a/indra/newview/skins/default/xui/ru/notifications.xml +++ b/indra/newview/skins/default/xui/ru/notifications.xml @@ -3642,6 +3642,9 @@ URL: [AUDIOURL] Вы отправили обновление своей внешности через [TIME] сек. [STATUS] + + Программа просмотра обнаружила, что вы можете выглядеть как облако, и пытается исправить это автоматически. + ( [EXISTENCE] сек. жизни ) Аватар "[NAME]" стал облаком. diff --git a/indra/newview/skins/default/xui/ru/panel_region_environment.xml b/indra/newview/skins/default/xui/ru/panel_region_environment.xml index 53b9053ebc..95cb7cea1f 100644 --- a/indra/newview/skins/default/xui/ru/panel_region_environment.xml +++ b/indra/newview/skins/default/xui/ru/panel_region_environment.xml @@ -49,7 +49,7 @@