📄 btcontent.cpp
字号:
}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);}void btContent::FlushCache(size_t idx){ BTCACHE *p; p = m_cache[idx]; if( idx == m_npieces ) p = m_cache_oldest; for( ; p; p = (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 ){ if( m_btfiles.IO(p->bc_buf, p->bc_off, p->bc_len, 1) < 0 ){ 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 can also be caused by a disk error."); CONSOLE.Warning(1, "Temporarily%s suspending download...", (!m_flush_failed && m_cache_size > cfg_cache_size*1024*1024) ? " increasing cache size and" : ""); m_flush_failed = now; WORLD.SeedOnly(1); } return; }else{ p->bc_f_flush = 0; if(m_flush_failed){ CONSOLE.Warning(3, "Flushing cache succeeded, resuming download"); m_flush_failed = 0; CacheConfigure(); WORLD.SeedOnly(0); } } } }else if( idx < p->bc_off / m_piece_length ) break; }}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;}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 && 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; } if( idx < m_npieces ){ // Don't use the cache for this (looks a bit ugly but helps performance). size_t tmp_cache_size = m_cache_size; m_cache_size = 0; int r = GetHashValue(idx, md); m_cache_size = tmp_cache_size; if( r < 0 ) return -1; pBChecked->Set(idx); // need to set before CheckInterest below if( memcmp(md, m_hash_table + idx * 20, 20) == 0 ){ m_left_bytes -= GetPieceLength(idx); pBF->Set(idx); WORLD.Tell_World_I_Have(idx); if( pBF->IsFull() ){ WORLD.CloseAllConnectionToSeed(); } } else f_checkint = 1; m_check_piece = idx + 1; } if( f_checkint ) WORLD.CheckInterest(); if( m_check_piece >= m_npieces ){ CONSOLE.Print("Checking completed."); m_btfiles.PrintOut(); // show file completion delete pBRefer; } return 0;}char* btContent::_file2mem(const char *fname, size_t *psiz){ char *b = (char*) 0; struct stat sb; FILE* fp; fp = fopen(fname,"r"); if( !fp ){ CONSOLE.Warning(1, "error, open %s failed. %s",fname,strerror(errno)); return (char*) 0; } if(stat(fname,&sb) < 0){ CONSOLE.Warning(1, "error, stat %s failed, %s",fname,strerror(errno)); return (char*) 0; } if( sb.st_size > MAX_METAINFO_FILESIZ ){ CONSOLE.Warning(1, "error, %s is really a metainfo file???",fname); return (char*) 0; } b = new char[sb.st_size];#ifndef WINDOWS if( !b ) return (char*) 0;#endif if(fread(b, sb.st_size, 1, fp) != 1){ if( ferror(fp) ){ delete []b; return (char*) 0; } } fclose(fp); if(psiz) *psiz = sb.st_size; return b;}int btContent::APieceComplete(size_t idx){ unsigned char md[20]; if(pBF->IsSet(idx)) return 1; if( GetHashValue(idx, md) < 0 ){ Uncache(idx); return -1; } if( memcmp(md,(m_hash_table + idx * 20), 20) != 0 ){ CONSOLE.Warning(3, "warn, piece %d hash check failed.", idx); return 0; } pBF->Set(idx); m_left_bytes -= GetPieceLength(idx); FlushCache(idx); return 1;}int btContent::GetHashValue(size_t idx,unsigned char *md){ if( ReadPiece(global_piece_buffer,idx) < 0 ) return -1; Sha1(global_piece_buffer,GetPieceLength(idx),md); return 0;}// This is btcontent's "IntervalCheck()"int btContent::SeedTimeout(){ uint64_t dl; if( pBF->IsFull() ){ if( !m_seed_timestamp ){ Tracker.Reset(1); Self.ResetDLTimer(); // set/report dl rate = 0 ReleaseHashTable(); m_seed_timestamp = now; FlushCache(); if( Self.TotalDL() > 0 ){ CONSOLE.Print("Download complete."); CONSOLE.Print("Total time used: %ld minutes.", (long)((now - m_start_timestamp) / 60)); } CONSOLE.Print_n("Seed for others %lu hours", (unsigned long)cfg_seed_hours); if( cfg_seed_ratio ) CONSOLE.Print_n(" or to ratio of %.2f", cfg_seed_ratio); CONSOLE.Print(""); }else if( now < m_seed_timestamp ) m_seed_timestamp = now; dl = (Self.TotalDL() > 0) ? Self.TotalDL() : GetTotalFilesLength(); if( (cfg_seed_ratio == 0 && cfg_seed_hours == 0) || (cfg_seed_hours > 0 && (now - m_seed_timestamp) >= (cfg_seed_hours * 60 * 60)) || (cfg_seed_ratio > 0 && cfg_seed_ratio <= (double) Self.TotalUL() / dl) ) return 1; } if( cfg_cache_size && now >= m_cache_eval_time ) CacheEval(); return 0;}size_t btContent::getFilePieces(size_t nfile){ return m_btfiles.getFilePieces(nfile);}void btContent::SetFilter(){ FlushCache(); m_btfiles.SetFilter(arg_file_to_download,pBFilter,m_piece_length, 1); WORLD.CheckInterest();}// Note, this functions assumes the program is exiting.void btContent::SaveBitfield(){ if( arg_bitfield_file ){ if( m_check_piece < m_npieces ){ // still checking pBChecked->Invert(); pBRefer->And(*pBChecked); pBF->Comb(*pBRefer); } if( !pBF->IsFull() ) pBF->WriteToFile(arg_bitfield_file); }}void btContent::DumpCache(){ BTCACHE *p = m_cache_oldest; CONSOLE.Debug("CACHE CONTENTS:"); for( ; p; p = p->age_next ){ CONSOLE.Debug(" 0x%llx: %d bytes %sflushed", p->bc_off, (int)(p->bc_len), p->bc_f_flush ? "un" : ""); }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -