/** * 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/partdata.h> #include <hn/log.h> #include <boost/lambda/lambda.hpp> #include <boost/lambda/if.hpp> #include <boost/lambda/bind.hpp> #include <fstream> using namespace boost::lambda; using namespace boost::multi_index; /** * @name Boost.Lambda (bind) placeholder types * * Need to redefine placeholders to avoid conflicting with Boost.Bind * placeholders, defined in hn/osdep.h */ //! @{ boost::lambda::placeholder1_type __1; boost::lambda::placeholder2_type __2; boost::lambda::placeholder3_type __3; //! @} // PartData::UsedRange class // ------------------------- template<typename IterType> PartData::UsedRange::UsedRange(PartData *parent, IterType it) : Range64((*it).begin(), (*it).end()), m_parent(parent), m_chunk(parent->m_chunks.project<ID_Avail>(it)) { parent->m_chunks.get<ID_Avail>().modify( m_chunk, ++bind(&PartData::Chunk::m_useCnt, __1(__1)) ); } PartData::UsedRange::~UsedRange() { m_parent->m_chunks.get<ID_Avail>().modify( m_chunk, --bind(&PartData::Chunk::m_useCnt, __1(__1)) ); } // PartData::Chunk class // --------------------- PartData::Chunk::Chunk(uint64_t begin, uint64_t end, HashBase *hash) : Range64(begin, end), m_hash(hash), m_verified(), m_partial(), m_avail(), m_useCnt() {} // PartData class // -------------- PartData::PartData( uint64_t size, const boost::filesystem::path &loc, const boost::filesystem::path &dest ) : m_size(size), m_location(loc), m_destination(dest) { std::ofstream o(loc.string().c_str()); o.flush(); } PartData::PartData(const boost::filesystem::path &) { //! \todo Implement reloading } void PartData::addSourceMask( uint32_t chunkSize, const std::vector<bool> &chunks ) { CHECK_THROW(chunks.size() == getChunkCount(chunkSize)); checkAddChunkMap(chunkSize); int i = 0; typedef ChunkMap::index<ID_Pos>::type PosIndex; typedef PosIndex::iterator Iter; PosIndex& pi = m_chunks.get<ID_Pos>(); for (Iter j = pi.begin(); j != pi.end(); ++j) { pi.modify(j, bind(&Chunk::m_avail, __1(__1)) += chunks[i++]); } } void PartData::addFullSource(uint32_t chunkSize) { checkAddChunkMap(chunkSize); typedef ChunkMap::index<ID_Pos>::type PosIndex; PosIndex& pi = m_chunks.get<ID_Pos>(); for (PosIndex::iterator i = pi.begin(); i != pi.end(); ++i) { pi.modify(i, ++bind(&Chunk::m_avail, __1(__1))); } } UsedRangePtr PartData::getRange(uint32_t size) { logDebug(boost::format("PartData::getRange(%d)") % size); boost::shared_ptr<PartData::UsedRange> ret; /** * \todo This stuff needs also to work well along with UsedRange concept, which * it doesn't do right now, and is disabled for that reason. Alternative * approach would be to incorporate this functionality to the other search * methods, which are already UsedRange-aware. */ // Round 1: First and last chunks // logDebug("Round 1: First and last chunks."); // if (!isComplete(0, size)) { // if (!m_complete.size()) { // ret.reset(new UsedRange(this, 0, size - 1)); // } else if ((*m_complete.begin()).begin() > size - 1) { // ret.reset(new UsedRange(this, 0, size - 1)); // } else if ((*m_complete.begin()).begin() > 0) { // uint64_t end = (*m_complete.begin()).begin() - 1; // ret.reset(new UsedRange(this, 0, end)); // } // logDebug("Got first."); // } else if (!isComplete(m_size - 1 - size, m_size - 1)) { // if (!m_complete.size()) { // ret.reset(new UsedRange(this, m_size-1-size, m_size-1)); // } else if ((*--m_complete.end()).end() < m_size - 1 - size) { // ret.reset(new UsedRange(this, m_size-1-size, m_size-1)); // } else if ((*--m_complete.end()).end() != m_size - 1) { // uint64_t begin = (*--m_complete.end()).end() + 1; // ret.reset(new UsedRange(this, begin, m_size - 1)); // } // logDebug("Got last."); // } // Round 2: Incomplete chunks if (!ret) { logDebug("Round 2: Incomplete chunks."); typedef ChunkMap::index<ID_Partial>::type PartialIndex; typedef PartialIndex::iterator PIter; PartialIndex &pi = m_chunks.get<ID_Partial>(); std::pair<PIter, PIter> r = pi.range(__1 == true, unbounded); PIter i = pi.end(); for (PIter j = r.first; j != r.second; ++j) { if (!(*j).m_useCnt) { i = j; } } // partial chunk with useCnt == 0 if (i != pi.end()) { ret.reset(new UsedRange(this, i)); logDebug("Got."); } } // Round 3: Lowest available unused chunk if (!ret) { logDebug("Round 3: Lowest available unused chunk."); typedef ChunkMap::index<ID_Avail>::type AvailIndex; typedef AvailIndex::iterator AIter; AvailIndex &ai = m_chunks.get<ID_Avail>(); AIter i = ai.end(); AIter r = ai.upper_bound(0); while (r != ai.end()) { if (!(*r).getUseCnt()) { i = r; break; } ++r; } if (i != ai.end()) { ret.reset(new UsedRange(this, i)); logDebug("Got."); } } // Round 4: Least used chunk if (!ret) { logDebug("Round 4: Least used chunk."); typedef ChunkMap::index<ID_UseCnt>::type UseIndex; typedef UseIndex::iterator UIter; UseIndex &ui = m_chunks.get<ID_UseCnt>(); UIter r = ui.upper_bound(0); if (r != ui.end()) try { logDebug("Reusing existing."); ret = (*r).getUsePtr(); } catch (boost::bad_weak_ptr&) { logDebug("Internal PartData error. Compensating..."); ret.reset(new UsedRange(this, r)); } } CHECK_THROW_MSG(ret, "Failed to generate chunk request."); logDebug(boost::format("Result: %d..%d") % ret->begin() % ret->end()); return ret; } uint32_t PartData::getChunkCount(uint32_t chunkSize) const { return m_size / chunkSize + (m_size % chunkSize ? 0 : 1); } void PartData::checkAddChunkMap(uint32_t cs) { typedef ChunkMap::index<ID_Length>::type::iterator LIter; std::pair<LIter, LIter> ret = m_chunks.get<ID_Length>().equal_range(cs); if (ret.first == m_chunks.get<ID_Length>().end()) { logDebug(boost::format("Adding ChunkSet(%d)") % cs); for (uint32_t i = 0; i < getChunkCount(cs); ++i) { m_chunks.insert(Chunk(i * cs, (i + 1) * cs - 1)); } } } bool PartData::isComplete() const { return m_complete.size() == 1 && !(*m_complete.begin()).begin() && (*m_complete.begin()).end() == m_size - 1; } bool PartData::isComplete(const Range64 &r) const { return m_complete.contains(r); } bool PartData::isComplete(uint64_t begin, uint64_t end) const { return m_complete.contains(begin, end); }