blob: c6484815febe168765a08f8de98e06a1228bd026 [file] [log] [blame]
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +01001#include "utils/generate_id.hpp"
2
3#include <sdbusplus/exception.hpp>
4
Ed Tanous36de0562022-05-27 13:05:49 -07005#include <algorithm>
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +01006#include <system_error>
7
8namespace utils
9{
10namespace details
11{
12
13static constexpr std::string_view allowedCharactersInId =
14 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_/";
15
Krzysztof Grobelnya950e422021-12-31 13:49:00 +010016size_t countDigits(size_t value)
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +010017{
Krzysztof Grobelnya950e422021-12-31 13:49:00 +010018 size_t result = 1;
19 while (value >= 10)
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +010020 {
Krzysztof Grobelnya950e422021-12-31 13:49:00 +010021 ++result;
22 value /= 10;
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +010023 }
Krzysztof Grobelnya950e422021-12-31 13:49:00 +010024 return result;
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +010025}
26
Krzysztof Grobelnya950e422021-12-31 13:49:00 +010027std::string generateId(std::string_view id, std::string_view name,
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +010028 const std::vector<std::string>& conflictIds,
29 size_t maxLength)
30{
Krzysztof Grobelnya950e422021-12-31 13:49:00 +010031 verifyIdCharacters(id);
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +010032
Krzysztof Grobelnya950e422021-12-31 13:49:00 +010033 if (id.starts_with('/'))
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +010034 {
Krzysztof Grobelnya950e422021-12-31 13:49:00 +010035 id = id.substr(1);
36 }
37
38 if ((id.length() > maxLength) ||
39 (id.ends_with('/') && id.length() >= maxLength))
40 {
41 throw sdbusplus::exception::SdBusError(
42 static_cast<int>(std::errc::invalid_argument), "Id too long");
43 }
44
45 if (!id.empty() && !id.ends_with('/'))
46 {
47 if (std::find(conflictIds.begin(), conflictIds.end(), id) !=
48 conflictIds.end())
49 {
50 throw sdbusplus::exception::SdBusError(
51 static_cast<int>(std::errc::file_exists), "Duplicated id");
52 }
53 return std::string(id);
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +010054 }
55
56 std::string strippedId(name);
57 strippedId.erase(
58 std::remove_if(strippedId.begin(), strippedId.end(),
59 [](char c) {
60 return c == '/' ||
61 details::allowedCharactersInId.find(c) ==
62 std::string_view::npos;
63 }),
64 strippedId.end());
Krzysztof Grobelnya950e422021-12-31 13:49:00 +010065 strippedId = std::string(id) + strippedId;
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +010066
67 size_t idx = 0;
68 std::string tmpId = strippedId.substr(0, maxLength);
69
70 while (std::find(conflictIds.begin(), conflictIds.end(), tmpId) !=
71 conflictIds.end() ||
72 tmpId.empty())
73 {
Krzysztof Grobelnya950e422021-12-31 13:49:00 +010074 size_t digitsInIdx = countDigits(idx);
75
76 if (digitsInIdx > maxLength)
77 {
78 throw sdbusplus::exception::SdBusError(
79 static_cast<int>(std::errc::file_exists),
80 "Unique indices are depleted");
81 }
82
83 tmpId =
84 strippedId.substr(0, maxLength - digitsInIdx) + std::to_string(idx);
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +010085 ++idx;
86 }
Krzysztof Grobelnya950e422021-12-31 13:49:00 +010087
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +010088 return tmpId;
89}
90
Krzysztof Grobelnya950e422021-12-31 13:49:00 +010091} // namespace details
92
93void verifyIdCharacters(std::string_view id)
94{
95 if (id.find_first_not_of(details::allowedCharactersInId) !=
96 std::string::npos)
97 {
98 throw sdbusplus::exception::SdBusError(
99 static_cast<int>(std::errc::invalid_argument),
100 "Invalid character in id");
101 }
102
103 if (auto pos = id.find_first_of("/");
104 pos != std::string::npos && pos != id.find_last_of("/"))
105 {
106 throw sdbusplus::exception::SdBusError(
107 static_cast<int>(std::errc::invalid_argument),
108 "Too many '/' in id");
109 }
110}
111
112std::pair<std::string, std::string> generateId(
113 std::string_view id, std::string_view name, std::string_view defaultName,
114 const std::vector<std::string>& conflictIds, const size_t maxLength)
115{
116 if (name.empty() && !id.ends_with('/'))
117 {
118 name = id;
119
120 if (auto pos = name.find_last_of("/"); pos != std::string::npos)
121 {
122 name = name.substr(pos + 1);
123 }
124 }
125
126 if (name.empty())
127 {
128 name = defaultName;
129 }
130
131 return std::make_pair(details::generateId(id, name, conflictIds, maxLength),
132 std::string{name});
133}
134
Krzysztof Grobelnyb8cc78d2021-11-29 15:54:53 +0100135} // namespace utils