📄 btcontent.cpp
字号:
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( m_cache_oldest == p ) m_cache_oldest = p->age_next; else p->age_prev->age_next = p->age_next; if( m_cache_newest == p ) m_cache_newest = p->age_prev; else p->age_next->age_prev = p->age_prev; m_cache_used -= p->bc_len; delete []p->bc_buf; delete p; } m_cache[idx] = (BTCACHE *)0;}void btContent::FlushQueue(){ if( m_flushq ){ if(arg_verbose) CONSOLE.Debug("Writing piece #%d to disk", (int)(m_flushq->idx)); FlushPiece(m_flushq->idx); if( !m_flush_failed ){ BTFLUSH *goner = m_flushq; m_flushq = m_flushq->next; delete goner; } }else{ if(arg_verbose) CONSOLE.Debug("Flushing %d/%d/%d", (int)(m_cache_oldest->bc_off / m_piece_length), (int)(m_cache_oldest->bc_off % m_piece_length), (int)(m_cache_oldest->bc_len)); FlushEntry(m_cache_oldest); }}/* Prepare for prefetching a whole piece. return -1: do not prefetch (problem or not needed) return 0: already ready (no time used) return 1: data was flushed (time used)*/int btContent::CachePrep(size_t idx){ int retval = 0; BTCACHE *p, *pnext; size_t need = GetPieceLength(idx); if( m_cache_size < m_cache_used + need ){ for( p=m_cache[idx]; p; p=p->bc_next ) need -= p->bc_len; if( 0==need ) retval = -1; // don't need to prefetch for( p=m_cache_oldest; p && m_cache_size < m_cache_used + need; p=pnext ){ pnext = p->age_next; if( p->bc_off / m_piece_length == idx ) continue; if( p->bc_f_flush ){ if(arg_verbose) CONSOLE.Debug("Flushing %d/%d/%d", (int)(p->bc_off / m_piece_length), (int)(p->bc_off % m_piece_length), (int)(p->bc_len)); FlushEntry(p); retval = 1; } if(arg_verbose) CONSOLE.Debug("Expiring %d/%d/%d", (int)(p->bc_off / m_piece_length), (int)(p->bc_off % m_piece_length), (int)(p->bc_len)); if( m_cache_oldest == p ) m_cache_oldest = p->age_next; else p->age_prev->age_next = p->age_next; if( m_cache_newest == p ) m_cache_newest = p->age_prev; else p->age_next->age_prev = p->age_prev; if( p->bc_prev ) p->bc_prev->bc_next = p->bc_next; else m_cache[p->bc_off / m_piece_length] = 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; } } return retval;}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; BTCACHE *p; p = m_cache[idx]; while( len && p ){ while( p && offset + len > p->bc_off && !CACHE_FIT(p, offset, len) ){ p = p->bc_next; } if( !p || !CACHE_FIT(p, offset, len) ) break; if( offset < p->bc_off ){ len2 = p->bc_off - offset; if( CacheIO(buf, offset, len2, 1) < 0 ) return -1; p = m_cache[idx]; // p may not be valid after CacheIO }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); }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( m_cache_oldest == p ) m_cache_oldest = p->age_next; else p->age_prev->age_next = 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; } p = p->bc_next; } buf += len2; offset += len2; len -= len2; }// 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( len >= cfg_cache_size*1024*768 ){ // 75% of cache limit if( buf ) return m_btfiles.IO(buf, off, len, method); else return 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 ){ delete []pnew->bc_buf; delete pnew; 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){ // Slight optimization to avoid division in every call. The second test is // still needed in case the torrent size is exactly n pieces. return (idx == m_npieces - 1 && 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(); 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( pBChecked->IsSet(idx) ){ while( idx < m_npieces && pBChecked->IsSet(idx) ){ if(arg_verbose) CONSOLE.Debug("Check: %u skipped", 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 ){ if(arg_verbose) CONSOLE.Debug("Check: %u ok", idx); m_left_bytes -= GetPieceLength(idx); pBF->Set(idx); WORLD.Tell_World_I_Have(idx); }else{ if(arg_verbose) CONSOLE.Debug("Check: %u failed", idx); f_checkint = 1; } m_check_piece = idx + 1; } if( f_checkint ) WORLD.CheckInterest(); if( m_check_piece >= m_npieces ){ CONSOLE.Print("Checking completed."); if( !pBF->IsEmpty() ) m_btfiles.PrintOut(); // show file completion if( pBF->IsFull() ){ WORLD.CloseAllConnectionToSeed(); } } 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 ){ // error reading data Uncache(idx); return -1; } if( memcmp(md,(m_hash_table + idx * 20), 20) != 0 ){ CONSOLE.Warning(3, "warn, piece %d hash check failed.", idx); Uncache(idx); CountHashFailure(); return 0; } pBF->Set(idx); m_left_bytes -= GetPieceLength(idx); Tracker.CountDL(GetPieceLength(idx)); // Add the completed piece to the flush queue. if( cfg_cache_size ){ if( IsFull() ){ FlushCache(); for( size_t n=1; n <= m_btfiles.GetNFiles(); n++ ) m_btfiles.CloseFile(n); // files will reopen read-only } if( !IsFull() || m_flush_failed ){ BTFLUSH *last = m_flushq; BTFLUSH *node = new BTFLUSH; if( !node ) FlushPiece(idx); else{ node->idx = idx; node->next = (BTFLUSH *)0; if( last ){ for( ; last->next; last = last->next); last->next = node; }else m_flushq = node; } } }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -