iipsrv  0.9.9
Cache.h
00001 // Tile Cache Class
00002 
00003 /*  IIP Image Server
00004 
00005     Copyright (C) 2005-2010 Ruven Pillay.
00006     Based on an LRU cache by Patrick Audley <http://blackcat.ca/lifeline/query.php/tag=LRU_CACHE>
00007     Copyright (C) 2004 by Patrick Audley
00008 
00009     This program is free software; you can redistribute it and/or modify
00010     it under the terms of the GNU General Public License as published by
00011     the Free Software Foundation; either version 2 of the License, or
00012     (at your option) any later version.
00013 
00014     This program is distributed in the hope that it will be useful,
00015     but WITHOUT ANY WARRANTY; without even the implied warranty of
00016     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017     GNU General Public License for more details.
00018 
00019     You should have received a copy of the GNU General Public License
00020     along with this program; if not, write to the Free Software
00021     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00022 */
00023 
00024 
00025 
00026 #ifndef _CACHE_H
00027 #define _CACHE_H
00028 
00029 // Remove our deprecated warnings for now. We should upgrade our hash_maps to
00030 // unordered_maps, however
00031 #undef __DEPRECATED
00032 
00033 // Use the hashmap extensions if we are using >= gcc 3.1
00034 #ifdef __GNUC__
00035 
00036 #if (__GNUC__ == 3 && __GNUC_MINOR__ >= 1) || (__GNUC__ >= 4)
00037 #define USE_HASHMAP 1
00038 #include <ext/hash_map>
00039 namespace __gnu_cxx
00040 {
00041   template<> struct hash< const std::string > {
00042     size_t operator()( const std::string& x ) const {
00043       return hash< const char* >()( x.c_str() );
00044     }
00045   };
00046 }
00047 #endif
00048 
00049 // And the high performance memory pool allocator if >= gcc 3.4
00050 #if (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || (__GNUC__ >= 4)
00051 #define POOL_ALLOCATOR 1
00052 #include <ext/pool_allocator.h>
00053 #endif
00054 
00055 #endif
00056 
00057 
00058 #ifndef USE_HASHMAP
00059 #include <map>
00060 #endif
00061 
00062 
00063 // Fix missing snprintf in Windows
00064 #if _MSC_VER
00065 #define snprintf _snprintf
00066 #endif
00067 
00068 
00069 #include <iostream>
00070 #include <list>
00071 #include <string>
00072 #include "RawTile.h"
00073 
00074 
00075 
00077 
00078 class Cache {
00079 
00080 
00081  private:
00082 
00084   int tileSize;
00085 
00087   unsigned long maxSize;
00088 
00090   unsigned long currentSize;
00091 
00093 #ifdef POOL_ALLOCATOR
00094   typedef std::list < std::pair<const std::string,RawTile>,
00095     __gnu_cxx::__pool_alloc< std::pair<const std::string,RawTile> > > TileList;
00096 #else
00097   typedef std::list < std::pair<const std::string,RawTile> > TileList;
00098 #endif
00099 
00101   typedef std::list < std::pair<const std::string,RawTile> >::iterator List_Iter;
00102 
00104 #ifdef USE_HASHMAP
00105 #ifdef POOL_ALLOCATOR
00106   typedef __gnu_cxx::hash_map < const std::string, List_Iter,
00107     __gnu_cxx::hash< const std::string >,
00108     std::equal_to< const std::string >,
00109     __gnu_cxx::__pool_alloc< std::pair<const std::string, List_Iter> >
00110     > TileMap;
00111 #else
00112   typedef __gnu_cxx::hash_map < const std::string,List_Iter > TileMap;
00113 #endif
00114 #else
00115   typedef std::map < const std::string,List_Iter > TileMap;
00116 #endif
00117 
00119   TileList tileList;
00120 
00122   TileMap tileMap;
00123 
00124 
00126 
00130   TileMap::iterator _touch( const std::string &key ) {
00131     TileMap::iterator miter = tileMap.find( key );
00132     if( miter == tileMap.end() ) return miter;
00133     // Move the found node to the head of the list.
00134     tileList.splice( tileList.begin(), tileList, miter->second );
00135     return miter;
00136   }
00137 
00138 
00140 
00144   void _remove( const TileMap::iterator &miter ) {
00145     // Reduce our current size counter
00146     currentSize -= ( (miter->second->second).dataLength +
00147                      ( (miter->second->second).filename.capacity() + (miter->second->first).capacity() )*sizeof(char) +
00148                      tileSize );
00149     tileList.erase( miter->second );
00150     tileMap.erase( miter );
00151   }
00152 
00153 
00155 
00156   void _remove( const std::string &key ) {
00157     TileMap::iterator miter = tileMap.find( key );
00158     this->_remove( miter );
00159   }
00160 
00161 
00162 
00163  public:
00164 
00166 
00167   Cache( float max ) {
00168     maxSize = (unsigned long)(max*1024000) ; currentSize = 0;
00169     // 64 chars added at the end represents an average string length
00170     tileSize = sizeof( RawTile ) + sizeof( std::pair<const std::string,RawTile> ) +
00171       sizeof( std::pair<const std::string, List_Iter> ) + sizeof(char)*64 + sizeof(List_Iter);
00172   };
00173 
00174 
00176   ~Cache() {
00177     tileList.clear();
00178     tileMap.clear();
00179   }
00180 
00181 
00183 
00184   void insert( const RawTile& r ) {
00185 
00186     if( maxSize == 0 ) return;
00187 
00188     std::string key = this->getIndex( r.filename, r.resolution, r.tileNum,
00189                                       r.hSequence, r.vSequence, r.compressionType, r.quality );
00190 
00191     // Touch the key, if it exists
00192     TileMap::iterator miter = this->_touch( key );
00193 
00194     // Check whether this tile exists in our cache
00195     if( miter != tileMap.end() ){
00196       // Check the timestamp and delete if necessary
00197       if( miter->second->second.timestamp < r.timestamp ){
00198         this->_remove( miter );
00199       }
00200       // If this index already exists and it is up to date, do nothing
00201       else return;
00202     }
00203 
00204     // Store the key if it doesn't already exist in our cache
00205     // Ok, do the actual insert at the head of the list
00206     tileList.push_front( std::make_pair(key,r) );
00207 
00208     // And store this in our map
00209     List_Iter liter = tileList.begin();
00210     tileMap[ key ] = liter;
00211 
00212     // Update our total current size variable. Use the string::capacity function
00213     // rather than length() as std::string can allocate slightly more than necessary
00214     // The +1 is for the terminating null byte
00215     currentSize += (r.dataLength + (r.filename.capacity()+key.capacity())*sizeof(char) + tileSize);
00216 
00217     // Check to see if we need to remove an element due to exceeding max_size
00218     while( currentSize > maxSize ) {
00219       // Remove the last element
00220       liter = tileList.end();
00221       --liter;
00222       this->_remove( liter->first );
00223     }
00224 
00225   }
00226 
00227 
00229   unsigned int getNumElements() { return tileList.size(); }
00230 
00231 
00233   float getMemorySize() { return (float) ( currentSize / 1024000.0 ); }
00234 
00235 
00237 
00247   RawTile* getTile( std::string f, int r, int t, int h, int v, CompressionType c, int q ) {
00248 
00249     if( maxSize == 0 ) return NULL;
00250 
00251     std::string key = this->getIndex( f, r, t, h, v, c, q );
00252 
00253     TileMap::iterator miter = tileMap.find( key );
00254     if( miter == tileMap.end() ) return NULL;
00255     this->_touch( key );
00256 
00257     return &(miter->second->second);
00258   }
00259 
00260 
00262 
00272   std::string getIndex( std::string f, int r, int t, int h, int v, CompressionType c, int q ) {
00273     char tmp[1024];
00274     snprintf( tmp, 1024, "%s:%d:%d:%d:%d:%d:%d", f.c_str(), r, t, h, v, c, q );
00275     return std::string( tmp );
00276   }
00277 
00278 
00279 
00280 };
00281 
00282 
00283 
00284 #endif