00001 #ifndef hpp_CPP_CacheEngine_CPP_hpp
00002 #define hpp_CPP_CacheEngine_CPP_hpp
00003
00004
00005 #include "../Types/Types.hpp"
00006
00007 #include "../Tree/Avl.hpp"
00008
00009 #include "../Containers/Container.hpp"
00010
00011 #include "../Strings/Strings.hpp"
00012
00013 #include <time.h>
00014
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
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
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
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
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
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
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
00253 public:
00255 typedef Container::PlainOldData<volatile Item *>::Array ItemGroup;
00257 typedef Container::NotConstructible<ItemGroup>::ChainedList ItemGroupList;
00258
00259
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
00279 private:
00285 volatile Item * addItemToCache(const Strings::FastString & URL)
00286 {
00287
00288 ItemTree::IterT iter = items.searchFor(URL);
00289 if (iter.isValid()) return *iter;
00290
00291
00292 volatile Item * item = new Item(URL);
00293 if (!item) return 0;
00294
00295
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
00329 public:
00336 ItemGroup * addGroupFromRoot(const Strings::FastString & URL, void * opaque)
00337 {
00338
00339 ItemGroup * group = findRoot(URL);
00340 if (group) return group;
00341 else
00342 {
00343
00344
00345 volatile Item * item = addItemToCache(URL);
00346
00347
00348 group = new ItemGroup;
00349 if (!group) { removeItemFromCache(item, false); return 0; }
00350
00351
00352 group->Append(item);
00353
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
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
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 {
00407 timeIndex.Clear();
00408 items.Clear();
00409
00410
00411 itemGroupList.Free();
00412 currentSize = 0;
00413 }
00414 else
00415 {
00416 while (((int32)maxAllowedSize - (int32)currentSize) < (int32)size)
00417 {
00418
00419 TimeTree::IterT iter = timeIndex.searchExtreme(true);
00420 if (!iter.isValid()) return false;
00421
00422
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
00432 currentSize -= item->getDataSize();
00433 removeItemFromCache(item);
00434 }
00435
00436 }
00437 return true;
00438 }
00439
00445 bool addItemToGroup(const Strings::FastString & URL, ItemGroup * group)
00446 {
00447 if (!group) return false;
00448
00449 volatile Item * item = addItemToCache(URL);
00450
00451
00452 group->Append(item);
00453
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;
00478
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)
00482 {
00483
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 }
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 {
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
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