xref: /aosp_15_r20/system/libvintf/FQName.cpp (revision 70a7ec852fcefd15a4fb57f8f183a8b1c3aacb08)
1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <vintf/FQName.h>
18 
19 #include <android-base/logging.h>
20 #include <android-base/parseint.h>
21 #include <android-base/strings.h>
22 #include <iostream>
23 #include <sstream>
24 
25 namespace android::vintf::details {
26 
FQName()27 FQName::FQName() : mIsIdentifier(false) {}
28 
parse(const std::string & s,FQName * into)29 bool FQName::parse(const std::string& s, FQName* into) {
30     return into->setTo(s);
31 }
32 
FQName(const std::string & package,const std::string & version,const std::string & name)33 FQName::FQName(const std::string& package, const std::string& version, const std::string& name) {
34     size_t majorVer, minorVer;
35     CHECK(parseVersion(version, &majorVer, &minorVer));
36     CHECK(setTo(package, majorVer, minorVer, name)) << string();
37 }
38 
setTo(const std::string & package,size_t majorVer,size_t minorVer,const std::string & name)39 bool FQName::setTo(const std::string& package, size_t majorVer, size_t minorVer,
40                    const std::string& name) {
41     mPackage = package;
42     mMajor = majorVer;
43     mMinor = minorVer;
44     mName = name;
45 
46     FQName other;
47     if (!parse(string(), &other)) return false;
48     if ((*this) != other) return false;
49     mIsIdentifier = other.isIdentifier();
50     return true;
51 }
52 
isIdentifier() const53 bool FQName::isIdentifier() const {
54     return mIsIdentifier;
55 }
56 
isInterfaceName() const57 bool FQName::isInterfaceName() const {
58     return !mName.empty() && mName[0] == 'I' && mName.find('.') == std::string::npos;
59 }
60 
isIdentStart(char a)61 static inline bool isIdentStart(char a) {
62     return ('a' <= a && a <= 'z') || ('A' <= a && a <= 'Z') || a == '_';
63 }
isLeadingDigit(char a)64 static inline bool isLeadingDigit(char a) {
65     return '1' <= a && a <= '9';
66 }
isDigit(char a)67 static inline bool isDigit(char a) {
68     return '0' <= a && a <= '9';
69 }
isIdentBody(char a)70 static inline bool isIdentBody(char a) {
71     return isIdentStart(a) || isDigit(a);
72 }
73 
74 // returns pointer to end of [a-zA-Z_][a-zA-Z0-9_]*
eatIdent(const char * l,const char * end)75 static const char* eatIdent(const char* l, const char* end) {
76     if (!(l < end && isIdentStart(*l++))) return nullptr;
77     while (l < end && isIdentBody(*l)) l++;
78     return l;
79 }
80 
81 // returns pointer to end of <ident>(\.<ident>)*
eatPackage(const char * l,const char * end)82 static const char* eatPackage(const char* l, const char* end) {
83     if ((l = eatIdent(l, end)) == nullptr) return nullptr;
84 
85     while (l < end && *l == '.') {
86         l++;
87         if ((l = eatIdent(l, end)) == nullptr) return nullptr;
88     }
89     return l;
90 }
91 
92 // returns pointer to end of [1-9][0-9]*|0
eatNumber(const char * l,const char * end)93 static const char* eatNumber(const char* l, const char* end) {
94     if (!(l < end)) return nullptr;
95     if (*l == '0') return l + 1;
96     if (!isLeadingDigit(*l++)) return nullptr;
97     while (l < end && isDigit(*l)) l++;
98     return l;
99 }
100 
setTo(const std::string & s)101 bool FQName::setTo(const std::string& s) {
102     clear();
103 
104     if (s.empty()) return false;
105 
106     const char* l = s.c_str();
107     const char* end = l + s.size();
108     // [email protected]::IFoo.Type
109     // S                   ES ES E S        E
110     //
111     // S - start pointer
112     // E - end pointer
113 
114     struct StartEnd {
115         const char* start = nullptr;
116         const char* end = nullptr;
117 
118         std::string string() {
119             if (start == nullptr) return std::string();
120             return std::string(start, end - start);
121         }
122     };
123     StartEnd package, major, minor, name;
124 
125     if (l < end && isIdentStart(*l)) {
126         package.start = l;
127         if ((package.end = l = eatPackage(l, end)) == nullptr) return false;
128     }
129     if (l < end && *l == '@') {
130         l++;
131 
132         major.start = l;
133         if ((major.end = l = eatNumber(l, end)) == nullptr) return false;
134 
135         if (!(l < end && *l++ == '.')) return false;
136 
137         minor.start = l;
138         if ((minor.end = l = eatNumber(l, end)) == nullptr) return false;
139     }
140     if (l < end && *l == ':') {
141         l++;
142         if (l < end && *l == ':') {
143             l++;
144             name.start = l;
145             if ((name.end = l = eatPackage(l, end)) == nullptr) return false;
146         } else {
147             return false;
148         }
149     }
150 
151     if (l < end) return false;
152 
153     CHECK((major.start == nullptr) == (minor.start == nullptr));
154 
155     // if we only parse a package, consider this to be a name
156     if (name.start == nullptr && major.start == nullptr) {
157         name.start = package.start;
158         name.end = package.end;
159         package.start = package.end = nullptr;
160     }
161 
162     // failures after this goto fail to clear
163     mName = name.string();
164     mPackage = package.string();
165 
166     if (major.start != nullptr) {
167         if (!parseVersion(major.string(), minor.string(), &mMajor, &mMinor)) goto fail;
168     } else if (mPackage.empty() && name.end == eatIdent(name.start, name.end)) {
169         // major.start == nullptr
170         mIsIdentifier = true;
171     }
172 
173     if (!mPackage.empty() && version().empty()) goto fail;
174 
175     return true;
176 fail:
177     clear();
178     return false;
179 }
180 
package() const181 const std::string& FQName::package() const {
182     return mPackage;
183 }
184 
version() const185 std::string FQName::version() const {
186     if (!hasVersion()) {
187         return "";
188     }
189     return std::to_string(mMajor) + "." + std::to_string(mMinor);
190 }
191 
atVersion() const192 std::string FQName::atVersion() const {
193     std::string v = version();
194     return v.empty() ? "" : ("@" + v);
195 }
196 
clear()197 void FQName::clear() {
198     mIsIdentifier = false;
199     mPackage.clear();
200     clearVersion();
201     mName.clear();
202 }
203 
clearVersion(size_t * majorVer,size_t * minorVer)204 void FQName::clearVersion(size_t* majorVer, size_t* minorVer) {
205     *majorVer = *minorVer = 0;
206 }
207 
parseVersion(const std::string & majorStr,const std::string & minorStr,size_t * majorVer,size_t * minorVer)208 bool FQName::parseVersion(const std::string& majorStr, const std::string& minorStr,
209                           size_t* majorVer, size_t* minorVer) {
210     bool versionParseSuccess = ::android::base::ParseUint(majorStr, majorVer) &&
211                                ::android::base::ParseUint(minorStr, minorVer);
212     if (!versionParseSuccess) {
213         LOG(ERROR) << "numbers in " << majorStr << "." << minorStr << " are out of range.";
214     }
215     return versionParseSuccess;
216 }
217 
parseVersion(const std::string & v,size_t * majorVer,size_t * minorVer)218 bool FQName::parseVersion(const std::string& v, size_t* majorVer, size_t* minorVer) {
219     if (v.empty()) {
220         clearVersion(majorVer, minorVer);
221         return true;
222     }
223 
224     std::vector<std::string> vs = base::Split(v, ".");
225     if (vs.size() != 2) return false;
226     return parseVersion(vs[0], vs[1], majorVer, minorVer);
227 }
228 
setVersion(const std::string & v)229 bool FQName::setVersion(const std::string& v) {
230     return parseVersion(v, &mMajor, &mMinor);
231 }
232 
clearVersion()233 void FQName::clearVersion() {
234     clearVersion(&mMajor, &mMinor);
235 }
236 
parseVersion(const std::string & majorStr,const std::string & minorStr)237 bool FQName::parseVersion(const std::string& majorStr, const std::string& minorStr) {
238     return parseVersion(majorStr, minorStr, &mMajor, &mMinor);
239 }
240 
name() const241 const std::string& FQName::name() const {
242     return mName;
243 }
244 
string() const245 std::string FQName::string() const {
246     std::string out;
247     out.append(mPackage);
248     out.append(atVersion());
249     if (!mName.empty()) {
250         if (!mPackage.empty() || !version().empty()) {
251             out.append("::");
252         }
253         out.append(mName);
254     }
255 
256     return out;
257 }
258 
operator <(const FQName & other) const259 bool FQName::operator<(const FQName& other) const {
260     return string() < other.string();
261 }
262 
operator ==(const FQName & other) const263 bool FQName::operator==(const FQName& other) const {
264     return string() == other.string();
265 }
266 
operator !=(const FQName & other) const267 bool FQName::operator!=(const FQName& other) const {
268     return !(*this == other);
269 }
270 
getInterfaceName() const271 const std::string& FQName::getInterfaceName() const {
272     CHECK(isInterfaceName()) << mName;
273 
274     return mName;
275 }
276 
getPackageAndVersion() const277 FQName FQName::getPackageAndVersion() const {
278     return FQName(package(), version(), "");
279 }
280 
getPackageComponents() const281 std::vector<std::string> FQName::getPackageComponents() const {
282     return base::Split(package(), ".");
283 }
284 
hasVersion() const285 bool FQName::hasVersion() const {
286     return mMajor > 0;
287 }
288 
getVersion() const289 std::pair<size_t, size_t> FQName::getVersion() const {
290     return {mMajor, mMinor};
291 }
292 
getPackageMajorVersion() const293 size_t FQName::getPackageMajorVersion() const {
294     CHECK(hasVersion()) << "FQName: No version exists at getPackageMajorVersion(). "
295                         << "Did you check hasVersion()?";
296     return mMajor;
297 }
298 
getPackageMinorVersion() const299 size_t FQName::getPackageMinorVersion() const {
300     CHECK(hasVersion()) << "FQName: No version exists at getPackageMinorVersion(). "
301                         << "Did you check hasVersion()?";
302     return mMinor;
303 }
304 
inPackage(const std::string & package) const305 bool FQName::inPackage(const std::string& package) const {
306     std::vector<std::string> components = getPackageComponents();
307     std::vector<std::string> inComponents = base::Split(package, ".");
308 
309     if (inComponents.size() > components.size()) {
310         return false;
311     }
312 
313     for (size_t i = 0; i < inComponents.size(); i++) {
314         if (inComponents[i] != components[i]) {
315             return false;
316         }
317     }
318 
319     return true;
320 }
321 
322 }  // namespace android::vintf::details
323