xref: /aosp_15_r20/external/pytorch/aten/src/ATen/core/qualified_name.h (revision da0073e96a02ea20f0ac840b70461e3646d07c45)
1 #pragma once
2 
3 #include <c10/util/ArrayRef.h>
4 #include <c10/util/Exception.h>
5 #include <c10/util/StringUtil.h>
6 #include <c10/util/irange.h>
7 #include <string>
8 
9 namespace c10 {
10 
11 // Represents a name of the form "foo.bar.baz"
12 struct QualifiedName {
13   QualifiedName() = default;
14 
15   // `name` can be a dotted string, like "foo.bar.baz", or just a bare name.
QualifiedNameQualifiedName16   /* implicit */ QualifiedName(const std::string& name) {
17     TORCH_CHECK(!name.empty());
18     // split the string into its atoms.
19     size_t startSearchFrom = 0;
20     size_t pos = name.find(delimiter_, startSearchFrom);
21 
22     while (pos != std::string::npos) {
23       auto atom = name.substr(startSearchFrom, pos - startSearchFrom);
24       TORCH_INTERNAL_ASSERT(
25           !atom.empty(), "Invalid name for qualified name: '", name, "'");
26       atoms_.push_back(std::move(atom));
27       startSearchFrom = pos + 1;
28       pos = name.find(delimiter_, startSearchFrom);
29     }
30 
31     auto finalAtom = name.substr(startSearchFrom);
32     TORCH_INTERNAL_ASSERT(
33         !finalAtom.empty(), "Invalid name for qualified name: '", name, "'");
34     atoms_.emplace_back(std::move(finalAtom));
35 
36     cacheAccessors();
37   }
38 
QualifiedNameQualifiedName39   explicit QualifiedName(std::vector<std::string> atoms) : atoms_(std::move(atoms)) {
40     for (const auto& atom : atoms_) {
41       TORCH_CHECK(!atom.empty(), "Atom cannot be empty");
42       TORCH_CHECK(
43           atom.find(delimiter_) == std::string::npos,
44           "Delimiter not allowed in atom");
45     }
46 
47     cacheAccessors();
48   }
49   // Unnecessary copy. Ideally we'd use something like std::string_view.
QualifiedNameQualifiedName50   /* implicit */ QualifiedName(const char* name)
51       : QualifiedName(std::string(name)) {}
52 
53   // `name` must be a bare name (no dots!)
QualifiedNameQualifiedName54   explicit QualifiedName(const QualifiedName& prefix, std::string name) {
55     TORCH_INTERNAL_ASSERT(!name.empty());
56     TORCH_INTERNAL_ASSERT(name.find(delimiter_) == std::string::npos);
57     atoms_.insert(atoms_.begin(), prefix.atoms_.begin(), prefix.atoms_.end());
58     atoms_.push_back(std::move(name));
59 
60     cacheAccessors();
61   }
62 
63   // Is `this` a prefix of `other`?
64   // For example, "foo.bar" is a prefix of "foo.bar.baz"
isPrefixOfQualifiedName65   bool isPrefixOf(const QualifiedName& other) const {
66     const auto& thisAtoms = atoms_;
67     const auto& otherAtoms = other.atoms_;
68 
69     if (thisAtoms.size() > otherAtoms.size()) {
70       // Can't be a prefix if it's bigger
71       return false;
72     }
73     for (const auto i : c10::irange(thisAtoms.size())) {
74       if (thisAtoms[i] != otherAtoms[i]) {
75         return false;
76       }
77     }
78     return true;
79   }
80 
81   // The fully qualified name, like "foo.bar.baz"
qualifiedNameQualifiedName82   const std::string& qualifiedName() const {
83     return qualifiedName_;
84   }
85 
86   // The leading qualifier, like "foo.bar"
prefixQualifiedName87   const std::string& prefix() const {
88     return prefix_;
89   }
90 
91   // The base name, like "baz"
nameQualifiedName92   const std::string& name() const {
93     return name_;
94   }
95 
atomsQualifiedName96   const std::vector<std::string>& atoms() const {
97     return atoms_;
98   }
99 
100   bool operator==(const QualifiedName& other) const {
101     return this->qualifiedName_ == other.qualifiedName_;
102   }
103 
104   bool operator!=(const QualifiedName& other) const {
105     return !(*this == other);
106   }
107 
108  private:
109   static constexpr char delimiter_ = '.';
110 
111   // Helper for cacheAccessors() below.
112   template<typename T>
joinQualifiedName113   std::string join(char delimiter, const T& v) {
114     std::string out;
115     size_t reserve = 0;
116     for (const auto& e : v) {
117       reserve += e.size() + 1;
118     }
119     out.reserve(reserve);
120     for (const auto i : c10::irange(v.size())) {
121       if (i != 0) {
122         out.push_back(delimiter);
123       }
124       out.append(v[i]);
125     }
126     return out;
127   }
128 
cacheAccessorsQualifiedName129   void cacheAccessors() {
130     qualifiedName_ = join(delimiter_, atoms_);
131     if (atoms_.size() > 1) {
132       ArrayRef<std::string> view(atoms_);
133       const auto prefixView = view.slice(0, view.size() - 1);
134       prefix_ = join(delimiter_, prefixView);
135     }
136 
137     if (!atoms_.empty()) {
138       name_ = atoms_.back();
139     }
140   }
141 
142   // The actual list of names, like "{foo, bar, baz}"
143   std::vector<std::string> atoms_;
144 
145   /*
146    * Cached accessors, derived from `atoms_`.
147    */
148   std::string qualifiedName_;
149   std::string prefix_;
150   std::string name_;
151 };
152 } // namespace c10
153 
154 namespace std {
155 template <>
156 struct hash<c10::QualifiedName> {
157   size_t operator()(const c10::QualifiedName& n) const noexcept {
158     return std::hash<std::string>()(n.qualifiedName());
159   }
160 };
161 } // namespace std
162