include/Cache/CacheEngine.hpp

Go to the documentation of this file.
00001 #ifndef hpp_CPP_CacheEngine_CPP_hpp
00002 #define hpp_CPP_CacheEngine_CPP_hpp
00003 
00004 // We need types
00005 #include "../Types/Types.hpp"
00006 // We need AVL trees too for fast index
00007 #include "../Tree/Avl.hpp"
00008 // We need containers too for item groups
00009 #include "../Containers/Container.hpp"
00010 // We need strings too
00011 #include "../Strings/Strings.hpp"
00012 // We need time declaration
00013 #include <time.h>
00014 // We need platform specific lock mechanism
00015 #include "../Platform/Platform.hpp"
00016 
00017 
00019 namespace Cache
00020 {
00022     typedef time_t CacheTime;
00024     typedef PlatformSpecific::FastLock Lock;
00025 
00038     class Item
00039     {
00040         // Types
00041     public:
00043         enum DocumentType
00044         {
00045             Unknown     =   0x00000000,     
00046             XML         =   0x00000001,     
00047             HTML        =   0x00000002,     
00048             JPEGImage   =   0x00000003,     
00049             PNGImage    =   0x00000004,     
00050             GIFImage    =   0x00000005,     
00051             Stylesheet  =   0x00000006,     
00052             ScriptFile  =   0x00000007,     
00053         };
00055         enum Status
00056         {
00057             Unset               = 0,    
00058             WaitingFetching     = 1,    
00059             Ready               = 2,    
00060 
00061             FetchingError       = -1,    
00062             BadURI              = -2,    
00063         };
00064 
00066         typedef void (*DeleteDataFnc)(void *);    
00067 
00068         // Members
00069     private:    
00071         const Strings::FastString   URL;
00073         CacheTime                   timestamp;
00075         DocumentType            type;
00077         void *                  data;
00079         uint32                  size;
00081         Status                  status;
00083         DeleteDataFnc           deleteFunction;
00084         
00085     public:    
00087         volatile Lock           lock;
00088 
00089         // The default deletion functions
00090     public:    
00094         template <typename T>
00095             static void DeleteDeleter(T * ptr) { delete ptr; }
00099         template <typename T>
00100             static void ArrayDeleteDeleter(T * ptr) { delete[] ptr; }
00103         static void FreeDeleter(void * ptr) { free(ptr); }
00106         static void NoDeleter(void * ptr) { }
00108         static void PODBlockDeleter(void * ptr) { delete[] (char*)ptr; }
00109 
00110         // Interface used when single threaded
00111     public:        
00113         inline static CacheTime Now() { return (CacheTime)time(NULL); }
00115         inline void setData(const DocumentType _type, void * _data, const uint32 _size, const DeleteDataFnc _deleter = &PODBlockDeleter) { type = _type; data = _data; size = _size; deleteFunction = _deleter; }
00117         inline void setStatus(const Status _status) { status = _status; }
00119         inline const void * getData() const { return data; }
00121         inline const uint32 getDataSize() const { return size; }
00123         inline const DocumentType getDocumentType() const { return type; }
00125         inline const Strings::FastString & getDocumentURL() const { return URL; }
00127         inline const CacheTime getTimestamp() const { return timestamp; }
00129         inline const Status getStatus() const { return status; }
00130 
00132         inline bool fetchingError() const { return status < 0; }
00133 
00135         inline void deleteData() { if (deleteFunction) (*deleteFunction)(data); data = 0; size = 0; type = Unknown; deleteFunction = 0; }
00137         inline void Suicide() { deleteData(); delete this; }
00138     
00139         // Interface used when multithreaded
00140     public:
00142         inline void setData(const DocumentType _type, void * _data, const uint32 _size, const DeleteDataFnc _deleter = &PODBlockDeleter) volatile { PlatformSpecific::LockingObjPtr<Item>(*this, lock)->setData(_type, _data, _size, _deleter); }
00144         inline void setStatus(const Status _status) volatile { PlatformSpecific::LockingObjPtr<Item>(*this, lock)->setStatus(_status); }
00146         inline const void * getData() volatile const { return PlatformSpecific::LockingConstObjPtr<Item>(*this, lock)->getData(); }
00148         inline const uint32 getDataSize() volatile const { return PlatformSpecific::LockingConstObjPtr<Item>(*this, lock)->getDataSize(); }
00150         inline const DocumentType getDocumentType() const volatile  { return PlatformSpecific::LockingConstObjPtr<Item>(*this, lock)->getDocumentType(); }
00152         inline const Strings::FastString & getDocumentURL() const volatile  { return *const_cast<const Strings::FastString*>(&URL); }
00154         inline const CacheTime getTimestamp() const volatile  { return PlatformSpecific::LockingConstObjPtr<Item>(*this, lock)->getTimestamp(); }
00156         inline const Status getStatus() const volatile { return PlatformSpecific::LockingConstObjPtr<Item>(*this, lock)->getStatus(); }
00157 
00159         inline bool fetchingError() const volatile { return PlatformSpecific::LockingConstObjPtr<Item>(*this, lock)->fetchingError(); }
00160         
00162         inline void deleteData() volatile { PlatformSpecific::LockingObjPtr<Item>(*this, lock)->deleteData(); }
00164         inline void Suicide() volatile { PlatformSpecific::LockingObjPtr<Item>(*this, lock)->Suicide(); }
00165 
00166         // Construction / Destruction
00167     public:    
00174         Item(const Strings::FastString & _URL, const DocumentType & _type = Unknown, void * _data = 0, const uint32 _size = 0, const DeleteDataFnc _deleter = &PODBlockDeleter) 
00175             : URL(_URL), timestamp(Now()), type(_type), data(_data), size(_size), deleteFunction(_deleter), status(WaitingFetching) {}
00176 
00179         ~Item() { deleteData(); }
00180     };
00181 
00183     struct ItemComparator
00184     {
00185         inline static bool LessThan(const volatile Item & a, const volatile Item& b) { return (bool)(a.getDocumentURL() < b.getDocumentURL()); }
00186         inline static bool Equal(const volatile Item & a, const volatile Item & b) { return (bool)(a.getDocumentURL() == b.getDocumentURL()); }
00187     };
00188 
00200     class RequiredFetcherImplementation
00201     {
00202     public:
00212         virtual bool queueDocumentToFetch(volatile Item & item, void * opaque = 0) = 0;
00213 
00228         virtual uint32 isItemFetched(volatile Item & item) const = 0;
00229     };
00230 
00231 
00232 
00250     class Holder
00251     {
00252         // Type definition
00253     public:    
00255         typedef Container::PlainOldData<volatile Item *>::Array ItemGroup;
00257         typedef Container::NotConstructible<ItemGroup>::ChainedList ItemGroupList;
00258 
00259         // Members
00260     private:
00262         RequiredFetcherImplementation &     fetcher;
00264         typedef Tree::AVL::Tree<volatile Item *, Strings::FastString, ItemComparator, Tree::AVL::PointerDeletion<volatile Item *, Strings::FastString> > ItemTree;
00266         ItemTree                            items;
00268         typedef Tree::AVL::Tree<volatile Item *, CacheTime>  TimeTree;
00270         TimeTree                            timeIndex;
00272         const uint32                        maxAllowedSize;
00274         uint32                              currentSize;
00276         ItemGroupList                       itemGroupList;
00277 
00278         // Helpers
00279     private:
00285         volatile Item * addItemToCache(const Strings::FastString & URL)
00286         {
00287             // Make sure the item doesn't exist already
00288             ItemTree::IterT iter = items.searchFor(URL);
00289             if (iter.isValid()) return *iter;
00290             
00291             // Not found already, so allocate it
00292             volatile Item * item = new Item(URL);
00293             if (!item) return 0; 
00294             
00295             // Then store in the tree
00296             if (!timeIndex.insertObject(item, item->getTimestamp())) { delete item; return 0; }
00297             if (!items.insertObject(item, URL)) 
00298             { 
00299                 timeIndex.Delete(item->getTimestamp());
00300                 delete item; return 0; 
00301             }
00302 
00303             return item;
00304         }
00305 
00309         inline void removeItemFromCache(volatile Item * item, const bool clean = true)
00310         {
00311             if (item)
00312             {
00313                 if (clean) item->deleteData();
00314                 timeIndex.Delete(item->getTimestamp());
00315                 items.Delete(item->getDocumentURL());
00316             }
00317         }
00318 
00322         inline void removeItemFromGroup(volatile Item * item, ItemGroup * group)
00323         {
00324             if (item && group) group->Remove(group->indexOf(item));
00325         }
00326 
00327 
00328         // Interface
00329     public:
00336         ItemGroup * addGroupFromRoot(const Strings::FastString & URL, void * opaque)
00337         {
00338             // Check if the group already exist
00339             ItemGroup * group = findRoot(URL);
00340             if (group) return group;
00341             else
00342             {
00343                 // Create an item
00344                 // Then add the item to the list too 
00345                 volatile Item * item = addItemToCache(URL);
00346 
00347                 // Ok, create one
00348                 group = new ItemGroup;
00349                 if (!group) { removeItemFromCache(item, false); return 0; }
00350 
00351                 // Append the item
00352                 group->Append(item);
00353                 // And then fetch it
00354                 if (!fetcher.queueDocumentToFetch(*item, opaque))
00355                     { removeItemFromCache(item); delete group; return 0; }
00356 
00357                 if (!itemGroupList.Add(group))
00358                     { removeItemFromCache(item); delete group; return 0; }
00359 
00360                 return group;
00361             }
00362         }
00363 
00367         ItemGroup * findRoot(const Strings::FastString & URL)
00368         {
00369             // This is stupid linear search
00370             ItemGroup * first = itemGroupList.parseList(true);
00371             while (first)
00372             {
00373                 if (first->getSize() && first->getElementAtUncheckedPosition(0)->getDocumentURL() == URL) return first;
00374                 first = itemGroupList.parseList();
00375             }
00376             return 0;
00377         }
00378 
00382         ItemGroup * findGroupFromItem(volatile Item * item)
00383         {
00384             if (!item) return 0;
00385             // This is stupid linear search
00386             ItemGroup * first = itemGroupList.parseList(true);
00387             while (first)
00388             {
00389                 if (first->getSize())
00390                 {
00391                     if (first->indexOf(item) != first->getSize()) return first;
00392                 }
00393                 first = itemGroupList.parseList();
00394             }
00395             return 0;
00396         }
00397 
00398 
00402         bool emptyCache(const uint32 size)
00403         {
00404             if (size > maxAllowedSize) return false;
00405             if (size == maxAllowedSize)
00406             {   // Clean the whole cache
00407                 timeIndex.Clear();
00408                 items.Clear();
00409 
00410                 // Then clear the list
00411                 itemGroupList.Free();
00412                 currentSize = 0;
00413             }
00414             else
00415             {   // Need to find the oldest items
00416                 while (((int32)maxAllowedSize - (int32)currentSize) < (int32)size)
00417                 {
00418                     // Find the oldest item
00419                     TimeTree::IterT iter = timeIndex.searchExtreme(true);
00420                     if (!iter.isValid()) return false; // It's not possible to find an oldest item...
00421 
00422                     // First remove this item from every group first
00423                     volatile Item * item = *iter;
00424                     ItemGroup * group = findGroupFromItem(item);
00425                     while (group) 
00426                     {
00427                         removeItemFromGroup(item, group);
00428                         group = findGroupFromItem(item);
00429                     }
00430 
00431                     // Then remove this item, once for all
00432                     currentSize -= item->getDataSize();
00433                     removeItemFromCache(item);
00434                 }
00435                 // Ok, it's done !
00436             }
00437             return true;
00438         }
00439 
00445         bool addItemToGroup(const Strings::FastString & URL, ItemGroup * group)
00446         {
00447             if (!group) return false;
00448             // Create an item
00449             volatile Item * item = addItemToCache(URL);
00450 
00451             // Append the item
00452             group->Append(item);
00453             // And then fetch it
00454             if (!fetcher.queueDocumentToFetch(*item))
00455                 { removeItemFromGroup(item, group); removeItemFromCache(item); return false; }
00456 
00457             return true;
00458         }
00459 
00464         inline volatile Item * findItemFromURL(const Strings::FastString & URL)
00465         {
00466             ItemTree::IterT iter = items.searchFor(URL); if (iter.isValid()) return *iter; else return 0;
00467         }
00468 
00475         const uint32 getFetchingAdvancement(const ItemGroup * group, bool booleanResult = false)
00476         {
00477             if (!group) return 0; // No documents
00478             // First, add cumulative sizes
00479             uint32 i = 0, size = 0;
00480             for (; i < group->getSize(); i++) size += group->getElementAtUncheckedPosition(i)->fetchingError() ? 0 : group->getElementAtUncheckedPosition(i)->getDataSize();
00481             if (!size) // No known size yet
00482             {
00483                 // Either every document are fetched, either none of them is.
00484                 if (booleanResult)
00485                 {
00486                     for (i = 0; i < group->getSize(); i++) 
00487                     {
00488                         uint32 current = fetcher.isItemFetched(*group->getElementAtUncheckedPosition(i));
00489                         if (current == (uint32)-1) return 0;
00490                     }
00491                     return 100;
00492                 } // Else, simply deduce percentage from the number of entirely fetched documents
00493                 uint32 currentCount = 0;
00494                 for (i = 0; i < group->getSize(); i++) 
00495                 {
00496                     currentCount += fetcher.isItemFetched(*group->getElementAtUncheckedPosition(i)) != (uint32)-1;
00497                 }
00498                 return (currentCount * 100) / group->getSize();
00499             }
00500             else
00501             {   // Need to compute percentage here
00502                 uint32 current = 0, count = 0;
00503                 for (i = 0; i < group->getSize(); i++) 
00504                 {
00505                     uint32 amountFetched = fetcher.isItemFetched(*group->getElementAtUncheckedPosition(i));
00506                     current += amountFetched == (uint32)-1 ? 0 : amountFetched;
00507                     count += amountFetched != (uint32)-1;
00508                 }
00509                 return booleanResult ? (count == group->getSize()) * 100 : (current * 100) / size;
00510             }
00511         }
00512 
00519         inline const bool entireGroupReady(const ItemGroup * group)
00520         {
00521             return getFetchingAdvancement(group, true) == 100;
00522         }
00523 
00529         inline const bool groupFetchingError(const ItemGroup * group)
00530         {
00531             if (!group || !group->getSize() || group->getElementAtUncheckedPosition(0)->fetchingError()) return true;
00532             return false;
00533         }
00534 
00535         // Construction
00536     public:
00542         Holder(RequiredFetcherImplementation & _fetcher, const uint32 maxSize = 33554432) : fetcher(_fetcher), currentSize(0), maxAllowedSize(maxSize) {}
00544         virtual ~Holder() { emptyCache(maxAllowedSize); }
00545     };
00546 }
00547 
00548 #endif

(C) An X-Ryl669 project 2007

This document describes Unlimited Zooming Interface source code. UZI stands for Unlimited Zooming Interface, and source code license is