diff --git a/storage/country.cpp b/storage/country.cpp index 45181b5884..8523347dfd 100644 --- a/storage/country.cpp +++ b/storage/country.cpp @@ -144,11 +144,11 @@ namespace { class DoStoreCountriesSingleMwms { - TCountriesContainer & m_cont; + TCountriesFacade & m_cont; TMapping m_idsMapping; public: - DoStoreCountriesSingleMwms(TCountriesContainer & cont) : m_cont(cont) {} + DoStoreCountriesSingleMwms(TCountriesFacade & cont) : m_cont(cont) {} Country * operator()(TCountryId const & id, uint32_t mapSize, int depth, TCountryId const & parent) { @@ -177,10 +177,10 @@ public: class DoStoreCountriesTwoComponentMwms { - TCountriesContainer & m_cont; + TCountriesFacade & m_cont; public: - DoStoreCountriesTwoComponentMwms(TCountriesContainer & cont) : m_cont(cont) {} + DoStoreCountriesTwoComponentMwms(TCountriesFacade & cont) : m_cont(cont) {} void operator()(string const & file, uint32_t mapSize, uint32_t routingSize, int depth, TCountryId const & parent) @@ -243,7 +243,7 @@ public: }; } // namespace -int64_t LoadCountries(string const & jsonBuffer, TCountriesContainer & countries, TMapping * mapping /* = nullptr */) +int64_t LoadCountries(string const & jsonBuffer, TCountriesFacade & countries, TMapping * mapping /* = nullptr */) { countries.Clear(); diff --git a/storage/country.hpp b/storage/country.hpp index 5a4a9ea3aa..ab9f9e229b 100644 --- a/storage/country.hpp +++ b/storage/country.hpp @@ -2,7 +2,7 @@ #include "storage/country_decl.hpp" #include "storage/index.hpp" -#include "storage/country_tree.hpp" +#include "storage/country_tree_facade.hpp" #include "storage/storage_defines.hpp" #include "platform/local_country_file.hpp" @@ -25,7 +25,7 @@ namespace storage { using TMapping = map; -/// This class keeps all the information about a country in countre tree (TCountriesContainer). +/// This class keeps all the information about a country in countre tree (TCountriesFacade). /// It is guaranteed that every node represent a unique region has a unique |m_name| in country tree. /// If several nodes have the same |m_name| they represent the same region. /// It happends in case of disputed territories. @@ -58,6 +58,7 @@ public: : m_name(name), m_parent(parent) {} bool operator<(Country const & other) const { return Name() < other.Name(); } + bool operator==(Country const & other) const { return Name() == other.Name(); } void SetFile(platform::CountryFile const & file) { m_file = file; } void SetSubtreeAttrs(uint32_t subtreeMwmNumber, size_t subtreeMwmSizeBytes) { @@ -75,9 +76,10 @@ public: }; typedef CountryTree TCountriesContainer; +typedef CountryTreeFacade TCountriesFacade; /// @return version of country file or -1 if error was encountered -int64_t LoadCountries(string const & jsonBuffer, TCountriesContainer & countries, TMapping * mapping = nullptr); +int64_t LoadCountries(string const & jsonBuffer, TCountriesFacade & countries, TMapping * mapping = nullptr); void LoadCountryFile2CountryInfo(string const & jsonBuffer, map & id2info, bool & isSingleMwm); diff --git a/storage/country_tree.hpp b/storage/country_tree.hpp index e08bd4a962..c59b0369b2 100644 --- a/storage/country_tree.hpp +++ b/storage/country_tree.hpp @@ -3,9 +3,12 @@ #include "base/assert.hpp" #include "std/algorithm.hpp" -#include "std/unique_ptr.hpp" +#include "std/shared_ptr.hpp" #include "std/vector.hpp" +template +bool IsEqual(T const & v1, T const & v2) { return !(v1 < v2) && !(v2 < v1); } + /// This class is developed for using in Storage. It's a implementation of a tree. /// It should be filled with AddAtDepth method. /// This class is used in Storage and filled based on countries.txt (countries_migrate.txt). @@ -18,12 +21,14 @@ class CountryTree /// \brief m_children contains all first generation descendants of the node. /// Note. Once created the order of elements of |m_children| should not be changed. /// See implementation of AddAtDepth and Add methods for details. - vector>> m_children; + vector>> m_children; CountryTree * m_parent; - static bool IsEqual(T const & v1, T const & v2) + /// @return reference is valid only up to the next tree structure modification + shared_ptr> Add(T const & value) { - return !(v1 < v2) && !(v2 < v1); + m_children.emplace_back(make_shared>(value, this)); + return m_children.back(); } public: @@ -33,19 +38,13 @@ public: } /// @return reference is valid only up to the next tree structure modification - T const & Value() const - { - return m_value; - } + T const & Value() const { return m_value; } /// @return reference is valid only up to the next tree structure modification - T & Value() - { - return m_value; - } + T & Value() { return m_value; } /// @return reference is valid only up to the next tree structure modification - T & AddAtDepth(int level, T const & value) + shared_ptr> AddAtDepth(int level, T const & value) { CountryTree * node = this; while (level-- > 0 && !node->m_children.empty()) @@ -54,70 +53,10 @@ public: return node->Add(value); } - /// @return reference is valid only up to the next tree structure modification - T & Add(T const & value) - { - m_children.emplace_back(make_unique>(value, this)); - return m_children.back()->Value(); - } - /// Deletes all children and makes tree empty - void Clear() - { - m_children.clear(); - } + void Clear() { m_children.clear(); } - bool operator<(CountryTree const & other) const - { - return Value() < other.Value(); - } - - /// \brief Checks all nodes in tree to find an equal one. If there're several equal nodes - /// returns the first found. - /// \returns a poiter item in the tree if found and nullptr otherwise. - /// @TODO(bykoianko) The complexity of the method is O(n). But the structure (tree) is built on the start of the program - /// and then actively used on run time. This method (and class) should be redesigned to make the function work faster. - /// A hash table is being planned to use. - void Find(T const & value, vector const *> & found) const - { - if (IsEqual(m_value, value)) - found.push_back(this); - for (auto const & child : m_children) - child->Find(value, found); - } - - CountryTree const * const FindFirst(T const & value) const - { - if (IsEqual(m_value, value)) - return this; - - for (auto const & child : m_children) - { - CountryTree const * const found = child->FindFirst(value); - if (found != nullptr) - return found; - } - return nullptr; - } - - /// \brief Find only leaves. - /// \note It's a termprary fucntion for compatablity with old countries.txt. - /// When new countries.txt with unique ids will be added FindLeaf will be removed - /// and Find will be used intead. - /// @TODO(bykoianko) Remove this method on countries.txt update. - CountryTree const * const FindFirstLeaf(T const & value) const - { - if (IsEqual(m_value, value) && m_children.empty()) - return this; // It's a leaf. - - for (auto const & child : m_children) - { - CountryTree const * const found = child->FindFirstLeaf(value); - if (found != nullptr) - return found; - } - return nullptr; - } + bool operator<(CountryTree const & other) const { return Value() < other.Value(); } bool HasParent() const { return m_parent != nullptr; } @@ -133,10 +72,7 @@ public: return *m_children[index]; } - size_t ChildrenCount() const - { - return m_children.size(); - } + size_t ChildrenCount() const { return m_children.size(); } /// \brief Calls functor f for each first generation descendant of the node. template diff --git a/storage/country_tree_facade.hpp b/storage/country_tree_facade.hpp new file mode 100644 index 0000000000..d5ca3368bb --- /dev/null +++ b/storage/country_tree_facade.hpp @@ -0,0 +1,135 @@ +#pragma once + +#include "storage/country_tree.hpp" + +#include "std/algorithm.hpp" +#include "std/unordered_map.hpp" + +template +struct CountryTreeKeyHasher +{ + size_t operator()(K const & k) const { return hash()(k.Name()); } +}; + +template<> +struct CountryTreeKeyHasher +{ + size_t operator()(int k) const { return hash()(k); } +}; + +/// This class is developed for using in Storage. It's a implementation of a tree. +/// It should be filled with AddAtDepth method. +/// This class is used in Storage and filled based on countries.txt (countries_migrate.txt). +/// While filling CountryTree nodes in countries.txt should be visited in DFS order. +template +class CountryTreeFacade +{ + using TCountryTreeHashTable = unordered_multimap>, CountryTreeKeyHasher>; + + CountryTree m_countryTree; + TCountryTreeHashTable m_countryTreeHashTable; + +public: + CountryTreeFacade(T const & value = T(), CountryTree * parent = nullptr) + : m_countryTree(value, parent) {} + + /// @return reference is valid only up to the next tree structure modification + T const & Value() const { return m_countryTree.Value(); } + + /// @return reference is valid only up to the next tree structure modification + T & Value() { return m_countryTree.Value(); } + + /// @return reference is valid only up to the next tree structure modification + T & AddAtDepth(int level, T const & value) + { + shared_ptr> const added = m_countryTree.AddAtDepth(level, value); + m_countryTreeHashTable.insert(make_pair(value, added)); + return added->Value(); + } + + /// Deletes all children and makes tree empty + void Clear() { m_countryTree.Clear(); } + + bool operator<(CountryTree const & other) const { return m_countryTree < other; } + + /// \brief Checks all nodes in tree to find an equal one. If there're several equal nodes + /// returns the first found. + /// \returns a poiter item in the tree if found and nullptr otherwise. + /// @TODO(bykoianko) The complexity of the method is O(n). But the structure (tree) is built on the start of the program + /// and then actively used on run time. This method (and class) should be redesigned to make the function work faster. + /// A hash table is being planned to use. + void Find(T const & value, vector const *> & found) const + { + found.clear(); + + if (IsEqual(value, m_countryTree.Value())) + found.push_back(&m_countryTree); + + auto const range = m_countryTreeHashTable.equal_range(value); + auto const end = m_countryTreeHashTable.end(); + if (range.first == end && range.second == end) + return; + + for_each(range.first, range.second, + [&found](typename TCountryTreeHashTable::value_type const & node) { found.push_back(&*node.second); }); + } + + CountryTree const * const FindFirst(T const & value) const + { + vector const *> found; + Find(value, found); + if (found.empty()) + return nullptr; + return found[0]; + } + + /// \brief Find only leaves. + /// \note It's a termprary fucntion for compatablity with old countries.txt. + /// When new countries.txt with unique ids will be added FindLeaf will be removed + /// and Find will be used intead. + /// @TODO(bykoianko) Remove this method on countries.txt update. + CountryTree const * const FindFirstLeaf(T const & value) const + { + vector const *> found; + Find(value, found); + + for (auto node : found) + if (node->ChildrenCount() == 0) + return node; + return nullptr; + } + + bool HasParent() const { m_countryTree.HasParent(); } + + CountryTree const & Parent() const { return m_countryTree.Parent(); } + + CountryTree const & Child(size_t index) const { return m_countryTree.Child(index); } + + size_t ChildrenCount() const { return m_countryTree.ChildrenCount(); } + + /// \brief Calls functor f for each first generation descendant of the node. + template + void ForEachChild(TFunctor && f) { return m_countryTree.ForEachChild(f); } + + template + void ForEachChild(TFunctor && f) const { return m_countryTree.ForEachChild(f); } + + /// \brief Calls functor f for all nodes (add descendant) in the tree. + template + void ForEachDescendant(TFunctor && f) { return m_countryTree.ForEachDescendant(f); } + + template + void ForEachDescendant(TFunctor && f) const { return m_countryTree.ForEachDescendant(f); } + + template + void ForEachInSubtree(TFunctor && f) { return m_countryTree.ForEachInSubtree(f); } + + template + void ForEachInSubtree(TFunctor && f) const { return m_countryTree.ForEachInSubtree(f); } + + template + void ForEachAncestorExceptForTheRoot(TFunctor && f) { return m_countryTree.ForEachAncestorExceptForTheRoot(f); } + + template + void ForEachAncestorExceptForTheRoot(TFunctor && f) const { return m_countryTree.ForEachAncestorExceptForTheRoot(f); } +}; diff --git a/storage/storage.cpp b/storage/storage.cpp index 7c171e48f5..d6fd62dc25 100644 --- a/storage/storage.cpp +++ b/storage/storage.cpp @@ -76,7 +76,7 @@ void DeleteFromDiskWithIndexes(LocalCountryFile const & localFile, MapOptions op localFile.DeleteFromDisk(options); } -TCountriesContainer const & LeafNodeFromCountryId(TCountriesContainer const & root, +TCountriesContainer const & LeafNodeFromCountryId(TCountriesFacade const & root, TCountryId const & countryId) { CountryTree const * node = root.FindFirstLeaf(Country(countryId)); diff --git a/storage/storage.hpp b/storage/storage.hpp index 2c268fc48f..2e2d58deab 100644 --- a/storage/storage.hpp +++ b/storage/storage.hpp @@ -2,6 +2,7 @@ #include "storage/country.hpp" #include "storage/country_name_getter.hpp" +#include "storage/country_tree_facade.hpp" #include "storage/index.hpp" #include "storage/map_files_downloader.hpp" #include "storage/queued_country.hpp" @@ -97,7 +98,7 @@ private: /// stores timestamp for update checks int64_t m_currentVersion; - TCountriesContainer m_countries; + CountryTreeFacade m_countries; /// @todo. It appeared that our application uses m_queue from /// different threads without any synchronization. To reproduce it diff --git a/storage/storage.pro b/storage/storage.pro index f811485857..87e8f722b4 100644 --- a/storage/storage.pro +++ b/storage/storage.pro @@ -17,6 +17,7 @@ HEADERS += \ country_name_getter.hpp \ country_polygon.hpp \ country_tree.hpp \ + country_tree_facade.hpp \ http_map_files_downloader.hpp \ index.hpp \ map_files_downloader.hpp \