diff --git a/src/navigate/CMakeLists.txt b/src/navigate/CMakeLists.txt index c8ebf9c..3f55ff3 100644 --- a/src/navigate/CMakeLists.txt +++ b/src/navigate/CMakeLists.txt @@ -9,6 +9,7 @@ add_executable(${PROJECT_NAME} genericpath.cpp dbsource.cpp linereader.cpp + listdircontent.cpp ) target_include_directories(${PROJECT_NAME} diff --git a/src/navigate/listdircontent.cpp b/src/navigate/listdircontent.cpp new file mode 100644 index 0000000..4bfb050 --- /dev/null +++ b/src/navigate/listdircontent.cpp @@ -0,0 +1,92 @@ +/* Copyright 2015, 2016, Michele Santullo + * This file is part of "dindexer". + * + * "dindexer" 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 3 of the License, or + * (at your option) any later version. + * + * "dindexer" 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 "dindexer". If not, see . + */ + +#include "listdircontent.hpp" +#include "genericpath.hpp" +#include "dbsource.hpp" +#include "helpers/infix_iterator.hpp" +#include +#include +#include +#include +#include +#include +#include + +namespace din { + namespace { + template std::vector db_result_to_vec ( const V& parResult ); + const std::size_t g_max_cached_lists = 4; + + template + std::vector db_result_to_vec (const V& parResult) { + std::vector result; + result.reserve(parResult.size()); + + for (const auto& row : parResult) { + std::ostringstream oss; + boost::copy(row, infix_ostream_iterator(oss, "\t")); + result.push_back(oss.str()); + } + return std::move(result); + } + } //unnamed namespace + + ListDirContent::ListDirContent (DBSource* parDB) : + m_cache(g_max_cached_lists), + m_db(parDB) + { + assert(m_db); + } + + auto ListDirContent::ls (const GenericPath& parDir) const -> const ListType& { + const auto curr_path = parDir.to_string(); + + //Check if requested item is already cached + auto it_found = std::find_if(m_cache.begin(), m_cache.end(), [&curr_path](const CachedItemType& itm) { return itm.first == curr_path; }); + if (it_found != m_cache.end()) { + CachedItemType tmp; + std::swap(tmp, *it_found); + assert(it_found->first.empty()); + assert(it_found->second.empty()); + assert(tmp.first == curr_path); + m_cache.erase(it_found); + assert(m_cache.size() < g_max_cached_lists); + m_cache.push_back(std::move(tmp)); + assert(not m_cache.empty()); + assert(m_cache.back().first == curr_path); + return m_cache.back().second; + } + + //Requested item is not cached, so we need to query the db now + if ("/" == curr_path) { + auto sets_ids = m_db->sets(); + auto sets_info = m_db->set_details(sets_ids); + m_cache.push_back(std::make_pair(curr_path, db_result_to_vec(sets_info))); + } + else { + const auto start_from = curr_path.find('/', 1); + auto path_prefix = boost::string_ref(curr_path).substr(start_from == curr_path.npos ? curr_path.size() : start_from + 1); + const auto set_id = boost::lexical_cast(parDir[0]); + auto files_info = m_db->file_details(set_id, parDir.level(), path_prefix); + m_cache.push_back(std::make_pair(curr_path, db_result_to_vec(files_info))); + } + assert(not m_cache.empty()); + assert(m_cache.back().first == curr_path); + return m_cache.back().second; + } +} //namespace din diff --git a/src/navigate/listdircontent.hpp b/src/navigate/listdircontent.hpp new file mode 100644 index 0000000..87d6bca --- /dev/null +++ b/src/navigate/listdircontent.hpp @@ -0,0 +1,44 @@ +/* Copyright 2015, 2016, Michele Santullo + * This file is part of "dindexer". + * + * "dindexer" 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 3 of the License, or + * (at your option) any later version. + * + * "dindexer" 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 "dindexer". If not, see . + */ + +#ifndef id5FCFB397A56A4D0D99AB6947A08E1075 +#define id5FCFB397A56A4D0D99AB6947A08E1075 + +#include +#include +#include + +namespace din { + class GenericPath; + class DBSource; + + class ListDirContent { + using ListType = std::vector; + using CachedItemType = std::pair; + public: + explicit ListDirContent ( DBSource* parDB ); + ~ListDirContent ( void ) noexcept = default; + + const ListType& ls ( const GenericPath& parDir ) const; + + private: + mutable boost::circular_buffer m_cache; + DBSource* m_db; + }; +} //namespace din + +#endif diff --git a/src/navigate/main.cpp b/src/navigate/main.cpp index f4e9754..b9f3e8b 100644 --- a/src/navigate/main.cpp +++ b/src/navigate/main.cpp @@ -21,23 +21,21 @@ #include "genericpath.hpp" #include "dbsource.hpp" #include "dindexerConfig.h" -#include "helpers/infix_iterator.hpp" #include "linereader.hpp" +#include "listdircontent.hpp" #include #include #include #include #include #include -#include namespace { void do_navigation ( din::DBSource& parDB ); - template void print_db_result ( const V& parResult ); bool on_exit ( void ); void on_pwd ( const din::GenericPath& parDirMan ); - void on_ls ( const din::GenericPath& parDirMan, din::DBSource& parDB ); + void on_ls ( const din::ListDirContent& parLS, const din::GenericPath& parDirMan ); } //unnamed namespace int main (int parArgc, char* parArgv[]) { @@ -70,14 +68,6 @@ int main (int parArgc, char* parArgv[]) { } namespace { - template - void print_db_result (const V& parResult) { - for (const auto& row : parResult) { - boost::copy(row, infix_ostream_iterator(std::cout, "\t")); - std::cout << "\n"; - } - } - bool on_exit() { return true; } @@ -86,22 +76,9 @@ namespace { std::cout << parDirMan.to_string() << '\n'; } - void on_ls (const din::GenericPath& parDirMan, din::DBSource& parDB) { - using namespace din; - - const auto curr_path = parDirMan.to_string(); - if ("/" == curr_path) { - auto sets_ids = parDB.sets(); - auto sets_info = parDB.set_details(sets_ids); - print_db_result(sets_info); - } - else { - const auto start_from = curr_path.find('/', 1); - auto path_prefix = boost::string_ref(curr_path).substr(start_from == curr_path.npos ? curr_path.size() : start_from + 1); - const auto set_id = boost::lexical_cast(parDirMan[0]); - auto files_info = parDB.file_details(set_id, parDirMan.level(), path_prefix); - print_db_result(files_info); - } + void on_ls (const din::ListDirContent& parLS, const din::GenericPath& parDirMan) { + const auto& ls_result = parLS.ls(parDirMan); + boost::copy(ls_result, std::ostream_iterator(std::cout, "\n")); } void do_navigation (din::DBSource& parDB) { @@ -112,11 +89,12 @@ namespace { std::string curr_line; din::CommandProcessor proc; din::GenericPath dir_man; + din::ListDirContent ls(&parDB); proc.add_command("exit", &on_exit, 0); proc.add_command("cd", std::function(std::bind(&din::GenericPath::push_piece, &dir_man, std::placeholders::_1)), 1); proc.add_command("disconnect", std::function(std::bind(&din::DBSource::disconnect, &parDB)), 0); proc.add_command("pwd", std::function(std::bind(&on_pwd, std::ref(dir_man))), 0); - proc.add_command("ls", std::function(std::bind(&on_ls, std::ref(dir_man), std::ref(parDB))), 0); + proc.add_command("ls", std::function(std::bind(on_ls, std::ref(ls), std::ref(dir_man))), 0); do { do { curr_line = lines.read(prompt);