/** * Copyright (C) 2004-2005 Alo Sarv <madcat_@users.sourceforge.net> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <hn/range.h> #include <boost/filesystem/path.hpp> #include <boost/weak_ptr.hpp> #include <boost/shared_ptr.hpp> #include <boost/multi_index_container.hpp> #include <boost/multi_index/key_extractors.hpp> #include <boost/multi_index/ordered_index.hpp> #include <map> #include <list> class SharedFile; class HashBase; class PartData { public: class UsedRange; private: class Chunk : public Range64 { public: Chunk(uint64_t begin, uint64_t end, HashBase *h = 0); //! \name Getters //! @{ bool getVerified() const { return m_verified; } bool getPartial() const { return m_partial; } uint32_t getAvail() const { return m_avail; } uint32_t getUseCnt() const { return m_useCnt; } //! May throw boost::bad_weak_ptr boost::shared_ptr<UsedRange> getUsePtr() const { return boost::shared_ptr<UsedRange>(m_used); } //! @} //! \name Setters //! @{ void setVerified(bool v) { m_verified = v; } void setPartial(bool p) { m_partial = p; } void setAvail(uint32_t a) { m_avail = a; } void setUseCnt(uint32_t u) { m_useCnt = u; } void setUsePtr(boost::weak_ptr<UsedRange> p) { m_used = p; } //! @} //! Need to declare this here too for Boost.MultiIndex to work uint64_t length() const { return Range64::length(); } public: HashBase *m_hash; //!< Optional bool m_verified; //!< Whether it's verified. bool m_partial; //!< Whether it's only partially downloaded uint32_t m_avail; //!< Availability count uint32_t m_useCnt; //!< Use count //! If m_useCnt > 0, this points to the relevant UsedRange boost::weak_ptr<UsedRange> m_used; }; struct ID_Pos; struct ID_Verified; struct ID_Partial; struct ID_Avail; struct ID_UseCnt; struct ID_Length; typedef boost::multi_index_container< Chunk, boost::multi_index::indexed_by< boost::multi_index::ordered_non_unique< boost::multi_index::tag<ID_Pos>, boost::multi_index::identity<Chunk> >, boost::multi_index::ordered_non_unique< boost::multi_index::tag<ID_Verified>, boost::multi_index::member< Chunk, bool, &Chunk::m_verified > >, boost::multi_index::ordered_non_unique< boost::multi_index::tag<ID_Partial>, boost::multi_index::member< Chunk, bool, &Chunk::m_partial > >, boost::multi_index::ordered_non_unique< boost::multi_index::tag<ID_Avail>, boost::multi_index::member< Chunk, uint32_t, &Chunk::m_avail > >, boost::multi_index::ordered_non_unique< boost::multi_index::tag<ID_UseCnt>, boost::multi_index::member< Chunk, uint32_t, &Chunk::m_useCnt > >, boost::multi_index::ordered_non_unique< boost::multi_index::tag<ID_Length>, boost::multi_index::const_mem_fun< Chunk, uint64_t, &Chunk::length > > > > ChunkMap; typedef ChunkMap::index<ID_Pos >::type CMPosIndex; typedef ChunkMap::index<ID_Verified>::type CMVerifiedIndex; typedef ChunkMap::index<ID_Partial >::type CMPartialIndex; typedef ChunkMap::index<ID_Avail >::type CMAvailIndex; typedef ChunkMap::index<ID_UseCnt >::type CMUseIndex; typedef ChunkMap::index<ID_Length >::type CMLenIndex; public: class UsedRange; class LockedRange; /** * \brief Construct a NEW temporary file * * Using this constructor, a new temporary file is constructed, at * specified location with specified size. * * @param size Size of the resulting file. * @param loc Location on disk where to store the temp file * @param dest Destination where to write the complete file * * \note The disk space indicated by @param size is not allocated on * actual disk right away. Instead, the size is allocated * dynamically as the file grows. This can be changed from global * application preferences though. */ PartData( uint64_t size, const boost::filesystem::path &loc, const boost::filesystem::path &dest ); /** * \brief Load a previously constructed temporary file. * * This method can be used to resume a previously started download, by * reading the necessery data from @param loc. * * @param loc Path to PartData reference file, which contains the * data required to resume the download. */ PartData(const boost::filesystem::path &loc); /** * \brief Add an availability chunk mask * * Allows modules to register chunk availability maps, so PartData * can decide the lowest-available chunk to be returned from get* * methods * * @param chunkSize Size of one chunk * @param chunks Boolean vector, where each true indicates * the source having the part, and false the source * not having the part. */ void addSourceMask(uint32_t chunkSize, const std::vector<bool> &chunks); /** * \brief Optimized version of addSourceMask(), adds a full source. * * Similar to addSourceMask(), this adds a source which has the entire * file. * * @param chunkSize Size of one chunk */ void addFullSource(uint32_t chunkSize); /** * Gets a free range that PartData considers most important. First/last * ranges are considered important, as well as rare ranges, and * incomplete ranges. * * @param size Size of the range to be aquired. * @return Pointer to a range marked as 'used'. * * \note The size of the given range my be smaller than was requested. */ boost::shared_ptr<UsedRange> getRange(uint32_t size); //! Write method simply writes data starting at specified offset. If //! there are no locks currently on the range, and the range is indeed //! incomplete, everything works. If something goes wrong, exceptions //! will be generated. void write(uint64_t beginOffset, const std::string &data); //! Check for completeness, either the entire file (used internally), or //! a specific subrange (also used internally). Public only for //! completeness. bool isComplete() const; bool isComplete(const Range64 &subRange) const; bool isComplete(uint64_t begin, uint64_t end) const; uint32_t getChunkCount(uint32_t chunkSize) const; //! UsedRange concept is similar to many thread libraries lock object //! concepts - you retrieve one via get() methods in PartData, and when //! it is destroyed, it takes care that all used/locked ranges do get //! freed properly. This object may only be used when wrapped in //! boost::shared_ptr. class UsedRange : public Range64 { public: std::auto_ptr<LockedRange> getLock(uint32_t size); ~UsedRange(); private: friend class PartData; //! Allowed only by PartData. UsedRange keeps a pointer back to //! its parent object, and also sets up event handers as //! neccesery to ensure the pointer remains valid. //! \note The template may be left undefined here since it's //! only called from inside partdata.cpp template<typename IterType> UsedRange(PartData *parent, IterType it); //! copying is not allowed UsedRange(const UsedRange&); UsedRange& operator=(const UsedRange&); //! Ranges that are locked within this usedrange RangeList64 m_locked; //! Parent PartData PartData *m_parent; //! Chunk this UsedRange refers to CMAvailIndex::iterator m_chunk; }; class LockedRange : public Range64 { public: //! Identical method as in PartData class, this is provided for //! completeness. void write(uint64_t beginOffset, const std::string &data); }; private: friend class SharedFile; friend int test_main(int, char[]); //! Copying part files is not allowed PartData(const PartData&); PartData& operator=(const PartData&); //! Only allowed by SharedFile ~PartData(); void checkAddChunkMap(uint32_t chunkSize); //! All that are complete RangeList64 m_complete; ChunkMap m_chunks; uint64_t m_size; boost::filesystem::path m_location; boost::filesystem::path m_destination; }; typedef boost::shared_ptr<PartData::UsedRange> UsedRangePtr; typedef std::auto_ptr<PartData::LockedRange> LockedRangePtr;