void file_storage::optimize(int pad_file_limit) { // the main purpuse of padding is to optimize disk // I/O. This is a conservative memory page size assumption int alignment = 8*1024; // it doesn't make any sense to pad files that // are smaller than one piece if (pad_file_limit >= 0 && pad_file_limit < alignment) pad_file_limit = alignment; size_type off = 0; int padding_file = 0; for (std::vector<internal_file_entry>::iterator i = m_files.begin(); i != m_files.end(); ++i) { if ((off & (alignment-1)) == 0) { // this file position is aligned, pick the largest // available file to put here std::vector<internal_file_entry>::iterator best_match = std::max_element(i, m_files.end() , &compare_file_entry_size); if (best_match != i) { int index = file_index(*best_match); int cur_index = file_index(*i); reorder_file(index, cur_index); i = m_files.begin() + cur_index; } } else if (pad_file_limit >= 0 && (off & (alignment-1)) != 0 && i->size > pad_file_limit && i->pad_file == false) { // if we have pad files enabled, and this file is // not piece-aligned and the file size exceeds the // limit, and it's not a padding file itself. // so add a padding file in front of it int pad_size = alignment - (off & (alignment-1)); // find the largest file that fits in pad_size std::vector<internal_file_entry>::iterator best_match = m_files.end(); for (std::vector<internal_file_entry>::iterator j = i+1; j < m_files.end(); ++j) { if (j->size > pad_size) continue; if (best_match == m_files.end() || j->size > best_match->size) best_match = j; } if (best_match != m_files.end()) { // we found one // We cannot have found i, because i->size > pad_file_limit // which is forced to be no less than alignment. We only // look for files <= pad_size, which never is greater than // alignment TORRENT_ASSERT(best_match != i); int index = file_index(*best_match); int cur_index = file_index(*i); reorder_file(index, cur_index); i = m_files.begin() + cur_index; i->offset = off; off += i->size; continue; } // we could not find a file that fits in pad_size // add a padding file // note that i will be set to point to the // new pad file. Once we're done adding it, we need // to increment i to point to the current file again internal_file_entry e; i = m_files.insert(i, e); i->size = pad_size; i->offset = off; char name[30]; snprintf(name, sizeof(name), ".____padding_file/%d", padding_file); std::string path = combine_path(m_name, name); i->set_name(path.c_str()); i->pad_file = true; off += pad_size; ++padding_file; // skip the pad file we just added and point // at the current file again ++i; } i->offset = off; off += i->size; } m_total_size = off; }
void file_storage::optimize(int pad_file_limit, int alignment) { // it doesn't make any sense to pad files that // are smaller than one block if (pad_file_limit >= 0 && pad_file_limit < 0x4000) pad_file_limit = 0x4000; // also, it doesn't make any sense to pad files // that are smaller than the alignment, since they // won't get aligned anyway; they are used as padding if (pad_file_limit >= 0 && pad_file_limit < alignment) pad_file_limit = alignment; size_type off = 0; int padding_file = 0; for (std::vector<internal_file_entry>::iterator i = m_files.begin(); i != m_files.end(); ++i) { if ((off & (alignment-1)) == 0) { // this file position is aligned, pick the largest // available file to put here std::vector<internal_file_entry>::iterator best_match = std::max_element(i, m_files.end() , &compare_file_entry_size); if (best_match != i) { int index = file_index(*best_match); int cur_index = file_index(*i); reorder_file(index, cur_index); i = m_files.begin() + cur_index; } } else if (pad_file_limit >= 0 && i->size > pad_file_limit && i->pad_file == false) { // if we have pad files enabled, and this file is // not piece-aligned and the file size exceeds the // limit, and it's not a padding file itself. // so add a padding file in front of it int pad_size = alignment - (off & (alignment-1)); // find the largest file that fits in pad_size std::vector<internal_file_entry>::iterator best_match = m_files.end(); for (std::vector<internal_file_entry>::iterator j = i+1; j < m_files.end(); ++j) { if (j->size > pad_size) continue; if (best_match == m_files.end() || j->size > best_match->size) best_match = j; } if (best_match != m_files.end()) { // we found one // We cannot have found i, because i->size > pad_file_limit // which is forced to be no less than alignment. We only // look for files <= pad_size, which never is greater than // alignment TORRENT_ASSERT(best_match != i); int index = file_index(*best_match); int cur_index = file_index(*i); reorder_file(index, cur_index); i = m_files.begin() + cur_index; i->offset = off; off += i->size; continue; } // we could not find a file that fits in pad_size // add a padding file // note that i will be set to point to the // new pad file. Once we're done adding it, we need // to increment i to point to the current file again // first add the pad file to the end of the file list // then swap it in place. This minimizes the amount // of copying of internal_file_entry, which is somewhat // expensive (until we have move semantics) int cur_index = file_index(*i); int index = m_files.size(); m_files.push_back(internal_file_entry()); internal_file_entry& e = m_files.back(); // i may have been invalidated, refresh it i = m_files.begin() + cur_index; e.size = pad_size; e.offset = off; char name[30]; snprintf(name, sizeof(name), ".____padding_file/%d", padding_file); std::string path = combine_path(m_name, name); e.set_name(path.c_str()); e.pad_file = true; off += pad_size; ++padding_file; if (!m_mtime.empty()) m_mtime.resize(index + 1, 0); if (!m_file_hashes.empty()) m_file_hashes.resize(index + 1, NULL); if (!m_file_base.empty()) m_file_base.resize(index + 1, 0); reorder_file(index, cur_index); TORRENT_ASSERT((off & (alignment-1)) == 0); continue; } i->offset = off; off += i->size; } m_total_size = off; }