You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

219 lines
8.8 KiB
C++

// Copyright 2008, Google Inc. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// 3. Neither the name of Google Inc. nor the names of its contributors may be
// used to endorse or promote products derived from this software without
// specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// This file contains the NetCache class template and NetFetcher base class.
#ifndef KML_BASE_NET_CACHE_H__
#define KML_BASE_NET_CACHE_H__
#include <map>
#include "kml/base/util.h"
#include "boost/intrusive_ptr.hpp"
namespace kmlbase {
// A CacheItem is derived from Referent and has a CreateFromString:
// class SomeCacheItem : public Referent {
// public:
// static SomeCacheItem* CreateFromString(const string& data);
// };
// This is the default NetFetcher. It represents the empty network which
// simply returns false for all URLs. This is provided for non-networked
// libkml usage and effectively stubs out network access. This is useful in
// the several places in KML where failed network fetch is quietly ignored.
// Application code should derive from NetFetcher and implement FetchUrl
// to perform (synchronous) network fetching as desired. All external I/O
// from within NetCache is called out to the application code in this manner.
class NetFetcher {
public:
virtual ~NetFetcher() {}
virtual bool FetchUrl(const string& url, string* data) const {
return false;
}
};
// This class template provides a generic memory cache facility parameterized
// by the CacheItem. Typical usages is as follows:
// Create a "CacheItem" class as described above (one that implements a
// CreateFromString with the signature given above):
// class MyCacheItem : public kmlbase::Referent {
// public:
// static MyCacheItem* CreateFromString(const string& data) {
// MyCacheItem* my_cache_item = new MyCacheItem;
// // whatever else your CacheItem does with an input data buffer
// return my_cache_item;
// }
// // other methods if you have them
// };
// typedef boost::intrusive_ptr<MyCacheItem> MyCacheItemPtr;
//
// Create a NetCache for that CacheItem:
// NetCache<MyCacheItem> net_cache_of_my_cache_items;
//
// Create a NetFetcher class by inheriting from NetFetcher as described above:
// class MyNetFetcher : kmlbase::NetFetcher {
// public:
// virtual bool FetchUrl(const string& url, string* data) const {
// // do however it is you want to fetch the url, save the content to data
// // Note that true means that this IS the data for this URL (not
// // a 404 page... _your_ code must detect higher level protocol issues).
// return true; // or false is a fetch on that url failed
// }
// // other methods if you have them
// };
//
// Your application code now fetches and creates CacheItems for a given URL
// by calling Fetch:
// MyCacheItemPtr a = net_cache_of_my_cache_items.Fetch(some-url);
// MyCacheItemPtr b = net_cache_of_my_cache_items.Fetch(some-other-url);
// When the NetCache goes out of scope all cached CacheItems are deleted,
// however use of boost::intrusive_ptr does permit any code to hold a pointer
// to an item originally from cache beyond the cache's lifetime.
// NOTE: This class is NOT thread safe!
template<class CacheItem>
class NetCache {
public:
typedef boost::intrusive_ptr<CacheItem> CacheItemPtr;
typedef std::pair<CacheItemPtr, uint64_t> CacheEntry;
typedef std::map<string, CacheEntry> CacheMap;
// Construct the NetCache with the given NetFetcher-derived class and
// with the given limit on number of items to cache. This size is entirely
// application specific, but it should noted that CacheItems _may_ hold
// file descriptors open so platform limits may limit max_size. Typical
// sizes are expected to be in the 10s to 100s of items.
NetCache(NetFetcher* net_fetcher, size_t max_size)
: max_size_(max_size),
cache_count_(0),
net_fetcher_(net_fetcher) {}
// This is the main public method in NetCache. If the NetFetcher FetchUrl
// returns true for this url the data fetched is passed to CreateFromString
// on the CacheItem to create a CacheItem from this data. This CacheItem
// is saved to the cache. If the cache has reached its limit as set in
// the constructor the oldest entry is discarded from the cache. If the
// CacheItem for this URL is in the cache it is simply returned.
CacheItemPtr Fetch(const string& url) {
// If an item is cached for this URL return it and we're done.
if (CacheItemPtr item = LookUp(url)) {
return item;
}
// Not found in cache: go fetch.
string data;
// NetFetcher knows only about "get me the data at this URL".
if (!net_fetcher_->FetchUrl(url, &data)) {
return NULL; // Fetch failed, no such URL.
}
// Fetch succeeded: create a CacheItem from the data.
CacheItemPtr item = CacheItem::CreateFromString(data);
if (!Save(url, item)) { // This is basically an internal error.
return NULL;
}
return item;
}
// This returns the CacheItem in the cache for the given url if it exists.
// If nothing is cached for this url then NULL is returned.
// In typical usage this method is not used by application code, but it is
// well behaved as described.
const CacheItemPtr LookUp(const string& url) const {
typename CacheMap::const_iterator iter = cache_map_.find(url);
if (iter == cache_map_.end()) {
return NULL;
}
// iter->first is key, second is val and val is KmzCacheEntry pair whose
// first is KmlFilePtr (second is creation time of cache entry).
return iter->second.first;
}
// This stores the given CacheItem to the cache for the given url.
// This failes of a CacheItem for this url exists (use Delete first).
// If the cache is at capacity this also first forces the removall
// of the oldest item in the cache. Application code should not typically
// use this directly: use Fetch().
bool Save(const string& url, const CacheItemPtr& cache_item) {
const CacheItemPtr exists = LookUp(url);
if (exists) {
return false;
}
if (cache_map_.size() == max_size_) {
RemoveOldest();
}
// It is not expected cache_count_ ever roll over. See net_cache_test.cc
// for some timing tests and results.
CacheEntry cache_entry = std::make_pair(cache_item, cache_count_++);
cache_map_[url] = cache_entry;
return true;
}
// If a CacheItem exists for this url it is deleted and true is returned.
// If no CacheItem exists for this url false is returned. Application code
// should generally have no need to use this directly.
bool Delete(const string& url) {
const CacheItemPtr cache_item = LookUp(url);
if (cache_item) {
cache_map_.erase(url);
return true;
}
return false;
}
// This removes the oldest entry in the cache. Application code should
// generally not need to use this directly.
bool RemoveOldest() {
if (cache_map_.empty()) {
return false;
}
// Find the entry with the smallest time.
typename CacheMap::iterator iter = cache_map_.begin();
typename CacheMap::iterator oldest = iter;
for (;iter != cache_map_.end(); ++iter) {
// STL map iter is a pair<key,val> with val CacheItem which is a pair
// whose second is the timestamp.
if (iter->second.second < oldest->second.second) {
oldest = iter;
}
}
cache_map_.erase(oldest);
return true;
}
// This returns the number of items presently in the cache.
size_t Size() const {
return cache_map_.size();
}
private:
const size_t max_size_;
CacheMap cache_map_;
uint64_t cache_count_;
const NetFetcher* net_fetcher_;
};
} // end namespace kmlbase
#endif // KML_BASE_NET_CACHE_H__