📄 btcontent.cpp
字号:
return retval;}void btContent::CacheClean(size_t need){ BTCACHE *p, *pnext; int f_flush = 0; if( m_flush_failed ) FlushCache(); // try again again: for( p=m_cache_oldest; p && m_cache_size < m_cache_used + need; p=pnext ){ pnext = p->age_next; if( f_flush ) FlushEntry(p); if( !p->bc_f_flush ){ if( p->age_prev ) p->age_prev->age_next = p->age_next; else m_cache_oldest = p->age_next; if( p->age_next ) p->age_next->age_prev = p->age_prev; else m_cache_newest = p->age_prev; if( p->bc_prev ) p->bc_prev->bc_next = p->bc_next; if( p->bc_next ) p->bc_next->bc_prev = p->bc_prev; size_t idx = p->bc_off / m_piece_length; if( p->bc_next && p->bc_next->bc_off / m_piece_length == idx ) m_cache[idx] = p->bc_next; else m_cache[idx] = (BTCACHE *)0; m_cache_used -= p->bc_len; delete []p->bc_buf; delete p; } } if( m_cache_size < m_cache_used + need ){ // still not enough if( m_cache_size < cfg_cache_size*1024*1024 ){ // can alloc more m_cache_size = (m_cache_used + need > cfg_cache_size*1024*1024) ? cfg_cache_size*1024*1024 : (m_cache_used + need); } if( m_cache_size < m_cache_used + need && m_cache_used && !f_flush ){ if(arg_verbose) CONSOLE.Debug("CacheClean flushing to obtain space"); f_flush = 1; goto again; } // else we tried... }}// Don't call this function if cfg_cache_size==0 !void btContent::CacheEval(){ BTCACHE *p = m_cache_oldest; size_t interval; size_t unflushed = 0, dlnext, upadd = 0, upmax = 0, upmin = 0, total; size_t rateup = Self.RateUL(); size_t ratedn = Self.RateDL(); size_t unchoked = WORLD.GetUnchoked(); // Time until next cache size eval: unchoke interval or time to dl a piece. if( ratedn ){ interval = m_piece_length / ratedn; if( interval > WORLD.GetUnchokeInterval() ) interval = WORLD.GetUnchokeInterval(); else if( 0==interval ) interval = 1; }else interval = WORLD.GetUnchokeInterval(); // Download: total unflushed data + data to dl before next eval // Hold the first piece a bit to let uploading begin. if( pBF->IsFull() ) dlnext = 0; else{ if( pBF->Count() < 2 ) unflushed = m_cache_used; else for( ; p; p = p->age_next ) if( p->bc_f_flush ) unflushed += p->bc_len; // Make sure we can read back and check a completed piece. dlnext = ratedn * interval + m_piece_length; // Set a shorter interval if DL cache need is very high. if( ratedn ){ size_t max = (cfg_cache_size*1024*1024 - unflushed - m_piece_length) / ratedn; if( interval > max ) interval = max; } } // Upload: need enough to hold read/dl'd data until it can be sent upmin = DEFAULT_SLICE_SIZE * unchoked; upmax = cfg_cache_size*1024*1024; if( pBF->IsFull() ){ // Seed mode. All cache data is prefetched, and we don't normally need to // keep prefetched data longer than 2.5 unchoke intervals. if( rateup && unchoked ){ // A very slow peer can't possibly benefit from cache--don't grow for it. size_t slowest = (size_t)( 1 + DEFAULT_SLICE_SIZE / ((double)cfg_cache_size*1024*1024 / rateup) ); // Lead cache: data we need to cache to keep the slowest up's data cached // Add a slice per up for timing uncertainty if( slowest = WORLD.GetSlowestUp(slowest) ) upadd = DEFAULT_SLICE_SIZE * ( rateup / slowest + unchoked-1 ); else upadd = DEFAULT_SLICE_SIZE * unchoked; upmin = DEFAULT_SLICE_SIZE * unchoked; upmax = (size_t)( DEFAULT_SLICE_SIZE * (unchoked-1) + rateup * 2.5 * WORLD.GetUnchokeInterval() ); } }else{ if( rateup > ratedn ){ size_t slowest = (size_t)( 1 + cfg_req_slice_size * ((double)ratedn / cfg_cache_size*1024*1024) + DEFAULT_SLICE_SIZE * ((double)rateup / cfg_cache_size*1024*1024) ); if( slowest = WORLD.GetSlowestUp(slowest) ) // lead cache is how much we'll use while uploading a slice to slowest // (default_slice_size / slowest) * (ratedn + rateup) upadd = (size_t)( ((double)DEFAULT_SLICE_SIZE / slowest) * (ratedn + rateup + 1) ); else upadd = m_piece_length * unchoked; } else if( rateup ){ // same as m_piece_length / (cfg_cache_size*1024*1024 / (double)ratedn) size_t slowest = (size_t)( 1 + ratedn * ((double)m_piece_length / (cfg_cache_size*1024*1024)) ); if( slowest = WORLD.GetSlowestUp(slowest) ){ // m_piece_length / (double)slowest * ratedn // optimize, then round up a piece and add a piece upadd = m_piece_length * (ratedn / slowest + 2); }else{ // gimme 10 seconds worth (unchoke interval) // Can't keep pieces in cache long enough to upload them. // Rely on prefetching slices from disk instead. upadd = ratedn * WORLD.GetUnchokeInterval() + DEFAULT_SLICE_SIZE * unchoked; } } } if( upadd < upmin ) upadd = upmin; // Add a slice to round up total = unflushed + dlnext + upadd + cfg_req_slice_size; // Limit to max configured size if( total > cfg_cache_size*1024*1024 ) total = cfg_cache_size*1024*1024; // Don't decrease cache size if flush failed. if( !m_flush_failed || total > m_cache_size ) m_cache_size = total; if(arg_verbose) CONSOLE.Debug("DL need: %dK UL need: %dK Cache: %dK Used: %dK", (int)((unflushed+dlnext)/1024), (int)(upadd/1024), (int)(m_cache_size/1024), (int)(m_cache_used/1024)); m_cache_eval_time = now + interval;}void btContent::CacheConfigure(){ if( cfg_cache_size ){ if( cfg_cache_size > GetTotalFilesLength()/1024/1024 ) cfg_cache_size = (GetTotalFilesLength()+1024*1024-1)/1024/1024; CacheEval(); }else m_cache_size = 0; if( m_cache_size < m_cache_used && !m_flush_failed ) CacheClean(0);}int btContent::NeedFlush() const{ if( m_flush_failed ){ if( now > m_flush_tried ) return 1; }else return m_flushq ? 1 : 0;}void btContent::FlushCache(size_t idx){ BTCACHE *p, *pnext; if( idx >= m_npieces ){ if(arg_verbose) CONSOLE.Debug("Flushing all cache"); if( m_flushq) do{ FlushQueue(); }while( m_flushq && !m_flush_failed ); p = m_cache_oldest; } else p = m_cache[idx]; for( ; p; p = pnext ){ pnext = (idx < m_npieces) ? p->bc_next : p->age_next; if( idx == p->bc_off / m_piece_length || (p->bc_f_flush && idx == m_npieces) || idx == (p->bc_off+p->bc_len-1) / m_piece_length ){ // update the age--flushing the entry or its piece if( m_cache_newest != p ){ if( p->age_prev ) p->age_prev->age_next = p->age_next; else if( p->age_next ) m_cache_oldest = p->age_next; if( p->age_next ) p->age_next->age_prev = p->age_prev; m_cache_newest->age_next = p; p->age_next = (BTCACHE *)0; p->age_prev = m_cache_newest; m_cache_newest = p; } if( p->bc_f_flush ) FlushEntry(p); } }}void btContent::FlushEntry(BTCACHE *p){ if( p->bc_f_flush ){ if( m_btfiles.IO(p->bc_buf, p->bc_off, p->bc_len, 1) < 0 ){ m_flush_tried = now; if( now >= m_flush_failed + 300 ){ if( !m_flush_failed ) m_cache_size += cfg_req_slice_size * WORLD.GetDownloads() * 2; CONSOLE.Warning(1, "warn, write file failed while flushing cache."); CONSOLE.Warning(1, "You need to have at least %llu bytes free on this filesystem!", (unsigned long long)(m_left_bytes + m_cache_used)); CONSOLE.Warning(1, "This could also be caused by a conflict or disk error."); if( !IsFull() || (!m_flush_failed && m_cache_size > cfg_cache_size*1024*1024) ){ CONSOLE.Warning(1, "Temporarily %s%s...", IsFull() ? "" : "suspending download", (!m_flush_failed && m_cache_size > cfg_cache_size*1024*1024) ? (IsFull() ? " and increasing cache" : "increasing cache") : ""); } m_flush_failed = now; WORLD.StopDownload(); } }else{ p->bc_f_flush = 0; if( Seeding() ){ for( size_t n=1; n <= m_btfiles.GetNFiles(); n++ ) m_btfiles.CloseFile(n); // files will reopen read-only } if(m_flush_failed){ m_flush_failed = 0; CONSOLE.Warning(3, "Flushing cache succeeded%s.", Seeding() ? "" : "; resuming download"); CacheConfigure(); WORLD.CheckInterest(); } } }}void btContent::Uncache(size_t idx){ BTCACHE *p, *pnext; p = m_cache[idx]; for( ; p; p = pnext ){ pnext = p->bc_next; if( idx == p->bc_off / m_piece_length ){ if( p->age_prev ) p->age_prev->age_next = p->age_next; else m_cache_oldest = p->age_next; if( p->age_next ) p->age_next->age_prev = p->age_prev; else m_cache_newest = p->age_prev; if( p->bc_prev ) p->bc_prev->bc_next = p->bc_next; if( p->bc_next ) p->bc_next->bc_prev = p->bc_prev; m_cache_used -= p->bc_len; delete []p->bc_buf; delete p; }else break; } m_cache[idx] = (BTCACHE *)0;}void btContent::FlushQueue(){ if( !m_flushq ) return; if(arg_verbose) CONSOLE.Debug("Writing piece #%d to disk", (int)(m_flushq->idx)); FlushCache(m_flushq->idx); if( !m_flush_failed ){ BTFLUSH *goner = m_flushq; m_flushq = m_flushq->next; delete goner; }}ssize_t btContent::WriteSlice(char *buf,size_t idx,size_t off,size_t len){ uint64_t offset = (uint64_t)idx * (uint64_t)m_piece_length + off; //CONSOLE.Debug("Offset-write: %llu - Piece:%lu", // (unsigned long long)offset, (unsigned long)idx); if( !m_cache_size ) return m_btfiles.IO(buf, offset, len, 1); else{ size_t len2; int flg_rescan; BTCACHE *p; p = m_cache[idx]; for( ; p && (offset + len) > p->bc_off && !CACHE_FIT(p,offset,len); p = p->bc_next ); for( ; len && p && CACHE_FIT(p, offset, len); ){ flg_rescan = 0; if( offset < p->bc_off ){ len2 = p->bc_off - offset; if( CacheIO(buf, offset, len2, 1) < 0 ) return -1; flg_rescan = 1; }else{ if( offset > p->bc_off ){ len2 = p->bc_off + p->bc_len - offset; if( len2 > len ) len2 = len; memcpy(p->bc_buf + offset - p->bc_off, buf, len2); p->bc_f_flush = 1; }else{ len2 = (len > p->bc_len) ? p->bc_len : len; memcpy(p->bc_buf, buf, len2); p->bc_f_flush = 1; } // re-received this data, make it new again if( m_cache_newest != p ){ if( p->age_prev ) p->age_prev->age_next = p->age_next; else if( p->age_next ) m_cache_oldest = p->age_next; if( p->age_next ) p->age_next->age_prev = p->age_prev; m_cache_newest->age_next = p; p->age_next = (BTCACHE *)0; p->age_prev = m_cache_newest; m_cache_newest = p; } } buf += len2; offset += len2; len -= len2; if( len ){ if( flg_rescan ){ for( p = m_cache[idx]; p && (offset + len) > p->bc_off && !CACHE_FIT(p,offset,len); p = p->bc_next ); }else{ p = p->bc_next; } } }// end for; if( len ) return CacheIO(buf, offset, len, 1); } return 0;}ssize_t btContent::CacheIO(char *buf, uint64_t off, size_t len, int method){ BTCACHE *p; BTCACHE *pp = (BTCACHE*) 0; BTCACHE *pnew = (BTCACHE*) 0; if(arg_verbose && 0==method) CONSOLE.Debug("Read to %s %d/%d/%d", buf?"buffer":"cache", (int)(off / m_piece_length), (int)(off % m_piece_length), (int)len); if( m_cache_size < m_cache_used + len ) CacheClean(len); // Note, there is no failure code from CacheClean(). If nothing can be done // to increase the cache size, we allocate what we need anyway. if( 0==method && buf && m_btfiles.IO(buf, off, len, method) < 0 ) return -1; pnew = new BTCACHE;#ifndef WINDOWS if( !pnew ) return (method && buf) ? m_btfiles.IO(buf, off, len, method) : 0;#endif pnew->bc_buf = new char[len];#ifndef WINDOWS if( !(pnew->bc_buf) ){ delete pnew; return (method && buf) ? m_btfiles.IO(buf, off, len, method) : 0; }#endif if( buf ) memcpy(pnew->bc_buf, buf, len); else if( 0==method && m_btfiles.IO(pnew->bc_buf, off, len, method) < 0 ) return -1; pnew->bc_off = off; pnew->bc_len = len; pnew->bc_f_flush = method; m_cache_used += len; pnew->age_next = (BTCACHE *)0; if( m_cache_newest ){ pnew->age_prev = m_cache_newest; m_cache_newest->age_next = pnew; }else{ pnew->age_prev = (BTCACHE *)0; m_cache_oldest = pnew; } m_cache_newest = pnew; // find insert point: after pp, before p. size_t idx = off / m_piece_length; p = m_cache[idx]; if( p ) pp = p->bc_prev; for( ; p && off > p->bc_off; pp = p, p = pp->bc_next ); pnew->bc_next = p; pnew->bc_prev = pp; if( pp ) pp->bc_next = pnew; if( p ) p->bc_prev = pnew; if( !m_cache[idx] || off < m_cache[idx]->bc_off ) m_cache[idx] = pnew; return 0;}ssize_t btContent::ReadPiece(char *buf,size_t idx){ return ReadSlice(buf, idx, 0, GetPieceLength(idx));}size_t btContent::GetPieceLength(size_t idx){ return (idx == m_btfiles.GetTotalLength() / m_piece_length) ? (size_t)(m_btfiles.GetTotalLength() % m_piece_length) :m_piece_length;}int btContent::CheckExist(){ size_t idx = 0; size_t percent = GetNPieces() / 100; unsigned char md[20]; if( !percent ) percent = 1; CONSOLE.Interact_n(""); for( ; idx < m_npieces; idx++ ){ if( GetHashValue(idx, md) < 0 ){ CONSOLE.Warning(1, "Error while checking piece %d of %d", (int)idx+1, (int)m_npieces); return -1; } if( memcmp(md, m_hash_table + idx * 20, 20) == 0 ){ m_left_bytes -= GetPieceLength(idx); pBF->Set(idx); } if( idx % percent == 0 || idx == m_npieces-1 ) CONSOLE.InteractU("Check exist: %d/%d", idx+1, m_npieces); } m_check_piece = m_npieces; pBChecked->SetAll(); delete pBRefer; return 0;}int btContent::CheckNextPiece(){ size_t idx = m_check_piece; unsigned char md[20]; int f_checkint = 0; if( idx >= m_npieces ) return 0; if( !pBRefer->IsSet(idx) ){ while( idx < m_npieces && !pBRefer->IsSet(idx) ){ pBChecked->Set(idx); ++idx; } f_checkint = 1; m_check_piece = idx; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -