phoenix-firestorm/indra/llfilesystem/llfilesystem.cpp

388 lines
9.2 KiB
C++

/**
* @file lldiskcache.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 "lldiskcache.h"
#include "llerror.h"
#include "llthread.h"
#include "lltimer.h"
#include "llfasttimer.h"
#include "llmemory.h"
#include <fstream>
#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
static LLTrace::BlockTimerStatHandle FTM_VFILE_WAIT("VFile Wait");
LLDiskCache::LLDiskCache(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;
}
LLDiskCache::~LLDiskCache()
{
}
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<LLAssetType::EType, std::string> 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 LLDiskCache::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 LLDiskCache::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 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;
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 LLDiskCache::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 LLDiskCache::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 LLDiskCache::isReadComplete()
{
if (mReadComplete)
{
return TRUE;
}
return FALSE;
}
S32 LLDiskCache::getLastBytesRead()
{
return mBytesRead;
}
BOOL LLDiskCache::eof()
{
return mPosition >= getSize();
}
BOOL LLDiskCache::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 LLDiskCache::writeFile(const U8 *buffer, S32 bytes, const LLUUID &uuid, LLAssetType::EType type)
{
LLDiskCache file(uuid, type, LLDiskCache::WRITE);
file.setMaxSize(bytes);
return file.write(buffer, bytes);
}
BOOL LLDiskCache::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 LLDiskCache::tell() const
{
return mPosition;
}
S32 LLDiskCache::getSize()
{
return LLDiskCache::getFileSize(mFileID, mFileType);
}
S32 LLDiskCache::getMaxSize()
{
// offer up a huge size since we don't care what the max is
return INT_MAX;
}
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 LLDiskCache::rename(const LLUUID &new_id, const LLAssetType::EType new_type)
{
LLDiskCache::renameFile(mFileID, mFileType, new_id, new_type);
mFileID = new_id;
mFileType = new_type;
return TRUE;
}
BOOL LLDiskCache::remove()
{
LLDiskCache::removeFile(mFileID, mFileType);
return TRUE;
}
// static
void LLDiskCache::initClass()
{
}
// static
void LLDiskCache::cleanupClass()
{
}
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 LLDiskCache::waitForLock()
{
// TODO: remove this function and calling sites?
}