NodeCache.cpp
Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include "Qt.h"
00024 #include "NodeCache.h"
00025 #include "Imports.cpp"
00026
00027 using namespace Gnutella::Bootstrapping;
00028
00029 namespace Gnutella {
00030 namespace Bootstrapping {
00031
00032 QDataStream & operator << (QDataStream &stream,
00033 const NodeCache::NodeAvailability &availability)
00034 {
00035 return stream << static_cast <uchar> (availability);
00036 }
00037
00038 QDataStream & operator >> (QDataStream &stream,
00039 NodeCache::NodeAvailability &availability)
00040 {
00041 uchar ch;
00042 stream >> ch;
00043 availability = static_cast <NodeCache::NodeAvailability> (ch);
00044 return stream;
00045 }
00046
00047 enum Constants
00048 {
00049 AvailabilityExpiration = 600,
00050 MaximalCacheSize = 2000,
00051 ShrinkCacheSize = 1500
00052 };
00053
00054 struct Entry;
00055 typedef QHash <NodeAddress, Entry*> NodeTable;
00056 typedef QLinkedList <Entry*> EntryList;
00057 typedef EntryList::iterator EntryIterator;
00058
00059
00060 struct Entry
00061 {
00062 NodeCache::NodeAvailability availability;
00063 QDateTime timeLastAdded;
00064 NodeInfo nodeInfo;
00065 EntryIterator availabilityListIterator;
00066 EntryIterator completeListIterator;
00067
00068 Entry() : availability (NodeCache::UnknownAvailability),
00069 timeLastAdded (QDateTime::currentDateTime()),
00070 nodeInfo(), availabilityListIterator(), completeListIterator()
00071 {}
00072 };
00073
00074 class NodeCachePrivate
00075 {
00076 public:
00077 NodeTable nodeTable;
00078 EntryList availabilityList[NodeCache::AvailabilityCount];
00079 EntryList completeList;
00080
00081 NodeCachePrivate() : nodeTable(), completeList() {}
00082 ~NodeCachePrivate() {}
00083 };
00084
00085 }
00086 }
00087
00088 NodeCache::NodeCache()
00089 : d (new NodeCachePrivate())
00090 {
00091 }
00092
00093 NodeCache::~NodeCache()
00094 {
00095 delete d;
00096 }
00097
00098 NodeSet NodeCache::getNodes (int count, NodeAvailability availability, bool freshNodes)
00099 {
00100 class AnyNode : public Predicate
00101 {
00102 public:
00103 bool operator() (const NodeInfo &) const { return true; }
00104 };
00105
00106 return getNodes (count, availability, freshNodes,
00107 static_cast <const Predicate &> (AnyNode()));
00108 }
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118 void NodeCache::addNode (const NodeInfo &nodeInfo,
00119 NodeAvailability availability)
00120 {
00121 Q_ASSERT (nodeInfo.address != NodeAddress::Null);
00122 qDebug() << "addNode() " << nodeInfo.address.toString() << nodeInfo.vendorCode.toString();
00123
00124 NodeTable::iterator i = d->nodeTable.find (nodeInfo.address);
00125 if (i == d->nodeTable.end()) {
00126 if (d->completeList.size() >= MaximalCacheSize) {
00127 Entry *entry;
00128 for (int i = d->completeList.size() - ShrinkCacheSize; i > 0; i--) {
00129 entry = d->completeList.takeLast();
00130 d->availabilityList[entry->availability].erase (entry->availabilityListIterator);
00131 int count = d->nodeTable.remove (entry->nodeInfo.address);
00132 Q_ASSERT (count == 1);
00133 delete entry;
00134 }
00135 }
00136 Entry *entry = new Entry;
00137 entry->nodeInfo = nodeInfo;
00138 entry->availability = availability;
00139 d->nodeTable.insert (nodeInfo.address, entry);
00140
00141 EntryList &availabilityList = d->availabilityList[availability];
00142 entry->availabilityListIterator = availabilityList.insert (availabilityList.begin(), entry);
00143 entry->completeListIterator = d->completeList.insert (d->completeList.begin(), entry);
00144
00145 emit nodeAdded();
00146 } else {
00147 Entry *entry = *i;
00148 entry->nodeInfo = nodeInfo;
00149 updateNode (nodeInfo.address, availability);
00150 }
00151 }
00152
00153 void NodeCache::updateNode (const NodeAddress &nodeAddress,
00154 NodeAvailability availability)
00155 {
00156 Q_ASSERT (nodeAddress != NodeAddress::Null);
00157
00158 NodeTable::iterator i = d->nodeTable.find (nodeAddress);
00159 if (i != d->nodeTable.end()) {
00160 Entry *entry = *i;
00161 QDateTime currentDateTime = QDateTime::currentDateTime();
00162
00163 bool canOverwrite = availability >= entry->availability
00164 || currentDateTime > entry->timeLastAdded.addSecs (AvailabilityExpiration);
00165
00166 if (canOverwrite) {
00167 d->availabilityList[entry->availability].erase (entry->availabilityListIterator);
00168 d->completeList.erase (entry->completeListIterator);
00169
00170 entry->availability = availability;
00171 entry->timeLastAdded = currentDateTime;
00172
00173 EntryList &availabilityList = d->availabilityList[availability];
00174 entry->availabilityListIterator = availabilityList.insert (availabilityList.begin(), entry);
00175 entry->completeListIterator = d->completeList.insert (d->completeList.begin(), entry);
00176 }
00177 }
00178 }
00179
00185 NodeSet NodeCache::getNodes (int count, NodeAvailability availability, bool freshNodes, const Predicate &pred)
00186 {
00187 NodeSet nodes;
00188 EntryList &availabilityList = d->availabilityList[availability];
00189 EntryIterator i = availabilityList.begin();
00190 QDateTime expirationTime = QDateTime::currentDateTime().addSecs (-AvailabilityExpiration);
00191 for (; i != availabilityList.end()
00192 && nodes.size() < count
00193 && (!freshNodes
00194 || (*i)->timeLastAdded > expirationTime)
00195 ; i++)
00196 if (pred((*i)->nodeInfo))
00197 nodes.insert ((*i)->nodeInfo.address);
00198 return nodes;
00199 }
00200
00201 bool NodeCache::loadNodes (const QString &filename)
00202 {
00203 QFile file (filename);
00204 if (!file.exists() || !file.open (QIODevice::ReadOnly))
00205 return false;
00206
00207 QDataStream stream (&file);
00208
00209 int count = 0;
00210 stream >> count;
00211
00212
00213 while (count-- > 0) {
00214 Entry *entry = new Entry;
00215
00216 stream >> entry->nodeInfo;
00217 stream >> entry->timeLastAdded;
00218 stream >> entry->availability;
00219
00220
00221 entry->availability = UnknownAvailability;
00222
00223 entry->completeListIterator = d->completeList.insert (d->completeList.end(), entry);
00224 entry->availabilityListIterator = d->availabilityList[entry->availability].insert (d->availabilityList[entry->availability].end(), entry);
00225 d->nodeTable.insert (entry->nodeInfo.address, entry);
00226 }
00227 return true;
00228 }
00229
00230 bool NodeCache::storeNodes (const QString &filename)
00231 {
00232
00233
00234 QFile file (filename);
00235
00236 if (!file.open (QIODevice::WriteOnly))
00237 return false;
00238
00239 QDataStream stream (&file);
00240
00241
00242 stream << d->completeList.count() - d->availabilityList[NodeUnavailable].count();
00243 EntryIterator i = d->completeList.begin();
00244 EntryIterator e = d->completeList.end();
00245 for (; i != e; i++) {
00246 if ((*i)->availability != NodeUnavailable) {
00247 stream << (*i)->nodeInfo;
00248 stream << (*i)->timeLastAdded;
00249 stream << (*i)->availability;
00250 }
00251 }
00252 return true;
00253 }