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. |
16 | /* 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, pos - 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 | |
39 | 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. |
50 | /* implicit */ QualifiedName(const char* name) |
51 | : QualifiedName(std::string(name)) {} |
52 | |
53 | // `name` must be a bare name (no dots!) |
54 | 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" |
65 | 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" |
82 | const std::string& qualifiedName() const { |
83 | return qualifiedName_; |
84 | } |
85 | |
86 | // The leading qualifier, like "foo.bar" |
87 | const std::string& prefix() const { |
88 | return prefix_; |
89 | } |
90 | |
91 | // The base name, like "baz" |
92 | const std::string& name() const { |
93 | return name_; |
94 | } |
95 | |
96 | 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> |
113 | 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 | |
129 | 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 | |