Flow-IPC 3.0.0
Flow-IPC project: Full implementation reference.
shared_name.cpp
Go to the documentation of this file.
1/* Flow-IPC: Core
2 * Copyright 2023 Akamai Technologies, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the
5 * "License"); you may not use this file except in
6 * compliance with the License. You may obtain a copy
7 * of the License at
8 *
9 * https://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in
12 * writing, software distributed under the License is
13 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
14 * CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing
16 * permissions and limitations under the License. */
17
18/// @file
20#include <boost/functional/hash/hash.hpp>
21#include <cassert>
22#include <istream>
23#include <ostream>
24#include <string>
25#include <utility>
26#include <algorithm>
27
28namespace ipc::util
29{
30
31// Initializers.
32
33
35
39
42
43// Implementations.
44
45Shared_name::Shared_name() = default;
48
49Shared_name Shared_name::ct(const char* src) // Static.
50{
51 Shared_name result;
52 result.m_raw_name.assign(src); // Copy it.
53 return result;
54}
55
56Shared_name Shared_name::ct(std::string&& src_moved) // Static.
57{
58 Shared_name result;
59 result.m_raw_name.assign(std::move(src_moved)); // Move it.
60 return result;
61}
62
65
67{
69 return operator+=(String_view{to_append.str()});
70}
71
73{
75 return operator/=(String_view{to_append.str()});
76}
77
79{
81 return src1 + String_view{src2.str()};
82}
83
85{
86 return Shared_name{src1} /= src2;
87}
88
89const std::string& Shared_name::str() const
90{
91 return m_raw_name;
92}
93
94const char* Shared_name::native_str() const
95{
96 return m_raw_name.c_str();
97}
98
99size_t Shared_name::size() const
100{
101 return m_raw_name.size();
102}
103
105{
106 return m_raw_name.empty();
107}
108
110{
111 return (!empty()) && (m_raw_name.back() == S_SEPARATOR);
112}
113
115{
116 return (!m_raw_name.empty()) && (m_raw_name.front() == S_SEPARATOR);
117}
118
120{
121 m_raw_name.clear();
122}
123
125{
126 using flow::util::in_closed_range;
127
128 // Keep in sync with sanitize()!
129
130 if (size() > S_MAX_LENGTH)
131 {
132 return false;
133 }
134 // else
135
136 /* Following is self-explanatory given the contract. It could be made much briefer, probably, by using built-in
137 * string functions and/or string_algo/regex/.... However, for nicer perf, we'd rather do only one linear-time scan.
138 * Finally, could other characters be potentially allowed by the various sys calls? Maybe, but we'd rather be safe
139 * than sorry in terms of portability, and also these chars should be aesthetically good as a convention. */
140
141 bool prev_is_sep = false;
142 for (const auto ch : m_raw_name)
143 {
144 bool is_sep = ch == S_SEPARATOR;
145 if ((!is_sep) && (!sanitized_non_sep(ch)))
146 {
147 return false;
148 }
149 // else
150
151 if (is_sep)
152 {
153 if (prev_is_sep)
154 {
155 return false;
156 }
157 // else
158 prev_is_sep = true;
159 }
160 else
161 {
162 prev_is_sep = false; // Might be no-op.
163 }
164 } // for (ch : m_raw_name)
165
166 return true;
167} // Shared_name::sanitized()
168
170{
171 using std::string;
172 using std::swap;
173
174 // SEPS are the 2 allowed input separator characters. They must be different. Try to keep this compile-time.
175 constexpr char SEPARATOR_ALT = '/';
176 static_assert(S_SEPARATOR != SEPARATOR_ALT,
177 "The real separator and the alt must be different characters.");
178 constexpr const char SEPS[] = { SEPARATOR_ALT, S_SEPARATOR, '\0' };
179
180 /* The following implementation, while self-explanatory, foregoes briefer approaches in favor of better perf.
181 * Keep in sync with sanitized()! */
182
183 if (empty()) // Get rid of degenerate case for simplicity of below.
184 {
185 return true;
186 }
187 // else at least one character to read and (unless it's illegal) write.
188
189 const auto n = size();
190 const auto start_ptr = m_raw_name.cbegin();
191 const auto end_ptr = m_raw_name.cend();
192 string sanitized_str;
193 sanitized_str.reserve(size());
194
195 for (auto ptr = start_ptr; ptr != end_ptr; ++ptr)
196 {
197 /* We cannot predict, at the start, whether we'll ultimately result in a too-long string, because repeating
198 * separators can actually lower the length. However, if we get to this iteration, then we *will* be writing a
199 * character (unless ch is illegal, but then we'd return false anyway); hence we can detect ASAP whether the
200 * output is too long for sure. */
201 if (sanitized_str.size() >= S_MAX_LENGTH)
202 {
203 return false;
204 }
205 // else not too long yet.
206
207 auto ch = *ptr;
208 const bool is_sep = (ch == S_SEPARATOR) || (ch == SEPARATOR_ALT);
209
210 // Perform nearly identical check to sanitized() (reminder: the 2 functions must remain in sync).
211 if ((!is_sep) && (!sanitized_non_sep(ch)))
212 {
213 return false;
214 }
215 // else: Legal character ch; though it may be sanitized by replacing with a different value.
216
217 if (is_sep)
218 {
219 ch = S_SEPARATOR; // Namely, all separator possibilities are normalized to this one.
220
221 /* Since we found a separator, immediately skip past any more adjacent separators by moving ptr
222 * inside this { body }. Set it to the position just before the first non-separator. That ranges between:
223 * not touching ptr at all (i.e., only one separator after all); and setting it to &*(n - 1) (meaning either
224 * we're at end of m_raw_name, or the rest of it is all separators). Note for() will ++ptr right after this. */
225 auto pos = size_t(ptr - start_ptr);
226 if (pos != (n - 1))
227 {
228 pos = m_raw_name.find_first_not_of(SEPS, pos + 1);
229 if (pos == string::npos)
230 {
231 pos = n;
232 }
233 --pos;
234
235 ptr = start_ptr + pos;
236 }
237 } // if (is_sep)
238
239 sanitized_str += ch;
240 } // for (ptr in [start_ptr, end_ptr))
241
242 // Passed gauntlet in terms of whether we can `return true` (we can and must). Finish up.
243 swap(m_raw_name, sanitized_str); // Constant-time. Then sanitized_str is deallocated.
244 return true;
245} // Shared_name::sanitize()
246
247bool Shared_name::sanitized_non_sep(char ch) // Static.
248{
249 /* std::isalpha/isalnum(s_c<uchar>(ch)) is tempting, but it's affected by the locale; we want none of that.
250 * std::isdigit(s_c<uchar>(ch)) should be fine... but just screw it, man. */
251 return ((ch >= 'A') && (ch <= 'Z')) || ((ch >= 'a') && (ch <= 'z'))
252 || ((ch >= '0') && (ch <= '9'));
253}
254
256{
257 assert(!resource_type.empty());
258
259 const auto& SENTINEL = Shared_name::S_SENTINEL;
260 const auto& ROOT_MAGIC = Shared_name::S_ROOT_MAGIC;
261
262 Shared_name name;
263 name /= ROOT_MAGIC;
264 name /= resource_type;
265 name /= SENTINEL;
266 name /= SENTINEL;
267 name /= SENTINEL;
268 return name;
269} // build_conventional_non_session_based_shared_name()
270
271std::ostream& operator<<(std::ostream& os, const Shared_name& val)
272{
273 // Output char count for convenience: these can be at a premium.
274 if (val.str().empty())
275 {
276 return os << "null";
277 }
278 // else
279 return os << val.str().size() << '|' << val.str();
280}
281
282std::istream& operator>>(std::istream& is, Shared_name& val)
283{
284 // @todo Can be made a tiny bit faster by writing directly into val.m_raw_name. Requires `friend` or something.
285 std::string str;
286 is >> str;
287 if (is)
288 {
289 val = Shared_name::ct(std::move(str));
290 }
291 // else { We'd write an empty string; technically that is against our contract. }
292 return is;
293}
294
295bool operator==(const Shared_name& val1, const Shared_name& val2)
296{
297 return val1.str() == val2.str();
298}
299
300bool operator!=(const Shared_name& val1, const Shared_name& val2)
301{
302 return !(operator==(val1, val2));
303}
304
306{
307 /* Existence/impl rationale: This is faster than if they had to: `val1 == Shared_name::ct(val2)`.
308 * Existence rationale: It's also nice to be able to write: `val1 == "something"` (String_view implicitly cted). */
309
310 using util::String_view;
311 return std::operator==(String_view{val1.str()}, val2);
312}
313
315{
316 return !(operator==(val1, val2));
317}
318
320{
321 return operator==(val2, val1);
322}
323
325{
326 return !(operator==(val1, val2));
327}
328
329bool operator<(const Shared_name& val1, const Shared_name& val2)
330{
331 return val1.str() < val2.str();
332}
333
334size_t hash_value(const Shared_name& val)
335{
336 using boost::hash;
337 using std::string;
338
339 return hash<string>()(val.str());
340}
341
342void swap(Shared_name& val1, Shared_name& val2)
343{
344 using std::swap;
345 swap(val1.m_raw_name, val2.m_raw_name);
346}
347
348} // namespace ipc::util
String-wrapping abstraction representing a name uniquely distinguishing a kernel-persistent entity fr...
bool sanitized() const
Returns true if and only if the contained name/fragment is sanitized according to length,...
const char * native_str() const
Returns (sans copying) pointer to NUL-terminated wrapped name string, suitable to pass into sys calls...
Definition: shared_name.cpp:94
Shared_name & operator+=(const Shared_name &src_to_append)
Appends the given other Shared_name.
Definition: shared_name.cpp:66
Shared_name()
Constructs empty() name.
static constexpr size_t S_MAX_LENGTH
Max value of size() such that, if str() used to name a supported shared resource, sys call safely won...
static const Shared_name S_SENTINEL
A Shared_name fragment, with no S_SEPARATOR characters inside, that represents a path component that ...
static Shared_name ct(const Source &src)
Copy-constructs from a char-sequence container such as string or util::String_view.
static const Shared_name S_RESOURCE_TYPE_ID_SHM
Relative-folder fragment (no separators) identifying the resource type for: SHM pools.
std::string m_raw_name
The name or name fragment; see str().
static const Shared_name S_RESOURCE_TYPE_ID_MUTEX
Relative-folder fragment (no separators) identifying the resource type for: boost....
static bool sanitized_non_sep(char ch)
Helper that returns true if and only if: ch is a valid character for purposes of ....
static const Shared_name S_EMPTY
A (default-cted) Shared_name. May be useful for functions returning const Shared_name&.
bool has_trailing_separator() const
Returns true if and only if !this->empty(), and str() ends with the S_SEPARATOR character.
void clear()
Makes it so empty() == true.
bool empty() const
Returns true if and only if str().empty() == true.
Shared_name & operator/=(const Shared_name &src_to_append)
Appends a folder separator followed by the given other Shared_name.
Definition: shared_name.cpp:72
friend void swap(Shared_name &val1, Shared_name &val2)
Swaps two objects.
static const Shared_name S_1ST_OR_ONLY
A Shared_name fragment, with no S_SEPARATOR characters inside, that represents a path component that ...
bool absolute() const
Returns true if and only if the first character is S_SEPARATOR.
Shared_name & operator=(const Shared_name &src)
Copy-assigns from an existing Shared_name.
bool sanitize()
Best-effort attempt to turn sanitized() from false to true, unless it is already true; returns the fi...
static constexpr char S_SEPARATOR
Character we use, by convention, to separate conceptual folders within str().
const std::string & str() const
Returns (sans copying) ref to immutable entire wrapped name string, suitable to pass into sys calls w...
Definition: shared_name.cpp:89
static const Shared_name S_ROOT_MAGIC
A Shared_name fragment, with no S_SEPARATOR characters inside, to be used in any Shared_name maintain...
size_t size() const
Returns str().size().
Definition: shared_name.cpp:99
util::Shared_name Shared_name
Convenience alias for the commonly used type util::Shared_name.
Flow-IPC module containing miscellaneous general-use facilities that are ubiquitously used by ~all Fl...
Definition: util.cpp:31
Shared_name operator/(const Shared_name &src1, const Shared_name &src2)
Returns new object equal to Shared_name(src1) /= src2.
Definition: shared_name.cpp:84
std::ostream & operator<<(std::ostream &os, const Native_handle &val)
Prints string representation of the given Native_handle to the given ostream.
Shared_name operator+(const Shared_name &src1, const Shared_name &src2)
Returns new object equal to Shared_name(src1) += src2.
Definition: shared_name.cpp:78
constexpr bool operator<(Native_handle val1, Native_handle val2) noexcept
Returns a less-than comparison of two Native_handle objects, with the usual total ordering guarantees...
Shared_name build_conventional_non_session_based_shared_name(const Shared_name &resource_type)
Builds an absolute name according to the path convention explained in Shared_name class doc header; t...
size_t hash_value(Native_handle val) noexcept
Hasher of Native_handle for boost.unordered et al.
void swap(Shared_name &val1, Shared_name &val2)
Swaps two objects.
constexpr void swap(Native_handle &val1, Native_handle &val2) noexcept
Standard swap.
bool operator==(util::String_view val1, const Shared_name &val2)
Returns true if and only if string(val1) == val2.str().
constexpr bool operator!=(Native_handle val1, Native_handle val2) noexcept
Negation of similar ==.
std::istream & operator>>(std::istream &is, Shared_name &val)
Reads Shared_name from the given istream; equivalent to reading string into Shared_name::str().
flow::util::String_view String_view
Short-hand for Flow's String_view.
Definition: util_fwd.hpp:112
constexpr bool operator==(Native_handle val1, Native_handle val2) noexcept
Returns true if and only if the two Native_handle objects are the same underlying handle.