diff --git a/dataStore.cpp b/dataStore.cpp new file mode 100644 index 0000000..a801788 --- /dev/null +++ b/dataStore.cpp @@ -0,0 +1,110 @@ +/*************************************************************************** + * By Théophile Bastian, 2017 + * M1 Network course project at ENS Cachan, Juliusz Chroboczek. + * License: WTFPL v2 + **************************************************************************/ + +#include "dataStore.h" +#include +#include +using namespace std; + +DataStore::DataStore(Protocol* proto) : proto(proto) +{} + +DataStore::~DataStore() { +} + +void DataStore::update() { + while(!timeEvents.empty()) { + const TimeEvent& evt = timeEvents.top(); + if(evt.time > time(NULL)) // We're done for now. + break; + + timeEvents.pop(); + switch(evt.type) { + case EV_REPUBLISH: + handleRepublish(evt.id); + break; + + case EV_EXPIRES: + handleExpire(evt.id, evt.seqno); + break; + } + } +} + +void DataStore::addData(Bytes pck, u32 seqno, u64 id, bool mine) { + if(curSeqno.find(id) != curSeqno.end() && curSeqno[id] >= seqno) + return; + + data[id] = Data(pck, mine); + curSeqno[id] = seqno; + + if(mine) { + timeEvents.push(TimeEvent( + time(NULL) + csts::TIME_REPUBLISH_DATA, + seqno, id, EV_REPUBLISH)); + + } + else { + timeEvents.push(TimeEvent( + time(NULL) + csts::TIMEOUT_DATA, + seqno, id, EV_EXPIRES)); + // If it is not renewed, it will expire. + } + + recvTime[id] = time(NULL); + fprintf(stderr, "[INFO] Storing data %lX (%u)\n", id, seqno); + + handleFlood(id); +} + +ssize_t DataStore::dataSize(u64 id) { + if(data.find(id) == data.end()) + return -1; + return data[id].data.size(); +} + +ssize_t DataStore::readData(u64 id, char* buffer, size_t size) { + if(data.find(id) == data.end()) + return -1; + ssize_t len = min((ssize_t)size, dataSize(id)); + data[id].data.writeToBuffer(buffer, size); + return len; +} + +void DataStore::ids(std::vector& out) { + for(auto& dat : data) + out.push_back(dat.first); +} + +void DataStore::setFlooded(u64 id) { + toFlood_.erase(id); +} + +void DataStore::handleExpire(u64 id, u32 seqno) { + if(seqno < curSeqno[id]) + return; // Was updated in time + + cleanData(id); +} + +void DataStore::handleRepublish(u64 id) { + if(data.find(id) == data.end() || !data[id].isMine) + return; + + curSeqno[id] = time(NULL); + handleFlood(id); +} + +void DataStore::handleFlood(u64 id) { + toFlood_.insert(id); +} + +void DataStore::cleanData(u64 id) { + curSeqno.erase(id); + recvTime.erase(id); + data.erase(id); // Deletes the Bytes as well +} + diff --git a/dataStore.h b/dataStore.h new file mode 100644 index 0000000..8c31f4f --- /dev/null +++ b/dataStore.h @@ -0,0 +1,98 @@ +/*************************************************************************** + * By Théophile Bastian, 2017 + * M1 Network course project at ENS Cachan, Juliusz Chroboczek. + * License: WTFPL v2 + **************************************************************************/ + +#pragma once +#include +#include +#include +#include "data.h" +#include "nw_constants.h" +#include "protocol.h" +#include "Bytes.h" + +class DataStore { + public: + DataStore(Protocol* proto); + ~DataStore(); + + void update(); + /** Performs bookkeeping actions. Sends packets. This function should + * be called often enough. + */ + + void addData(Bytes pck, u32 seqno, u64 id, bool mine=false); + /** Adds a data in the data storage. If `mine` is `true`, the current + * node considers itself responsible for the data and will republish + * it when needed. + */ + + ssize_t dataSize(u64 id); + /** Gets the size of the data with id `id`. Returns -1 if there is no + * such data stored. + */ + + ssize_t readData(u64 id, char* buffer, size_t size); + /** Fills `buffer` with the data of the given `id`. + * If the data does not exist, returs -1; else, returns the length of + * data actually retrieved. + */ + + const Bytes& operator[](u64 id) { + return data[id].data; + } + + u32 getSeqno(u64 id) const { return curSeqno.at(id); } + + void ids(std::vector& out); + /** Fills `out` with the IDs of the stored data. */ + + const std::set toFlood() const { return toFlood_; } + /** Returns a list of data IDs that should be flooded. */ + + void setFlooded(u64 id); + /** Marks a data as flooded */ + + private: //meth + void handleExpire(u64 id, u32 seqno); + void handleRepublish(u64 id); + void handleFlood(u64 id); + + void cleanData(u64 id); + + private: + enum EvType { + EV_REPUBLISH, EV_EXPIRES + }; + struct TimeEvent { + TimeEvent(time_t time, u32 seqno, u64 id, EvType type) : + time(time), seqno(seqno), id(id), type(type) {} + time_t time; + u32 seqno; + u64 id; + EvType type; + + bool operator<(const TimeEvent& e) const { + return time > e.time; // Max-priority queue + } + }; + + struct Data { + Data() : data(), isMine(false) {} + Data(Bytes b) : data(b), isMine(false) {} + Data(Bytes b, bool mine) : data(b), isMine(mine) {} + Bytes data; + bool isMine; + }; + + std::priority_queue timeEvents; + Protocol* proto; + std::unordered_map data; + std::unordered_map curSeqno; + std::unordered_map recvTime; + + std::set toFlood_; +}; + diff --git a/flooder.cpp b/flooder.cpp new file mode 100644 index 0000000..6d54477 --- /dev/null +++ b/flooder.cpp @@ -0,0 +1,54 @@ +/*************************************************************************** + * By Théophile Bastian, 2017 + * M1 Network course project at ENS Cachan, Juliusz Chroboczek. + * License: WTFPL v2 + **************************************************************************/ + +#include "flooder.h" + +Flooder::Flooder(const Bytes& data, u64 datId, u32 seqno, Protocol* proto, + const std::list& peers) : + data(data), datId(datId), seqno(seqno), proto(proto) +{ + fprintf(stderr, "[DBG] Created flooder for %lX (%u)\n", datId, seqno); + for(auto it=peers.begin(); it != peers.end(); ++it) { + triesCount[it->id] = 0; + toFlood.push(FloodEvt(time(NULL), it->id)); + } + update(); +} + +void Flooder::update() { + while(!toFlood.empty()) { + const FloodEvt& evt = toFlood.top(); + if(evt.time > time(NULL)) + break; + toFlood.pop(); + + if(triesCount[evt.id] > csts::FLOOD_RETRIES) { + fprintf(stderr, "[WARNING] Could not flood to %lX: no IHave.\n", + evt.id); + continue; + } + else if(triesCount[evt.id] < 0) { + fprintf(stderr, "[DBG] Got your IHave, %lX!\n", evt.id); + continue; // IHave received + } + + sendTo(evt.id); + triesCount[evt.id]++; + toFlood.push(FloodEvt(time(NULL) + csts::TIME_RESEND_FLOOD, evt.id)); + } +} + +void Flooder::gotIHave(u64 id, u32 withSeqno) { + if(seqno > withSeqno) + return; + + triesCount[id] = -1; +} + +void Flooder::sendTo(u64 id) { + proto->sendData(id, data, seqno, datId); +} + diff --git a/flooder.h b/flooder.h new file mode 100644 index 0000000..ffeca8e --- /dev/null +++ b/flooder.h @@ -0,0 +1,49 @@ +/*************************************************************************** + * By Théophile Bastian, 2017 + * M1 Network course project at ENS Cachan, Juliusz Chroboczek. + * License: WTFPL v2 + **************************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include "data.h" +#include "nw_constants.h" +#include "protocol.h" + +class Flooder { + public: + Flooder(const Bytes& data, u64 datId, u32 seqno, Protocol* proto, + const std::list& peers); + + void update(); + /** Call often enough (ie ~1s) */ + + void gotIHave(u64 id, u32 withSeqno); + /** Acknoledges the reception of the resource by peer `id`. */ + + bool done() const { return toFlood.empty(); } + + private: //meth + void sendTo(u64 id); + + private: + struct FloodEvt { + FloodEvt(time_t time, u64 id) : time(time), id(id) {} + time_t time; + u64 id; + bool operator<(const FloodEvt& e) const { + return time > e.time; // Max priority queue. + } + }; + std::unordered_map triesCount; + std::priority_queue toFlood; + Bytes data; + u64 datId; + u32 seqno; + Protocol* proto; +}; +