/* Web Polygraph http://www.web-polygraph.org/ * (C) 2003-2006 The Measurement Factory * Licensed under the Apache License, Version 2.0 */ #include "base/polygraph.h" #include "client/ConnHash.h" #include "xstd/gadgets.h" ObjFarm ConnHash::TheItems; ObjFarm ConnHash::TheQueueItems; /* ConnHashItem */ ConnHashItem::ConnHashItem(): theCollision(0) { } void ConnHashItem::reset() { theCollision = 0; } bool ConnHashItem::vacant() const { return theQueue.count() == 0; } bool ConnHashItem::matches(const NetAddr &hopAddr, const NetAddr &tunnelAddr) const { Assert(theQueue.count()); const Connection *conn = theQueue.firstOut()->conn; // if tunnelAddr is undefined, check that connection tunnel end is as well return conn->raddr() == hopAddr && conn->tunnelEnd() == tunnelAddr; } /* ConnHash */ ConnHash::ConnHash(int anCcAddrMax) { if (anCcAddrMax) ccAddrMax(anCcAddrMax); } void ConnHash::ccAddrMax(int aMax) { theHash.stretch(Max(aMax|1, 7)); theHash.count(theHash.capacity()); TheItems.limit(1024); // magic, no good way to estimate TheQueueItems.limit(1024); // magic, no good way to estimate } int ConnHash::hash(const NetAddr &addr) const { return addr.addrN().hash0() % theHash.count(); } bool ConnHash::find(const NetAddr &hopAddr, const NetAddr &tunnelAddr, ConnHashPos &pos) { const int idx = hash(hopAddr); pos = &theHash[idx]; while (ConnHashItem *hi = *pos) { if (hi->matches(hopAddr, tunnelAddr)) return true; pos = hi->nextPos(); } return false; } bool ConnHash::find(const NetAddr &hostAddr, const NetAddr &tunnelAddr) { ConnHashPos pos; return find(hostAddr, tunnelAddr, pos); } void ConnHash::add(Connection *conn) { ConnHashPos pos; if (!find(conn->raddr(), conn->tunnelEnd(), pos)) *pos = TheItems.get(); ConnHashQueueItem *qi = TheQueueItems.get(); qi->conn = conn; (*pos)->queue().enqueue(qi); } void ConnHash::delAt(ConnHashPos pos, ConnHashQueueItem *qi) { TheQueueItems.put(qi); ConnHashItem *hi = *pos; if (hi->vacant()) { *pos = hi->collision(); TheItems.put(hi); } } Connection *ConnHash::delAt(ConnHashPos pos) { Assert(*pos); ConnHashItem *hi = *pos; ConnHashQueueItem *qi = hi->queue().dequeue(hi->queue().lastIn()); // LIFO Connection *conn = qi->conn; delAt(pos, qi); return conn; } void ConnHash::del(Connection *conn) { ConnHashPos pos; Assert(find(conn->raddr(), conn->tunnelEnd(), pos)); ConnHashItem *hi = *pos; ConnHashQueueItem *qhead = hi->queue().firstOut(); ConnHashQueueItem *qi = qhead; // XXX: this loop may be expensive if queues get long // perhaps merge ConnHashQueueItem with Connection somehow? do { if (qi->conn == conn) { hi->queue().dequeue(qi); delAt(pos, qi); return; } qi = (ConnHashQueueItem*)qi->next(); } while (qi != qhead); Assert(false); }