QueryRoutingTable.cpp

Go to the documentation of this file.
00001 /*
00002 
00003 Copyright (C) 2005-2007 by Peter Dimov.
00004 
00005 This file is part of Calitko (http://www.calitko.org).
00006 
00007 Calitko is free software; you can redistribute it and/or modify
00008 it under the terms of the GNU General Public License as published by
00009 the Free Software Foundation; either version 2 of the License, or
00010 (at your option) any later version.
00011 
00012 Calitko is distributed in the hope that it will be useful,
00013 but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 GNU General Public License for more details.
00016 
00017 You should have received a copy of the GNU General Public License
00018 along with Calitko; if not, write to the Free Software
00019 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00020 
00021 */
00022 
00023 #include "Qt.h"
00024 #include "QueryRoutingTable.h"
00025 #include <math.h>
00026 #include "Imports.cpp"
00027 
00028 using namespace Gnutella::PacketProcessing::QueryRouting;
00029 
00030 namespace Gnutella {
00031 namespace PacketProcessing {
00032 namespace QueryRouting {
00033 
00034 static const int A_INT=0x4F1BBCDC;
00035 static const quint8 EntryMask [8]  = { 0x80, 0x40, 0x20, 0x10,
00036                                        0x08, 0x04, 0x02, 0x01};
00037 static const quint8 ValueShift [8] = { 7, 6, 5, 4, 3, 2, 1};
00038 
00039 static bool isPowerOf2 (quint32 size)
00040 {
00041     if( size == 0 ) return false;
00042     return ( (size & size-1) == 0 );
00043 }
00044 
00045 quint8 leftNibbleNonZero (quint8 ch)
00046 {
00047     return ch & 0xF0 != 0;
00048 }
00049 
00050 quint8 rightNibbleNonZero (quint8 ch)
00051 {
00052     return ch & 0x0F != 0;
00053 }
00054 
00055 quint8 checkTwoNibbles(quint8 ch)
00056 {
00057     quint8 changed = 0;
00058     changed |= leftNibbleNonZero (ch);
00059     changed <<= 1;
00060     changed |= rightNibbleNonZero (ch);
00061     return changed;
00062 }
00063 
00064 /* Atul: If entryBits == 1, there is no point
00065    checking each bit ;-). If entryBits == 8,
00066    we can check simply for null char, else
00067    we check the nibbles
00068  */
00069 quint8 getBitsToOr (quint8 ch, quint8 entryBits)
00070 {
00071     Q_ASSERT (entryBits == 1 || entryBits == 4 || entryBits == 8);
00072     if (entryBits == 1)
00073         return ch;
00074     else if (entryBits == 4)
00075         return checkTwoNibbles (ch);
00076     else if (entryBits == 8)
00077         return ch != 0;
00078     return 0; // Must never come here (see precondition) but provide a fallback.
00079 }
00080 
00081 } // QueryRouting
00082 } // PacketProcessing
00083 } // Gnutella
00084 
00085 QueryRoutingTable::QueryRoutingTable()
00086  :  rawTable_(), size_ (0), sizeBits_ (0)
00087 {
00088 }
00089 
00090 QueryRoutingTable::~QueryRoutingTable()
00091 {
00092 }
00093 
00094 void QueryRoutingTable::reset (quint32 size)
00095 {
00096     Q_ASSERT (size >= 8 && isPowerOf2 (size));
00097 
00098     if (size_ != size) {
00099         size_ = size;
00100         rawTable_ = QByteArray (size / 8, 0);
00101         double sizeAsDouble = size;
00102         sizeBits_ = static_cast <quint32> (log (sizeAsDouble) / log (2.0));
00103     } else {
00104         rawTable_.fill (0);
00105     }
00106 }
00107 
00111 /* Atul: applyPatch takes a group of entryBits bits
00112          and checks if any bit is 1 (or the group as a whole
00113          does not evaluate to 0). It collects these flags into
00114          variable changed and XOR's with the source
00115  */
00116 void QueryRoutingTable::applyPatch (QByteArray &patchBuffer, quint8 entryBits)
00117 {
00118     Q_ASSERT (patchBuffer.size() == qint32 (size_ * entryBits / 8));
00119     Q_ASSERT (entryBits == 1 || entryBits == 4 || entryBits == 8);
00120 
00121     int k = 0; /* iterates over all bytes of patchBuffer */
00122     for( int i = 0; i < rawTable_.size(); ++i ) {
00123         quint8 changed = 0;
00124         for (int j = 0; j < entryBits; ++j, ++k) {
00125             int numBits = 8/entryBits;
00126             changed <<= numBits;
00127             changed |= getBitsToOr (patchBuffer[k], entryBits);
00128         }
00129         rawTable_[i] = rawTable_[i] ^ changed;
00130     }
00131 }
00132 
00136 QByteArray QueryRoutingTable::makePatchTo (const QueryRoutingTable &other,
00137                                            quint8 entryBits) const
00138 {
00139     Q_ASSERT (other.size_ == size_);
00140     Q_ASSERT (entryBits == 1 || entryBits == 4 || entryBits == 8);
00141 
00142     QByteArray patchBuffer (size_ * entryBits / 8, 0);
00143 
00144     const quint8 *oldTable = reinterpret_cast <const quint8 *>
00145                                                 (rawTable_.data());
00146     const quint8 *oldTableEnd = oldTable + rawTable_.size();
00147     const quint8 *newTable = reinterpret_cast <const quint8 *>
00148                                                 (other.rawTable_.data());
00149     quint8 *patch = reinterpret_cast <quint8 *> (patchBuffer.data());
00150     quint8 *patchEnd = patch + patchBuffer.size();
00151 
00152     quint8 tmp, set, reset;
00153     quint8 setMask1, setMask2, resetMask1, resetMask2;
00154     quint8 *end;
00155     if (entryBits == 0x01) {
00156         while (oldTable != oldTableEnd)
00157             *patch++ = *oldTable++ ^ *newTable++;
00158     }
00159     if (entryBits == 0x04) {
00160         setMask1 = 0xF0; // -1 => infinity(=2) + -1 = 1 => has entry
00161         resetMask1 = 0x10; // +1 => 1 + 1 = 2(=infinity) => no entry
00162         setMask2 = 0x0F; // -1 => infinity(=2) + -1 = 1 => has entry
00163         resetMask2 = 0x01; // +1 => 1 + 1 = 2(=infinity) => no entry
00164         while (oldTable != oldTableEnd) {
00165             tmp = *oldTable ^ *newTable;
00166             set = tmp & *newTable++;
00167             reset = tmp & *oldTable++;
00168             for (end = patch + 4; patch < end; patch++) {
00169                 *patch |= (set & 0x80) ? setMask1 : 0x00;
00170                 *patch |= (reset & 0x80) ? resetMask1 :0x00;
00171                 *patch |= (set & 0x40) ? setMask2 : 0x00;
00172                 *patch |= (reset & 0x40) ? resetMask2 :0x00;
00173                 set <<= 2; reset <<= 2;
00174             }
00175         }
00176     }
00177     if (entryBits == 0x08) {
00178         setMask1 = 0xFF; // -1 => infinity(=2) + -1 = 1 => has entry
00179         resetMask1 = 0x01; // +1 => 1 + 1 = 2(=infinity) => no entry
00180         while (oldTable != oldTableEnd) {
00181             tmp = *oldTable ^ *newTable;
00182             set = tmp & *newTable++;
00183             reset = tmp & *oldTable++;
00184             for (end = patch + 8; patch < end; patch++) {
00185                 *patch |= (set & 0x80) ? setMask1 : 0x00;
00186                 *patch |= (reset & 0x80) ? resetMask1 :0x00;
00187                 set <<= 1; reset <<= 1;
00188             }
00189         }
00190     }
00191     Q_ASSERT (patch == patchEnd);
00192     return patchBuffer;
00193 }
00194 
00195 void QueryRoutingTable::scaledCopy (const QueryRoutingTable &other)
00196 {
00197     Q_ASSERT (size_ > 1 && other.size_ > 1);
00198 
00199     // ei -> Entry Index => current index in current byte
00200     // ec -> Entry Count => how many entries are grouped together
00201     quint8 *p1 = reinterpret_cast <quint8 *> (rawTable_.data());
00202     const quint8 *e1 = p1 + rawTable_.size();
00203     quint8 ei1 = 0; // Entry index (inside/relative to a byte).
00204     quint32 ec1 = (size_ > other.size_) ? (size_ / other.size_) : (1);
00205 
00206     const quint8 *p2 = reinterpret_cast <const quint8 *>
00207                                             (other.rawTable_.data());
00208     const quint8 *e2 = p2 + other.rawTable_.size();
00209     quint8 ei2 = 0; // Entry index (inside/relative to a byte).
00210     quint32 ec2 = (other.size_ > size_) ? (other.size_ / size_) : (1);
00211 
00212     quint32 i;
00213     quint8 mv; // Minimal value.
00214     while (p1 < e1 && p2 < e2) {
00215         // Take the minimal value in the group from source.
00216         for (mv = 0, i = 0; i < ec2; i++) {
00217             mv |= (*p2 & EntryMask [ei2]) != 0;
00218             ++ei2 %= 8;
00219             if (ei2 == 0) p2++;
00220         }
00221         // Set the value to all entries in the destination group.
00222         for (i = 0; i < ec1; i++) {
00223             *p1 &= ~EntryMask [ei1];
00224             *p1 |= mv << ValueShift [ei1];
00225             ++ei1 %= 8;
00226             if (ei1 == 0) p1++;
00227         }
00228     }
00229     // Both tables must have been comletely iterated:
00230     Q_ASSERT (p1 == e1 && p2 == e2);
00231 }
00232 
00233 void QueryRoutingTable::addTable (const QueryRoutingTable &other)
00234 {
00235     Q_ASSERT (size_ > 1 && other.size_ > 1);
00236 
00237     quint8 *p1 = reinterpret_cast <quint8 *> (rawTable_.data());
00238     const quint8 *e1 = p1 + rawTable_.size();
00239     const quint8 *p2 = reinterpret_cast <const quint8 *>
00240                                             (other.rawTable_.data());
00241     const quint8 *e2 = p2 + other.rawTable_.size();
00242 
00243     if (rawTable_.size() == other.rawTable_.size()) {
00244         while (p1 != e1)
00245             *p1++ |= *p2++;
00246     } else {
00247         // ei -> Entry Index => current index in current byte
00248         // ec -> Entry Count => how many entries are grouped together
00249         quint8 ei1 = 0;
00250         quint32 ec1 = (size_ > other.size_) ? (size_ / other.size_) : (1);
00251         quint8 ei2 = 0;
00252         quint32 ec2 = (other.size_ > size_) ? (other.size_ / size_) : (1);
00253 
00254         quint32 i;
00255         quint8 mv; // Minimal value.
00256         while (p1 < e1 && p2 < e2) {
00257             // Take the minimal value in the group from source.
00258             for (mv = 0, i = 0; i < ec2; i++) {
00259                 mv |= (*p2 & EntryMask [ei2]) != 0;
00260                 ++ei2 %= 8;
00261                 if (ei2 == 0) p2++;
00262             }
00263             // Or the value to all entries in the destination group.
00264             for (i = 0; i < ec1; i++) {
00265                 *p1 |= mv << ValueShift [ei1];
00266                 ++ei1 %= 8;
00267                 if (ei1 == 0) p1++;
00268             }
00269         }
00270     }
00271     // Both tables must have been comletely iterated:
00272     Q_ASSERT (p1 == e1 && p2 == e2);
00273 }
00274 
00275 bool QueryRoutingTable::isEntrySet (quint32 index) const
00276 {
00277     Q_ASSERT (index < size_);
00278 
00279     quint32 byteIndex = index / 8;
00280     quint32 bitIndex = index % 8;
00281     quint8 byte = static_cast <quint8> (rawTable_.at (byteIndex));
00282 
00283     return (byte & (0x80 >> bitIndex)) != 0;
00284 }
00285 
00286 void QueryRoutingTable::setEntry (quint32 index)
00287 {
00288     Q_ASSERT (index < size_);
00289 
00290     quint32 byteIndex = index / 8;
00291     quint32 bitIndex = index % 8;
00292     quint8 *byte = reinterpret_cast <quint8 *> (rawTable_.data()) + byteIndex;
00293 
00294     *byte |= (0x80 >> bitIndex);
00295 }
00296 
00297 void QueryRoutingTable::resetEntry (quint32 index)
00298 {
00299     Q_ASSERT (index < size_);
00300 
00301     quint32 byteIndex = index / 8;
00302     quint32 bitIndex = index % 8;
00303     quint8 *byte = reinterpret_cast <quint8 *> (rawTable_.data()) + byteIndex;
00304 
00305     *byte &= ~(0x80 >> bitIndex);
00306 }
00307 
00308 bool QueryRoutingTable::hasString (QString x) const
00309 {
00310     Q_ASSERT (size_ > 0);
00311     quint32 index = hash (x,0,x.length(), sizeBits_);
00312     return isEntrySet (index);
00313 }
00314 
00315 void QueryRoutingTable::add (QString x)
00316 {
00317     Q_ASSERT (size_ > 0);
00318     quint32 index = hash (x, 0, x.length(), sizeBits_); // % p.tableSize;
00319     setEntry (index);
00320 }
00321 
00322 void QueryRoutingTable::remove (QString x)
00323 {
00324     Q_ASSERT (size_ > 0);
00325     quint32 index = hash (x, 0, x.length(), sizeBits_); // % p.tableSize;
00327     resetEntry (index);
00328 }
00329 
00330 int QueryRoutingTable::hash(QString x, int start, int end, quint8 bits) const
00331 {
00332 
00333     //1. First turn x[start...end-1] into a number by treating all 4-byte
00334     //chunks as a little-endian quadword, and XOR'ing the result together.
00335     //We pad x with zeroes as needed.
00336     //    To avoid having do deal with special cases, we do this by XOR'ing
00337     //a rolling value one byte at a time, taking advantage of the fact that
00338     //x XOR 0==x.
00339 
00340     int _xor=0;  //the running total
00341     int j=0;    //the byte position in xor.  INVARIANT: j==(i-start)%4
00342     for (int i=start; i<end; i++) {
00343 //        int b=Character.toLowerCase(x.charAt(i)) & 0xFF;
00344         int b = x.at(i).toLower().toAscii() & 0xFF;
00345         b=b<<(j*8);
00346         _xor=_xor^b;
00347         j=(j+1)%4;
00348     }
00349     //2. Now map number to range 0 - (2^bits-1).
00350     return hashFast(_xor, bits);
00351 //    return xor % (0x00000001 << (bits-1));
00352 }
00353 
00354 int QueryRoutingTable::hashFast(int x, quint8 bits) const
00355 {
00356     //Multiplication-based hash function.  See Chapter 12.3.2. of CLR.
00357     quint64 prod = static_cast <quint64> (x) * static_cast <quint64> (A_INT);
00358     quint64 ret = prod << 32;
00359     ret = ret >> (32 + (32 - bits));
00360     return static_cast <int> (ret);
00361 }