From d56616b1035f9721cf3cb5554e3f033fde0e6ad9 Mon Sep 17 00:00:00 2001 From: Fredrik Roubert Date: Thu, 24 Mar 2011 10:31:45 +0000 Subject: [PATCH] Cpp: Implementing Cache. --- cpp/src/re2_cache.cc | 51 +++++++++++++++++++++++++++++++++------ cpp/src/re2_cache.h | 37 ++++++++++++++++++++++------ cpp/src/re2_cache_test.cc | 15 +++++++----- 3 files changed, 83 insertions(+), 20 deletions(-) diff --git a/cpp/src/re2_cache.cc b/cpp/src/re2_cache.cc index b90dbf562..1abf95e49 100644 --- a/cpp/src/re2_cache.cc +++ b/cpp/src/re2_cache.cc @@ -18,21 +18,58 @@ #include #include +#include #include -namespace i18n { -namespace phonenumbers { +#include "base/logging.h" +#include "base/synchronization/lock.h" using std::string; -RE2Cache::RE2Cache(size_t /*max_items*/) {} -RE2Cache::~RE2Cache() {} +// A basic text book string hash function implementation, this one taken from +// The Practice of Programming (Kernighan and Pike 1999). It could be a good +// idea in the future to evaluate how well it actually performs and possibly +// switch to another hash function better suited to this particular use case. +namespace __gnu_cxx { +template<> struct hash { + enum { MULTIPLIER = 31 }; + size_t operator()(const string& key) const { + size_t h = 0; + for (const char* p = key.c_str(); *p != '\0'; ++p) { + h *= MULTIPLIER; + h += *p; + } + return h; + } +}; +} // namespace __gnu_cxx + +namespace i18n { +namespace phonenumbers { -RE2Cache::ScopedAccess::ScopedAccess(RE2Cache* /*cache*/, const string& pattern) - : pattern_(pattern), regexp_(new RE2(pattern_)) {} +RE2Cache::RE2Cache(size_t min_items) : cache_impl_(new CacheImpl(min_items)) {} +RE2Cache::~RE2Cache() { + base::AutoLock l(lock_); + LOG(2) << "Cache entries upon destruction: " << cache_impl_->size(); + for (CacheImpl::const_iterator + it = cache_impl_->begin(); it != cache_impl_->end(); ++it) { + delete it->second; + } +} -RE2Cache::ScopedAccess::~ScopedAccess() {} +RE2Cache::ScopedAccess::ScopedAccess(RE2Cache* cache, const string& pattern) { + DCHECK(cache); + base::AutoLock l(cache->lock_); + CacheImpl* const cache_impl = cache->cache_impl_.get(); + CacheImpl::const_iterator it = cache_impl->find(pattern); + if (it != cache_impl->end()) { + regexp_ = it->second; + } else { + regexp_ = new RE2(pattern); + cache_impl->insert(make_pair(pattern, regexp_)); + } +} } // namespace phonenumbers } // namespace i18n diff --git a/cpp/src/re2_cache.h b/cpp/src/re2_cache.h index 323fb2532..ab2e9bbf1 100644 --- a/cpp/src/re2_cache.h +++ b/cpp/src/re2_cache.h @@ -14,17 +14,32 @@ // Author: Fredrik Roubert -// The RE2Cache provides an interface to store RE2 objects in some kind of -// cache. Currently, it doesn't do any caching at all but just provides the -// interface. TODO: Implement caching. ;-) +// RE2Cache is a simple wrapper around hash_map<> to store RE2 objects. +// +// To get a cached RE2 object for a regexp pattern string, create a ScopedAccess +// object with a pointer to the cache object and the pattern string itself as +// constructor parameters. If an RE2 object corresponding to the pattern string +// doesn't already exist, it will be created by the access object constructor. +// The access object implements operator const RE& and can therefore be passed +// as an argument to any function that expects an RE2 object. +// +// RE2Cache cache; +// RE2Cache::ScopedAccess foo(&cache, "foo"); +// bool match = RE2::FullMatch("foobar", foo); #ifndef I18N_PHONENUMBERS_RE2_CACHE_H_ #define I18N_PHONENUMBERS_RE2_CACHE_H_ +#ifdef __DEPRECATED +#undef __DEPRECATED // Don't warn for using . +#endif + #include +#include #include #include "base/scoped_ptr.h" +#include "base/synchronization/lock.h" namespace re2 { class RE2; @@ -35,23 +50,31 @@ namespace phonenumbers { using re2::RE2; using std::string; +using __gnu_cxx::hash_map; class RE2Cache { + private: + typedef hash_map CacheImpl; + public: - explicit RE2Cache(size_t max_items); + explicit RE2Cache(size_t min_items); ~RE2Cache(); class ScopedAccess { public: ScopedAccess(RE2Cache* cache, const string& pattern); - ~ScopedAccess(); operator const RE2&() const { return *regexp_; } private: - const string pattern_; - scoped_ptr regexp_; + const RE2* regexp_; friend class RE2CacheTest_AccessConstructor_Test; }; + + private: + base::Lock lock_; // protects cache_impl_ + scoped_ptr cache_impl_; // protected by lock_ + friend class RE2CacheTest_CacheConstructor_Test; + friend class RE2CacheTest_AccessConstructor_Test; }; } // namespace phonenumbers diff --git a/cpp/src/re2_cache_test.cc b/cpp/src/re2_cache_test.cc index c7d9b7ed4..70f6c44b0 100644 --- a/cpp/src/re2_cache_test.cc +++ b/cpp/src/re2_cache_test.cc @@ -14,9 +14,6 @@ // Author: Fredrik Roubert -// Test the wrapper of the cache. The cache functionality itself will be tested -// by the unit test for the cache implementation. - #include #include @@ -32,20 +29,26 @@ using std::string; class RE2CacheTest : public testing::Test { protected: - static const size_t max_items_ = 2; + static const size_t min_items_ = 2; - RE2CacheTest() : cache_(max_items_) {} + RE2CacheTest() : cache_(min_items_) {} virtual ~RE2CacheTest() {} RE2Cache cache_; }; +TEST_F(RE2CacheTest, CacheConstructor) { + ASSERT_TRUE(cache_.cache_impl_ != NULL); + EXPECT_TRUE(cache_.cache_impl_->empty()); +} + TEST_F(RE2CacheTest, AccessConstructor) { static const string foo("foo"); RE2Cache::ScopedAccess access(&cache_, foo); - EXPECT_EQ(foo, access.pattern_); EXPECT_TRUE(access.regexp_ != NULL); + ASSERT_TRUE(cache_.cache_impl_ != NULL); + EXPECT_EQ(1, cache_.cache_impl_->size()); } TEST_F(RE2CacheTest, OperatorRE2) {