// file : libbutl/prefix-map.mxx -*- C++ -*- // license : MIT; see accompanying LICENSE file #ifndef __cpp_modules_ts #pragma once #endif // C includes. #ifndef __cpp_lib_modules_ts #include #include #include // move() #include // min() #endif // Other includes. #ifdef __cpp_modules_ts export module butl.prefix_map; #ifdef __cpp_lib_modules_ts import std.core; #endif #endif #include LIBBUTL_MODEXPORT namespace butl { // A map of hierarchical "paths", e.g., 'foo.bar' or 'foo/bar' with the // ability to retrieve a range of entries that have a specific prefix as // well as finding the most qualified entry for specific path. The '.' and // '/' above are the delimiter characters. // // Note that as a special rule, the default implementation of compare_prefix // treats empty key as everyone's prefix even if the paths don't start with // the delimiter (useful to represent a "root path"). // // Implementation-wise, the idea is to pretend that each key ends with the // delimiter. This way we automatically avoid matching 'foobar' as having a // prefix 'foo'. // template struct compare_prefix; template struct compare_prefix> { typedef std::basic_string K; typedef C delimiter_type; typedef typename K::size_type size_type; typedef typename K::traits_type traits_type; explicit compare_prefix (delimiter_type d): d_ (d) {} bool operator() (const K& x, const K& y) const { return compare (x.c_str (), x.size (), y.c_str (), y.size ()) < 0; } bool prefix (const K& p, const K& k) const { size_type pn (p.size ()), kn (k.size ()); return pn == 0 || // Empty key is always a prefix. (pn <= kn && compare (p.c_str (), pn, k.c_str (), pn == kn ? pn : pn + 1) == 0); } // If the key is not empty, convert the key to its prefix and return // true. Return false otherwise. // bool prefix (K& k) const { if (k.empty ()) return false; size_type p (k.rfind (d_)); k.resize (p != K::npos ? p : 0); return true; } protected: int compare (const C* x, size_type xn, const C* y, size_type yn) const { size_type n (std::min (xn, yn)); int r (traits_type::compare (x, y, n)); if (r == 0) { // Pretend there is the delimiter characters at the end of the // shorter string. // char xc (xn > n ? x[n] : (xn++, d_)); char yc (yn > n ? y[n] : (yn++, d_)); r = traits_type::compare (&xc, &yc, 1); // If we are still equal, then compare the lengths. // if (r == 0) r = (xn == yn ? 0 : (xn < yn ? -1 : 1)); } return r; } private: delimiter_type d_; }; template struct prefix_map_common: M { typedef M map_type; typedef typename map_type::key_type key_type; typedef typename map_type::value_type value_type; typedef typename map_type::key_compare compare_type; typedef typename compare_type::delimiter_type delimiter_type; typedef typename map_type::iterator iterator; typedef typename map_type::const_iterator const_iterator; explicit prefix_map_common (delimiter_type d) : map_type (compare_type (d)) {} prefix_map_common (std::initializer_list i, delimiter_type d) : map_type (std::move (i), compare_type (d)) {} // Find all the entries that are sub-prefixes of the specified prefix. // std::pair find_sub (const key_type&); std::pair find_sub (const key_type&) const; // Find the most qualified entry that is a super-prefix of the specified // prefix. // iterator find_sup (const key_type&); const_iterator find_sup (const key_type&) const; }; template ::delimiter_type D> struct prefix_map_impl: prefix_map_common { typedef typename prefix_map_common::value_type value_type; prefix_map_impl (): prefix_map_common (D) {} prefix_map_impl (std::initializer_list i) : prefix_map_common (std::move (i), D) {} }; template ::delimiter_type D> using prefix_map = prefix_map_impl>, D>; template ::delimiter_type D> using prefix_multimap = prefix_map_impl>, D>; } #include