#pragma once #include "tl/detail/prologue.h" #include "tl/vector.h" #include "tl/string.h" #include "tl/hash_and_combine.h" namespace tl { namespace detail { namespace path_system { template struct path_key_less; ////////////////////////////////////////////////////////////////////////// // inner path_base definition template class path_base { public: virtual ~path_base() noexcept = default; using iterator = tl::vector::iterator; using const_iterator = tl::vector::const_iterator; using const_reverse_iterator = tl::vector::const_reverse_iterator; using key_less = path_key_less; using value_type = std::string; //both have checked indexing const string& operator[](size_t index) const noexcept; string& operator[](size_t index) noexcept; const string& at(size_t index) const noexcept; string& at(size_t index) noexcept; void shrink(size_t size) noexcept; bool empty() const noexcept; size_t size() const noexcept; void reserve(size_t capacity) noexcept; // if the path is empty, these operations have no effect. They return the removed element as a string. string pop_back() noexcept; virtual bool push_back(string element) noexcept; virtual void take_elements(tl::vector&& elements) noexcept; iterator begin() noexcept; iterator end() noexcept; const_iterator begin() const noexcept; const_iterator end() const noexcept; const_reverse_iterator rbegin() const noexcept; const_reverse_iterator rend() const noexcept; uint32_t hash() const noexcept; protected: void push_element(string element) noexcept; tl::vector m_elements; mutable uint32_t m_hash = 0; friend bool path_key_less::operator()(const path_base& a1, const path_base& a2) const noexcept; }; ////////////////////////////////////////////////////////////////////////// template struct path_key_less { bool operator()(const path_base& a1, const path_base& a2) const noexcept; }; } } } ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// namespace tl { namespace detail { namespace path_system { ////////////////////////////////////////////////////////////////////////// template const string& path_base::operator[](size_t index) const noexcept { if (index >= m_elements.size()) TL_PLAIN_CRASH("Illegal access"); return m_elements[index]; } ////////////////////////////////////////////////////////////////////////// template string& path_base::operator[](size_t index) noexcept { if (index >= m_elements.size()) TL_PLAIN_CRASH("Illegal access"); m_hash = 0; return m_elements[index]; } ////////////////////////////////////////////////////////////////////////// template const string& path_base::at(size_t index) const noexcept { if (index >= m_elements.size()) TL_PLAIN_CRASH("Illegal access"); return m_elements[index]; } ////////////////////////////////////////////////////////////////////////// template string& path_base::at(size_t index) noexcept { if (index >= m_elements.size()) TL_PLAIN_CRASH("Illegal access"); m_hash = 0; return m_elements[index]; } ////////////////////////////////////////////////////////////////////////// template void path_base::shrink(size_t size) noexcept { if (size < this->size()) { m_elements.resize(size); m_hash = 0; } } ////////////////////////////////////////////////////////////////////////// template uint32_t path_base::hash() const noexcept { if (m_hash == 0) { for (const string& e : m_elements) hash_and_combine(m_hash, e); } return m_hash; } ////////////////////////////////////////////////////////////////////////// template size_t path_base::size() const noexcept { return m_elements.size(); } ////////////////////////////////////////////////////////////////////////// template bool path_base::empty() const noexcept { return m_elements.empty(); } ////////////////////////////////////////////////////////////////////////// template void path_base::reserve(size_t capacity) noexcept { m_elements.reserve(capacity); } ////////////////////////////////////////////////////////////////////////// template string path_base::pop_back() noexcept { if (empty()) { TL_PLAIN_FAIL(); return string(); } m_hash = 0; auto res = m_elements.back(); m_elements.pop_back(); return res; } ////////////////////////////////////////////////////////////////////////// template bool path_base::push_back(string element) noexcept { push_element(std::move(element)); return true; } ////////////////////////////////////////////////////////////////////////// template void path_base::push_element(string element) noexcept { m_hash = 0; m_elements.push_back(std::move(element)); } ////////////////////////////////////////////////////////////////////////// template void path_base::take_elements(tl::vector&& elements) noexcept { m_hash = 0; m_elements = std::move(elements); } ////////////////////////////////////////////////////////////////////////// template typename path_base::iterator path_base::begin() noexcept { return m_elements.begin(); } ////////////////////////////////////////////////////////////////////////// template typename path_base::iterator path_base::end() noexcept { return m_elements.end(); } ////////////////////////////////////////////////////////////////////////// template typename path_base::const_iterator path_base::begin() const noexcept { return m_elements.cbegin(); } ////////////////////////////////////////////////////////////////////////// template typename path_base::const_iterator path_base::end() const noexcept { return m_elements.cend(); } ////////////////////////////////////////////////////////////////////////// template typename path_base::const_reverse_iterator path_base::rbegin() const noexcept { return m_elements.crbegin(); } ////////////////////////////////////////////////////////////////////////// template typename path_base::const_reverse_iterator path_base::rend() const noexcept { return m_elements.crend(); } ////////////////////////////////////////////////////////////////////////// template bool path_key_less::operator()(const path_base& a1, const path_base& a2) const noexcept { const size_t sz1 = a1.m_elements.size(); const size_t sz2 = a2.m_elements.size(); for (size_t i = 0; i < sz1 && i < sz2; ++i) { if (a1.m_elements[i] != a2.m_elements[i]) return a1.m_elements[i].unique_key() < a2.m_elements[i].unique_key(); } return sz1 < sz2; } } } }