⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 diskcache.cc

📁 本人收集整理的一份c/c++跨平台网络库
💻 CC
字号:
#include <time.h>#ifdef WIN32#include "win32.h"#endif#include "basicdefs.h"#include "common.h"#include "diskcache.h"#include "fileutils.h"#include "pathutils.h"#include "stream.h"#include "stringencode.h"#include "stringutils.h"#ifdef _DEBUG#define TRANSPARENT_CACHE_NAMES 1#else  // !_DEBUG#define TRANSPARENT_CACHE_NAMES 0#endif  // !_DEBUGnamespace utils_base {class DiskCache;///////////////////////////////////////////////////////////////////////////////// DiskCacheAdapter///////////////////////////////////////////////////////////////////////////////class DiskCacheAdapter : public StreamAdapterInterface {public:  DiskCacheAdapter(const DiskCache* cache, const std::string& id, size_t index,                   StreamInterface* stream)  : StreamAdapterInterface(stream), cache_(cache), id_(id), index_(index)  { }  virtual ~DiskCacheAdapter() {    Close();    cache_->ReleaseResource(id_, index_);  }private:  const DiskCache* cache_;  std::string id_;  size_t index_;};///////////////////////////////////////////////////////////////////////////////// DiskCache///////////////////////////////////////////////////////////////////////////////DiskCache::DiskCache() : max_cache_(0), total_size_(0), total_accessors_(0) {}DiskCache::~DiskCache() {  ASSERT(0 == total_accessors_);}bool DiskCache::Initialize(const std::string& folder, size_t size) {  if (!folder_.empty() || !Filesystem::CreateFolder(folder))    return false;  folder_ = folder;  max_cache_ = size;  ASSERT(0 == total_size_);  if (!InitializeEntries())    return false;  return CheckLimit();}bool DiskCache::Purge() {  if (folder_.empty())    return false;  if (total_accessors_ > 0) {    LOG_F(LS_WARNING) << "Cache files open";    return false;  }  if (!PurgeFiles())    return false;  map_.clear();  return true;}bool DiskCache::LockResource(const std::string& id) {  Entry* entry = GetOrCreateEntry(id, true);  if (LS_LOCKED == entry->lock_state)    return false;  if ((LS_UNLOCKED == entry->lock_state) && (entry->accessors > 0))    return false;  if ((total_size_ > max_cache_) && !CheckLimit()) {    LOG_F(LS_WARNING) << "Cache overfull";    return false;  }  entry->lock_state = LS_LOCKED;  return true;}StreamInterface* DiskCache::WriteResource(const std::string& id, size_t index) {  Entry* entry = GetOrCreateEntry(id, false);  if (LS_LOCKED != entry->lock_state)    return NULL;  size_t previous_size = 0;  std::string filename(IdToFilename(id, index));  FileStream::GetSize(filename, &previous_size);  ASSERT(previous_size <= entry->size);  if (previous_size > entry->size) {    previous_size = entry->size;  }  scoped_ptr<FileStream> file(new FileStream);  if (!file->Open(filename, "wb")) {    LOG_F(LS_ERROR) << "Couldn't create cache file";    return NULL;  }  entry->streams = stdmax(entry->streams, index + 1);  entry->size -= previous_size;  total_size_ -= previous_size;  entry->accessors += 1;  total_accessors_ += 1;  return new DiskCacheAdapter(this, id, index, file.release());}bool DiskCache::UnlockResource(const std::string& id) {  Entry* entry = GetOrCreateEntry(id, false);  if (LS_LOCKED != entry->lock_state)    return false;  if (entry->accessors > 0) {    entry->lock_state = LS_UNLOCKING;  } else {    entry->lock_state = LS_UNLOCKED;    entry->last_modified = time(0);    CheckLimit();  }  return true;}StreamInterface* DiskCache::ReadResource(const std::string& id,                                         size_t index) const {  const Entry* entry = GetEntry(id);  if (LS_UNLOCKED != entry->lock_state)    return NULL;  if (index >= entry->streams)    return NULL;  scoped_ptr<FileStream> file(new FileStream);  if (!file->Open(IdToFilename(id, index), "rb"))    return NULL;  entry->accessors += 1;  total_accessors_ += 1;  return new DiskCacheAdapter(this, id, index, file.release());}bool DiskCache::HasResource(const std::string& id) const {  const Entry* entry = GetEntry(id);  return (NULL != entry) && (entry->streams > 0);}bool DiskCache::HasResourceStream(const std::string& id, size_t index) const {  const Entry* entry = GetEntry(id);  if ((NULL == entry) || (index >= entry->streams))    return false;  std::string filename = IdToFilename(id, index);  return FileExists(filename);}bool DiskCache::DeleteResource(const std::string& id) {  Entry* entry = GetOrCreateEntry(id, false);  if (!entry)    return true;  if ((LS_UNLOCKED != entry->lock_state) || (entry->accessors > 0))    return false;  bool success = true;  for (size_t index = 0; index < entry->streams; ++index) {    std::string filename = IdToFilename(id, index);    if (!FileExists(filename))      continue;    if (!DeleteFile(filename)) {      LOG_F(LS_ERROR) << "Couldn't remove cache file: " << filename;      success = false;    }  }  total_size_ -= entry->size;  map_.erase(id);  return success;}bool DiskCache::CheckLimit() {#ifdef _DEBUG  // Temporary check to make sure everything is working correctly.  size_t cache_size = 0;  for (EntryMap::iterator it = map_.begin(); it != map_.end(); ++it) {    cache_size += it->second.size;  }  ASSERT(cache_size == total_size_);#endif  // _DEBUG  // TODO: Replace this with a non-brain-dead algorithm for clearing out the  // oldest resources... something that isn't O(n^2)  while (total_size_ > max_cache_) {    EntryMap::iterator oldest = map_.end();    for (EntryMap::iterator it = map_.begin(); it != map_.end(); ++it) {      if ((LS_UNLOCKED != it->second.lock_state) || (it->second.accessors > 0))        continue;      oldest = it;      break;    }    if (oldest == map_.end()) {      LOG_F(LS_WARNING) << "All resources are locked!";      return false;    }    for (EntryMap::iterator it = oldest++; it != map_.end(); ++it) {      if (it->second.last_modified < oldest->second.last_modified) {        oldest = it;      }    }    if (!DeleteResource(oldest->first)) {      LOG_F(LS_ERROR) << "Couldn't delete from cache!";      return false;    }  }  return true;}std::string DiskCache::IdToFilename(const std::string& id, size_t index) const {#ifdef TRANSPARENT_CACHE_NAMES  // This escapes colons and other filesystem characters, so the user can't open  // special devices (like "COM1:"), or access other directories.  size_t buffer_size = id.length()*3 + 1;  char* buffer = new char[buffer_size];  encode(buffer, buffer_size, id.data(), id.length(),         unsafe_filename_characters(), '%');  // TODO: ASSERT(strlen(buffer) < FileSystem::MaxBasenameLength());#else  // !TRANSPARENT_CACHE_NAMES  // We might want to just use a hash of the filename at some point, both for  // obfuscation, and to avoid both filename length and escaping issues.  ASSERT(false);#endif  // !TRANSPARENT_CACHE_NAMES  char extension[32];  sprintfn(extension, ARRAY_SIZE(extension), ".%u", index);  Pathname pathname;  pathname.SetFolder(folder_);  pathname.SetBasename(buffer);  pathname.SetExtension(extension);#ifdef TRANSPARENT_CACHE_NAMES  delete [] buffer;#endif  // TRANSPARENT_CACHE_NAMES  return pathname.pathname();}bool DiskCache::FilenameToId(const std::string& filename, std::string* id,                             size_t* index) const {  Pathname pathname(filename);  if (1 != sscanf(pathname.extension().c_str(), ".%u", index))    return false;  size_t buffer_size = pathname.basename().length() + 1;  char* buffer = new char[buffer_size];  decode(buffer, buffer_size, pathname.basename().data(),         pathname.basename().length(), '%');  id->assign(buffer);  delete [] buffer;  return true;}DiskCache::Entry* DiskCache::GetOrCreateEntry(const std::string& id,                                              bool create) {  EntryMap::iterator it = map_.find(id);  if (it != map_.end())    return &it->second;  if (!create)    return NULL;  Entry e;  e.lock_state = LS_UNLOCKED;  e.accessors = 0;  e.size = 0;  e.streams = 0;  e.last_modified = time(0);  it = map_.insert(EntryMap::value_type(id, e)).first;  return &it->second;}void DiskCache::ReleaseResource(const std::string& id, size_t index) const {  const Entry* entry = GetEntry(id);  if (!entry) {    LOG_F(LS_WARNING) << "Missing cache entry";    ASSERT(false);    return;  }  entry->accessors -= 1;  total_accessors_ -= 1;  if (LS_UNLOCKED != entry->lock_state) {    // This is safe, because locked resources only issue WriteResource, which    // is non-const.  Think about a better way to handle it.    DiskCache* this2 = const_cast<DiskCache*>(this);    Entry* entry2 = this2->GetOrCreateEntry(id, false);    size_t new_size = 0;    std::string filename(IdToFilename(id, index));    FileStream::GetSize(filename, &new_size);    entry2->size += new_size;    this2->total_size_ += new_size;    if ((LS_UNLOCKING == entry->lock_state) && (0 == entry->accessors)) {      entry2->last_modified = time(0);      entry2->lock_state = LS_UNLOCKED;      this2->CheckLimit();    }  }}///////////////////////////////////////////////////////////////////////////////} // namespace talk_base

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -