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
219 lines
8.8 KiB
C
2 years ago
|
// 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__
|