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
9namespace c10 {
10
11// Represents a name of the form "foo.bar.baz"
12struct 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
154namespace std {
155template <>
156struct 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