clientext.h

Go to the documentation of this file.
00001 /**
00002  *  Copyright (C) 2004-2005 Alo Sarv <madcat_@users.sourceforge.net>
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License
00015  *  along with this program; if not, write to the Free Software
00016  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00017  */
00018 
00019 /** \file clientext.h Extension classes for Client object */
00020 
00021 #ifndef __CLIENTEXT_H__
00022 #define __CLIENTEXT_H__
00023 
00024 #include <hn/hnfwd.h>
00025 #include <hn/hash.h>
00026 #include <hn/range.h>
00027 #include <boost/tuple/tuple.hpp>
00028 #include "ed2kfwd.h"
00029 #include "downloadlist.h"
00030 
00031 namespace Detail {
00032         //! PartMaps are shared between SourceInfo and DownloadInfo
00033         typedef boost::shared_ptr<std::vector<bool> > PartMapPtr;
00034 
00035 
00036 /**
00037  * Base class for "Client Extensions" - state-dependant parts of Client
00038  * constructed as needed; Encapsulates m_parent pointer, which the extension
00039  * can use to access the parent object. This is mainly used for trace logging,
00040  * but also for Download::addSource() calls.
00041  */
00042 class ClientExtBase {
00043 public:
00044         ClientExtBase(Client *parent) : m_parent(parent) {}
00045 protected:
00046         Client *m_parent;
00047 private:
00048         ClientExtBase();
00049         ClientExtBase(const ClientExtBase&);
00050         ClientExtBase& operator=(const ClientExtBase&);
00051 };
00052 
00053 /**
00054  * UploadInfo object is a part of Client object that represents the upload
00055  * state. Each uploading client is required to have this object as member. This
00056  * object contains all the information needed to perform actual uploading to the
00057  * remote client, e.g. data buffers et al.
00058  */
00059 class UploadInfo : ClientExtBase {
00060 public:
00061         UploadInfo(Client *parent, QueueInfoPtr nfo);
00062         ~UploadInfo();
00063 
00064         /**
00065          * @name Accessors for public usage
00066          */
00067         //@{
00068         SharedFile*    getReqFile()   const { return m_reqFile;          }
00069         Hash<ED2KHash> getReqHash()   const { return m_reqHash;          }
00070         uint32_t       getSent()      const { return m_sent;             }
00071         bool           isCompressed() const { return m_compressed;       }
00072         bool           hasBuffered()  const { return m_buffer.size();    }
00073         uint8_t   getReqChunkCount()  const { return m_reqChunks.size(); }
00074 
00075         void setReqFile(SharedFile *sf)          { m_reqFile = sf;     }
00076         void setReqHash(const Hash<ED2KHash> &h) { m_reqHash = h;      }
00077         void addReqChunk(Range32 r);
00078         //@}
00079 
00080         /**
00081          * Reads first chunk in m_reqChunks into m_buffer from disk
00082          */
00083         void bufferData();
00084 
00085         /**
00086          * Attempts to compress the current m_buffer.
00087          *
00088          * @return true if compression succeeded, false otherwise
00089          */
00090         bool compress();
00091 
00092         /**
00093          * Get next data chunk from m_buffer. The requested data is
00094          * deleted from buffer.
00095          *
00096          * @param amount     Amount of data to retrieve
00097          * @return           Three values, where first integer is the begin
00098          *                   offset of the data, second value is either end
00099          *                   offset (in case of uncompressed data), or the size
00100          *                   of total compressed chunk, and string containing
00101          *                   the actual data.
00102          *
00103          * \note This function may return less data than requested if there was
00104          *       not enough data in m_buffer.
00105          */
00106         boost::tuple<uint32_t, uint32_t, std::string> getNext(uint32_t amount);
00107 
00108         //! \returns Number of UploadInfo objects alive
00109         static size_t count();
00110 private:
00111         //! Requested file
00112         SharedFile *m_reqFile;
00113 
00114         //! The hash of the requested file
00115         Hash<ED2KHash> m_reqHash;
00116 
00117         //! Requested ranges
00118         std::list<Range32> m_reqChunks;
00119 
00120         /**
00121          * Data to be sent to this client, buffered. This data may be compressed
00122          * if the remote client supports compression. Also, the size of this
00123          * buffer may be up to 180k when upload has just started, so this is
00124          * quite memory-heavy.
00125          */
00126         std::string m_buffer;
00127 
00128         /**
00129          * If the client is uploading, this is the current uploading position.
00130          */
00131         uint32_t m_curPos;
00132 
00133         /**
00134          * End position of current buffered data. Notice that this may instead
00135          * indicate the size of compressed chunk, instead of the end offset, if
00136          * the buffer is compressed. This because of eMule extended protocol
00137          * system, refer to Packets::PackedChunk packet implementation for more
00138          * information.
00139          */
00140         uint32_t m_endPos;
00141 
00142         /**
00143          * Whether current buffer is compressed or not.
00144          */
00145         bool m_compressed;
00146 
00147         /**
00148          * How much data (excluding overhead) have we sent to this client during
00149          * this session. We should send 9.28mb to every client and then
00150          * terminate the uploading so others can also get data.
00151          *
00152          * This counter thus starts at 0, updated at every getNext() call, and
00153          * if reaches 9.28mb, the object should be destroyed.
00154          */
00155         uint32_t m_sent;
00156 };
00157 
00158 /**
00159  * QueueInfo object is part of Client object that represents the queue state.
00160  * This object is an extension of the Client object and contains data members
00161  * related to queued client only. Each client in waiting queue must have this as
00162  * member object.
00163  */
00164 class QueueInfo : ClientExtBase {
00165 public:
00166         QueueInfo(
00167                 Client *parent, SharedFile *sf, const Hash<ED2KHash> &h
00168         );
00169         QueueInfo(Client *parent, UploadInfoPtr nfo);
00170         ~QueueInfo();
00171 
00172         /**
00173          * @name Accessors for public usage
00174          */
00175         //!@{
00176         uint32_t       getQR()         const { return m_queueRanking;  }
00177         SharedFile*    getReqFile()    const { return m_reqFile;       }
00178         Hash<ED2KHash> getReqHash()    const { return m_reqHash;       }
00179         uint64_t    getWaitStartTime() const { return m_waitStartTime; }
00180         uint64_t   getLastQueueReask() const { return m_lastQueueReask;}
00181 
00182         void setQR(uint32_t r)                   { m_queueRanking = r; }
00183         void setReqFile(SharedFile *sf, const Hash<ED2KHash> &h) {
00184                 m_reqFile = sf;
00185                 m_reqHash = h;
00186         }
00187         void resetWaitTime()     { m_waitStartTime = Utils::getTick(); }
00188         //!@}
00189 
00190         //! \returns Number of QueueInfo objects alive
00191         static size_t count();
00192 private:
00193         friend class ::Client;
00194 
00195         /**
00196          * Queue ranking. 0 means we are currently uploading to the client.
00197          * Nonzero means we are in waiting queue, on that specific position.
00198          * This member is updated by ClientList class during queue resorting.
00199          */
00200         uint32_t m_queueRanking;
00201 
00202         /**
00203          * The requested file. eMule-compatible clients may change this while
00204          * waiting in the queue.
00205          */
00206         SharedFile *m_reqFile;
00207 
00208         /**
00209          * Hash of the requested file; stored here to lower MetaDb lookups
00210          * during reasks
00211          */
00212         Hash<ED2KHash> m_reqHash;
00213 
00214         /**
00215          * Tick when the client entered queue. This is set to current tick when
00216          * the object is constructed, and is used in queue score calculations
00217          */
00218         uint64_t m_waitStartTime;
00219 
00220         /**
00221          * Last time this client asked US about QR. If this is longer than 1 hour
00222          * from current tick, the client will be dropped from queue during next
00223          * queue update.
00224          */
00225         uint64_t m_lastQueueReask;
00226 };
00227 
00228 /**
00229  * DownloadInfo object controls downloading data from the remote client. It is
00230  * driven by Client object, and instanciated when we can start downloading from
00231  * the remote client. As such, it's purpose is to act as a layer between Client
00232  * (which only performs basic packet handling), and actual data processing
00233  * (which is handled by PartData class internally). This class is also
00234  * responsible for generating chunk requests for asking from the remote client.
00235  */
00236 class DownloadInfo : ClientExtBase {
00237 public:
00238         DownloadInfo(Client *parent, PartData *pd, PartMapPtr partMap);
00239         ~DownloadInfo();
00240 
00241         PartData* getReqPD() const { return m_reqPD; }
00242 
00243         /**
00244          * Generate three chunk requests to be downloaded from this client. Call
00245          * this method AFTER selecting a part using selectPart() method.
00246          *
00247          * Note that in ed2k network, chunk requests are used in overlapping
00248          * manner, e.g. each next chunk request contains two previous ranges
00249          * plus one new range, and a new chunk request is sent after each chunk
00250          * is downloaded. This method takes care of these details, and returns
00251          * three chunk requests ready to be sent out to the uploader.
00252          *
00253          * @return    Up to three requested chunks of size <= 180kb each
00254          */
00255         std::list<Range32> getChunkReqs();
00256 
00257         /**
00258          * Write data to the underlying temp file.
00259          *
00260          * @param r       Range to write to
00261          * @param data    Data to be written
00262          * @return        True if chunk was completed, and
00263          *                getReqChunks() must be called again.
00264          */
00265         bool write(Range32 r, const std::string &data);
00266 
00267         /**
00268          * Write packed data.
00269          *
00270          * @param begin   Begin offset of the packed data.
00271          * @param size    Total size of packed data, e.g. at what point we
00272          *                consider the data buffer "complete".
00273          * @param data    The data itself
00274          * @return        True if chunk was completed, and getReqChunks() must
00275          *                be called again.
00276          */
00277         bool writePacked(uint32_t begin, uint32_t size,const std::string &data);
00278 
00279         //! \returns Number of DownloadInfo objects alive
00280         static size_t count();
00281 private:
00282         typedef std::list<LockedRangePtr>::iterator Iter;
00283 
00284         PartData         *m_reqPD;   //!< Requested file
00285 
00286         //!< The chunks the remote party has of this file
00287         PartMapPtr m_partMap;
00288 
00289         UsedRangePtr m_curPart;      //!< Current active <=9500kb part
00290 
00291         //! Currently locked up to 3 chunks
00292         std::list<LockedRangePtr> m_reqChunks;
00293 
00294         std::string m_packedBuffer; //!< Internal buffer for packed data
00295         uint32_t m_packedBegin;     //!< Begin offset for packed data
00296 
00297         //! How much data we have received during this download session
00298         uint32_t m_received;
00299 };
00300 
00301 /**
00302  * SourceInfo indicates a client which has one (or more) files to offer
00303  * to us. SourceInfo object remains alive until the client has some
00304  * files to offer us, e.g. until m_offered set size drops to zero.
00305  * SourceInfo object may co-exist with DownloadInfo object in same
00306  * Client object.
00307  */
00308 class SourceInfo : ClientExtBase {
00309 public:
00310         /**
00311          * Construct new SourceInfo object.
00312          *
00313          * @param parent     Parent Client object
00314          * @param file       The file this client offers
00315          */
00316         SourceInfo(Client *parent, Download *file);
00317 
00318         //! Destructor; disconnects m_sig if connected
00319         ~SourceInfo();
00320 
00321         /**
00322          * @name Generic accessors
00323          */
00324         //@{
00325         uint32_t       getOffCount()    const { return m_offered.size();   }
00326         Download*      getReqFile()     const { return m_reqFile;          }
00327         PartMapPtr     getPartMap()    const { return m_partMap;       }
00328         uint32_t       getQR()         const { return m_qr;            }
00329         bool           hasNeededParts() const { return m_needParts;    }
00330         bool           isFullSource()   const { return !m_partMap->size(); }
00331         uint64_t       getLastSrcExch() const { return m_lastSrcExch;      }
00332 
00333         void setQR(uint32_t qr)              { m_qr = qr;              }
00334         void setPartMap(const std::vector<bool> &pm);
00335         void setReqFile(Download *file);
00336         void addOffered(Download *file);
00337 
00338         /**
00339          * Remove an offered file from this source.
00340          *
00341          * @param file        File this source is no longer offering
00342          * @param cleanUp     If true, also clean up our stuff from PartData
00343          */
00344         void remOffered(Download *file, bool cleanUp = true);
00345 
00346         //! @returns true if *this offers @param file
00347         bool offers(Download *file) const {
00348                 return m_offered.find(file) != m_offered.end();
00349         }
00350         //@}
00351 
00352         //! \returns Number of SourceInfo objects alive
00353         static size_t count();
00354 private:
00355         /**
00356          * Checks if we need parts from this client, and sets m_needParts
00357          * variable accordingly. Assumes m_pm contains the remote client's
00358          * offered parts map.
00359          */
00360         void checkNeedParts();
00361 
00362         std::set<Download*> m_offered; //!< Offered files
00363         Download*      m_reqFile;      //!< Currently requested file
00364         PartMapPtr     m_partMap;      //!< Chunks the client has
00365         uint32_t       m_qr;           //!< Queue rank; Note: QR 0 == QueueFull
00366         bool           m_needParts;    //!< If we need smth
00367         uint64_t       m_lastSrcExch;  //!< Last time we did SourceExchange
00368 };
00369 
00370 } // namespace Detail
00371 
00372 #endif