Flow-IPC 1.0.0
Flow-IPC project: Full implementation reference.
shared_name.hpp
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
19#pragma once
20
22#include "ipc/common.hpp"
23
24namespace ipc::util
25{
26
27/**
28 * String-wrapping abstraction representing a name uniquely distinguishing a kernel-persistent
29 * entity from all others in the system, or a fragment of such a name. Conceptually it relates to `std::string`
30 * similarly to how `filesystem::path` does.
31 *
32 * This is a very simple class in terms of what logic it actually adds: it encapsulates an `std::string` and allows for,
33 * basically, string operations like concatenation, with some syntactic sugar added for a simple *folder* convention.
34 * However, the design *context* (namely, how shared resources are named and why) is less trivial, and this doc header
35 * is a good place to get into those topics as well. Hence, we first cover practical aspects, with the architectural
36 * context referenced only as needed. Then, below that, there's an architecture discussion about naming in general.
37 *
38 * ### Construction/assignment from and conversion to strings/similar ###
39 * Interanally it stores an `std::string`.
40 *
41 * Conversion: That `string` is accessible by `const&` via str() and similarly the NUL-terminated native_str().
42 * Also there is an `ostream<<` printer; do note it does not simply print str() but rather a beautified version.
43 * (`ostream>>` input is also provided but is identical to `ostream >> string`.)
44 *
45 * Construction/assignment: It comes with standard default/copy/move ctors and assignment. There is also a
46 * constructor that takes two iterators-to-`char` (including 2 `const char*`). However, construction
47 * from `string` (including destuctive move-construction), util::String_view, NUL-terminated `const char*`,
48 * `vector<char>` (etc.) is available exclusively in the form of `static` quasi-ctors ct(). Lastly: To assign please
49 * use move ctor: `existing_sh_name = Shared_name::ct(src)`.
50 *
51 * Rationale: C++ ADL semantics cause a ton of super-annoying problems -- especially inside ipc::transport
52 * namespace itself -- where compilers will auto-convert, e.g., a `string` to `Shared_name` without being asked
53 * to do so at all. For example, one sees `operator << string` output the `string` in "beautified" form, because
54 * a `Shared_name` is constructed implicitly and then printed via its `<<`. I (ygoldfel), then, took a cue from
55 * boost.asio's IP address classes which use `static` quasi-ctors to avoid any such issues. (The alternative
56 * was to fine-tune the ctor set a-la `filesystem::path`, with conversion traits and all kinds of craziness.)
57 *
58 * ### Practical Shared_name matters ###
59 * A *shared resource* in this context is some entity (such as a SHM-mapped addressable area; or a POSIX message queue)
60 * that can be *opened* for further access by potentially 2+ processes simultaneously. When opening (at least),
61 * the shared resource is referred to -- in the associated opening sys call or similar -- by a string name, and the name
62 * works equally from those 2+ processes. Shared_name stores such a string. It is also suitable for *fragments* of
63 * these names, including prefixes, suffixes, or middle parts. Therefore -- much like `boost::filesystem::path` is
64 * basically a string wrapper -- Shared_name is just a string wrapper. In fact the `std::string` it stores is
65 * accessible through str() by reference.
66 *
67 * All standard string-like operations (generally, the `+=`, `/=`, `+`, and `/` operations) are equally performant and
68 * not *any* smarter than the corresponding string concatenation ops (the `/` variants just add a separator character).
69 * The same holds for all operations, except see the next section. In particular, all characters are allowed in any
70 * position, and there is no max length enforced. Hence, the user may assume max possible performance and zero
71 * restrictions, with the exception of:
72 *
73 * ### Conventions understood/enforced by Shared_name ###
74 * As noted, generally the class allows everything and does nothing smart that `std::string` wouldn't do. This is for
75 * flexibility and performance and is inspired by `boost::filesystem::path`. However, there is *optional* support
76 * for some simple conventions. First let's discuss those conventions:
77 *
78 * In Flow-IPC, Shared_name is to be used for all shared resource *types* needed. (These are not enumerated here.)
79 * Different resource types might have different rules for (1) characters allowed; and (2) max length allowed; so that
80 * if this is violated, a creation/opening sys call (or similar) will fail with an error. Therefore, the following
81 * *conventions* are understood and represent the *union* of known system restrictions, so that sanitized()
82 * names will work for *all* known resource types. Also, some conventions are for human usability.
83 * In total, these conventions are understood:
84 * - Only certain characters are allowed. Namely, only alphanumerics [A-Za-z0-9] are allowed as of this writing.
85 * - There is a *folder* convention: We anticipate a file tree-like organization of various items; hence a
86 * folder separator character, #S_SEPARATOR, is allowed also.
87 * - A complete, usable (in a sys call or similar) name is to start with one leading #S_SEPARATOR.
88 * - We anticipate no empty "folder" names; and therefore no sequences of 2+ adjacent #S_SEPARATOR chars.
89 * - Names shall not exceed a certain max length. #S_MAX_LENGTH is chosen to be low enough to work for all supported
90 * resource types.
91 *
92 * How does Shared_name actually *use* the knowledge of these conventions? To reiterate: normally, it does not care.
93 * The only parts of its API that *do* care are as follows:
94 * - absolute() will return `true` if and only if the first char is #S_SEPARATOR. By convention, you should not
95 * pass str() to a sys call/similar, unless `absolute() == true`.
96 * - sanitized() will return `true` if and only if str() is a valid name or name fragment (no illegal characters;
97 * does not exceed #S_MAX_LENGTH; no multi-separator sequences).
98 * - sanitize() will attempt to non-destructively modify (if needed) name in such a way as to make sanitized() return
99 * `true`.
100 * You should call them only when you specifically need some conventions checked or enforced. Otherwise Shared_name
101 * ops will be as dumb/fast as possible.
102 *
103 * @note The Flow-IPC library *user* is unlikely to directly pass a Shared_name into a sys call or similar. Probably
104 * Flow-IPC internals will do it for the user. Therefore, the likeliest pattern for the user to encounter in
105 * public Flow-IPC APIs is: When initially naming some `transport` object, a constructor or factory will take
106 * a *relative* (`absolute() == false`) Shared_name prepared by the user. Within that relative fragment,
107 * the user is to use the folder conventions above (via `/=` or `/` operators perhaps) if needed. Inside
108 * Flow-IPC, the impl can then construct an absolute name by internally pre-pending stuff to the
109 * user-constructed fragment. If you have ensured the name is sanitized(), then it will not fail on account of a
110 * bad name. If you have *not*, then it may still not fail: sanitized() is a conservative criterion and may be
111 * too stringent for some resource types and OS calls. It is up to *you* to either ensure sanitized() or
112 * otherwise worry about the possibility of a bad name (illegal characters, excessive length).
113 *
114 * ### Thread safety ###
115 * Shared_name has the same safety under concurrency as `std::string` (i.e., if you intend on writing while
116 * reading/writing same `*this` concurrently, synchronized access is necessary to avoid corruption and other standard
117 * thread safety violations).
118 *
119 * @internal
120 * ### Architecture discussion / shared name convention synopsis ###
121 * As promised, we've covered the practical aspects of Shared_name first. Now let's discuss the larger design.
122 * Note that while this does inform some of Shared_name API (which is quite simple), really the discussion has wider
123 * implications, and this doc header merely seems like a good place for it. It would've been okay to split off
124 * much of the below info into ipc::session somewhere, but... maybe that's better... but the human in me (ygoldfel)
125 * feels you'll want to know this now, here, even though for formal appropriateness maybe it should be moved.
126 * In any case here it is:
127 *
128 * As noted before, there are N shared resource types, and Shared_name is to be used for all of them. At minimum,
129 * then:
130 * - 2 names are to be equal if and only if they refer to the same object of the same type, or they're 2 different
131 * objects but of *different* types.
132 * - This must be the case at each given point in time.
133 *
134 * When designing Flow-IPC, we decided that if would be helpful for humans if we immediately made this more stringent
135 * by enforcing the following stronger requirement as well:
136 * - 2 names are to be equal if and only if they are referring to objects of the same (resource) type. So then,
137 * e.g., if a SHM-area is named "X" then a POSIX queue cannot also be named "X" at the same point in time.
138 *
139 * Further, some things are made easier if the distinctness restriction (2 distinct names <=> 2 distinct objects
140 * at each given point in time) is made more stringent by extending it to *all time*; resulting in:
141 * - 2 names are to be equal if and only if they refer to the same object of the same type.
142 * - This must be the case over all time (not merely a each *given* point in time).
143 * - In other words, if a name "X" is used for some object of some type, then it's only *ever* used for that
144 * object and never another (until reboot).
145 *
146 * @note Shared resources are assumed to be *kernel-persistent*, which means the slate is wiped with each system boot.
147 * Hence "all time" really means "within the relevant uptime."
148 *
149 * Practically speaking, then, how do we enable these distinctness guarantees, in absolute names (i.e., names to be
150 * passed, usually internally by Flow-IPC, into sys calls/similar)? Firstly, the resource type for the object
151 * referred to by each absolute name should be encoded in a standard prefix inside the name; a simple distinct constant
152 * for each resource type works. There should be a handful at most of these possible values. This will guarantee
153 * non-clashing between different types of resource.
154 *
155 * That only leaves the distinct-across-all-time requirement. However, it's outside the scope of this header to
156 * explain how that's achieved, as it involves ipc::session and other concepts and is just a large topic.
157 *
158 * For convenience, we now summarize the resulting absolute shared name convention, as of this writing, without a full
159 * rationale and with references to other doc headers if appropriate. In order, the characters of a shared name
160 * in the Flow-IPC convention are as follows. Use session::build_conventional_shared_name() to construct a
161 * path with these semantics.
162 *
163 * -# Leading #S_SEPARATOR. Indicates a full (absolute) name or prefix thereof.
164 * -# A certain constant magic string, #S_ROOT_MAGIC, indicating ::ipc is operating.
165 * (E.g., if... uh... emacs likes to put stuff in, say, SHM, at a certain SHM path, hopefully it won't clash
166 * with us accidentally.)
167 * -# A #S_SEPARATOR.
168 * -# Hard-coded resource type constant. Indicates resource type. E.g., something like "posixq" might mean a
169 * POSIX message queue. The (quite few) possible values for this are stored internally in Flow-IPC.
170 * -# A #S_SEPARATOR.
171 * -# Session-server application name. This brief name is 1-1 with all distinct executables supported by Flow-IPC.
172 * See ipc::session for details.
173 * -# A #S_SEPARATOR.
174 * -# Server-namespace. This uniquely identifies the server application *instance* across all time, for a given
175 * session-server application name. See ipc::session for how this value is computed.
176 * In the rare case where the resource applies to *all* server-namespaces, use special value #S_SENTINEL.
177 * -# A #S_SEPARATOR.
178 * -# What comes next depends on the *scope* of this particular resource.
179 * - Relevant to a given session A-B (where A and B are processes that can converse with each other),
180 * a/k/a *per-session scope*:
181 * -# Session-client application name. Analogous to session-server application name but for the supported
182 * clients. See ipc::session for details.
183 * -# A #S_SEPARATOR.
184 * -# Client-namespace. Given everything up to this point, uniquely identifies client application *instance*
185 * across all time (like server-namespace above, basically). See ipc::session for how it's computed.
186 * - Relevant to no particular process pair A-B, but does apply only to a given session-client application,
187 * a/k/a *per-client-app scope*:
188 * -# Session-client application name, as just above.
189 * -# A #S_SEPARATOR.
190 * -# The reserved client-namespace, #S_SENTINEL, indicating no particular session-client.
191 * - Global (not limited to any smaller scope above), a/k/a *global scope*. This is intended largely for
192 * internal Flow-IPC needs, as of this writing.
193 * -# The reserved session-client application name, #S_SENTINEL, indicating no particular client application.
194 * -# A #S_SEPARATOR.
195 * -# The rest of the name depends on the convention decided for that particular resource type and hence outside
196 * our scope here; see doc header for the relevant class corresponding to a shared resource type of interest.
197 * - For each object, determine the *scope* (the 3 possibilities and resulting naming conventions are just above).
198 * - Keep using the basic folder convention, with non-empty folder names and #S_SEPARATOR as separator.
199 * - When uniqueness is desired, the `1`->`2`->`3`... technique is typically used (always remembering that
200 * such an auto-ID must be safely generated across all participating processes in system).
201 *
202 * In rare cases a Shared_name will be used outside the ipc::session paradigm, such as for a lower-level purpose
203 * that nevertheless requires a shared-resource name (e.g., for a lower-level-use SHM pool). In that case
204 * the following shall be equal to #S_SENTINEL: session-server application name; server-namespace. The components
205 * subsequent to that shall be as-if it's a global-scope resource; hence: #S_SENTINEL.
206 * build_conventional_non_session_based_share_name() shall construct such a name.
207 *
208 * @note *camelCase*: As of this writing, #S_SEPARATOR is `_` (because `/` is not available for native reasons).
209 * So, when it comes up, the convention is to use camelCase for tokens between nearby separators. Briefly:
210 * start with lower case; do not word-smoosh; each new word after word 1 is to begin with exactly 1 capital or
211 * number; an acronym is to be treated as a single word. Examples of compliant tokens:
212 * `httpUrlConnectionV44Beta`, `tableIdent`. These are bad: `httpURLConnection` (wrong acronym convention),
213 * `HTTPURLConnection` (ditto), `tableident` (word-smooshing).
214 *
215 * ### Rationale for max length handling ###
216 * As noted in the public section of this doc header: #S_MAX_LENGTH is a part of sanitized() criteria; it is not
217 * enforced by Shared_name itself outside of sanitized() and sanitize(). It is up to the Shared_name user (which
218 * may, and usually is, internal Flow-IPC code) to actually ensure sanitized() is `true` -- or otherwise avoid system
219 * API errors on account of bad names. All *we* choose to do, regarding the length aspect, is choose one conservatively
220 * low so that:
221 * - At least it is no greater than the *lowest* inherent limit out of all the known resource types to which
222 * Shared_name apply.
223 * - Additionally, because in many cases a given `public` API takes a relative Shared_name and pre-pends an
224 * absolute "folder" path of known length, further leave slack to make it so that that prefix can still comfortably
225 * fit without hitting the limit from the previous bullet point.
226 *
227 * To wit, then, current tally of relevant resource types:
228 * - POSIX message queue (`man mq_overview`): Linux: An internal forward-slash, included in `NAME_MAX = 255`
229 * characters including NUL; so apparently: 253. (The `man` text could be interpreted as meaning 253, 254, 255;
230 * it is ambiguous. Experiments show it's 253.)
231 * - SHM pool (ultimately: `man shm_open`): Linux: Same as preceding: 253.
232 * - bipc message queue (search: boost.interprocess `message_queue`): It just opens a SHM pool per MQ; so
233 * same as preceding: 253.
234 * - Unix domain sockets (ultimately: `man unix`): Linux, abstract namespace: It's a hairy topic. `sun_path[108]`
235 * is the case as of this writing; minus 1 leading and trailing NUL; and there's a note some (non-Linux?) Unix
236 * domain socket implementations go as low as `sun_path[92]`. But in Linux: 106.
237 *
238 * So that puts us at 107 without the "prefix slack." Applying a prefix slack of 32 characters gives us: 107 - 32 =
239 * 75.
240 */
242{
243public:
244 // Constants.
245
246 /// A (default-cted) Shared_name. May be useful for functions returning `const Shared_name&`.
247 static const Shared_name S_EMPTY;
248
249 /**
250 * Max value of size() such that, if str() used to name a supported shared resource, sys call safely won't barf.
251 *
252 * @todo Shared_name::S_MAX_LENGTH currently applies to all shared resource types, but it'd be a useful feature to
253 * have different limits depending on OS/whatever limitations for particular resources types such as SHM
254 * object names versus queue names versus whatever.
255 *
256 * @internal
257 *
258 * @todo Research real limits on Shared_name::S_MAX_LENGTH for different real resource types; choose something for
259 * MAX_LENGTH that leaves enough slack to avoid running into trouble when making actual sys calls; as discussed in
260 * the at-internal section of Shared_name doc header about this topic. Explain here how we get to the
261 * limit ultimately chosen. The limit as of this writing is 64, but real research is needed.
262 */
263 static const size_t S_MAX_LENGTH;
264
265 /// Character we use, by convention, to separate conceptual folders within str().
266 static const char S_SEPARATOR;
267
268 /**
269 * A Shared_name fragment, with no #S_SEPARATOR characters inside, to be used in any Shared_name maintained by
270 * ::ipc itself as the leading path component.
271 */
273
274 /**
275 * A Shared_name fragment, with no #S_SEPARATOR characters inside, that represents a path component that shall
276 * be different from any other generated string at the same path depth in the same context; and represents
277 * a sentinel. In actual fact it equals `0`; and typically (though not necessarily -- other conventions may exist)
278 * generated strings shall be `1`, `2`, ..., of which the former is #S_1ST_OR_ONLY.
279 */
281
282 /**
283 * A Shared_name fragment, with no #S_SEPARATOR characters inside, that represents a path component that
284 * (1) is not #S_SENTINEL and (2) is suggested as the *first* or *only* unique ID of
285 * items at the same depth in the same context. In actual fact it equals `1`; and typically (though not
286 * necessarily -- other conventions may exist) generated strings shall be `1`, `2`, ..., of which
287 * the former is #S_1ST_OR_ONLY.
288 *
289 * So if #S_SENTINEL means "not a thing" or "special thing to be treated not like a normal thing",
290 * then #S_1ST_OR_ONLY means "a normal thing, of which there may be only one, and this is the first one
291 * so created."
292 *
293 * ### Suggested use pattern ###
294 * This is most useful when, at this path level, you currently only have reason to have one object.
295 * Then you can name it #S_1ST_OR_ONLY without relying on a magic string `1`. However, if in the future
296 * there is reason to add more objects at the same level, then remove the use of `S_1ST_OR_ONLY` and instead
297 * use a `uint` or `atomic<uint>` data member that starts at simply the numeric `1`, `++`ing it each time a
298 * new object is added (and converting it via `Shared_name::ct(std::to_string(x))` to string).
299 * The first object will have the same name as before, compatibly, while subsequent ones will be
300 * efficiently named uniquely.
301 *
302 * Is this cheesy and reliant on the fact that this constant is, in fact, a string conversion of the number one?
303 * Yes, but the above pattern works and is reasonably efficient. I (ygoldfel) considered making this
304 * a super-general API that keeps generating unique IDs, but it just seemed like overkill given the simplicity
305 * of the task. Converting back from Shared_name, generating a new `++`ed one, then converting back -- while
306 * providing a simple-enough API to ensure atomicity -- seemed inferior to just letting people
307 * maintain a compatible `atomic<uint>`, when and if desired, and using #S_1ST_OR_ONLY until then.
308 */
310
311 /// Relative-folder fragment (no separators) identifying the resource type for: SHM pools.
313
314 /// Relative-folder fragment (no separators) identifying the resource type for: boost.interprocess named mutex.
316
317 // Constructors/destructor.
318
319 /// Constructs empty() name.
321
322 /**
323 * Copy-constructs from an existing Shared_name.
324 *
325 * @param src
326 * Source object.
327 */
329
330 /**
331 * Move-constructs from an existing Shared_name, which is made empty() if not already so.
332 *
333 * @param src_moved
334 * Source object, which is potentially modified.
335 */
337
338 /**
339 * Copy-constructs from a `char` range given as a pair of random-iterators; in particular `const char*`s work.
340 *
341 * @tparam Input_it
342 * An STL-compliant random iterator type. In particular `const char*` works.
343 * @param begin
344 * Start of range to copy.
345 * @param end
346 * One past last element in range to copy (`begin` to copy nothing).
347 */
348 template<typename Input_it>
349 explicit Shared_name(Input_it begin, Input_it end);
350
351 // `static` ctors.
352
353 /**
354 * Copy-constructs from a `char`-sequence container (including `string`, util::String_view, `vector<char>`).
355 * This overload shall be used on a non-`&&` arg if `ct(const char*)` does not apply.
356 *
357 * Specifically the returned object's internal `std::string` is constructed as: `std::string s(raw_name)`.
358 * As a result, whatever most-performant available single-arg ctor `basic_string` makes available is forwarded-to.
359 * (E.g., C++17 has a `String_view_like` ctor which is overload-resolved-to only when it most makes sense.)
360 *
361 * ### Rationale ###
362 * See class doc header for rationale as-to why this is a `static` ctor as opposed to a regular ctor.
363 *
364 * @tparam Source
365 * See above.
366 * @param src
367 * String to copy.
368 * @return The new object.
369 */
370 template<typename Source>
371 static Shared_name ct(const Source& src);
372
373 /**
374 * Copy-constructs from a NUL-terminated `const char*` string.
375 *
376 * ### Rationale ###
377 * See class doc header for rationale as-to why this is a `static` ctor as opposed to a regular ctor.
378 *
379 * @tparam Source
380 * See above.
381 * @param src
382 * String to copy.
383 * @return The new object.
384 */
385 static Shared_name ct(const char* src);
386
387 /**
388 * Destructively move-constructs from an `std::string`, emptying that source object.
389 *
390 * ### Rationale ###
391 * See class doc header for rationale as-to why this is a `static` ctor as opposed to a regular ctor.
392 *
393 * @param src_moved
394 * String to move (make-`.empty()`).
395 * @return The new object.
396 */
397 static Shared_name ct(std::string&& src_moved);
398
399 // Methods.
400
401 /**
402 * Copy-assigns from an existing Shared_name.
403 *
404 * @param src
405 * Source object.
406 * @return `*this`.
407 */
409
410 /**
411 * Move-assigns from an existing Shared_name.
412 *
413 * @param src_moved
414 * Source object, which is potentially modified.
415 * @return `*this`.
416 */
418
419 /**
420 * Returns (sans copying) ref to immutable entire wrapped name string, suitable to pass into sys calls when naming
421 * supported shared resources assuming `absolute() == true`. If you require a NUL-terminated string (such as for
422 * a native call), use native_str().
423 *
424 * @note Returning a util::String_view seemed pointless, as one can always be constructed easily by the caller, and
425 * in any case -- for the time being at least -- some/many APIs that take non-C-strings will take `std::string`
426 * as opposed to a util::String_view.
427 *
428 * @return See above.
429 */
430 const std::string& str() const;
431
432 /**
433 * Returns (sans copying) pointer to NUL-terminated wrapped name string, suitable to pass into sys calls when naming
434 * supported shared resources assuming `absolute() == true`. If you require an `std::string` (such as for some fancy
435 * boost.interprocess call) See also str().
436 *
437 * ### Why isn't it named `c_str()`? ###
438 * It was, but `capnp::StringPtr` (and similar/derived classes such as `capnp::Text::Reader`) has a "clever"
439 * `operator T()` conversion operator that is enabled for all `T` that have `.c_str()`; and its implementation
440 * relies on being able to symmetrically construct `T(const char*)` -- like `std::string`. We intentionally
441 * lack that. Hence renamed this away from `.c_str()`. This may appear like kow-towing to capnp's quirks,
442 * but actually conceivably that's a pattern people use, so let's not break it.
443 *
444 * @return See above.
445 */
446 const char* native_str() const;
447
448 /**
449 * Returns `str().size()`.
450 * @return See above.
451 */
452 size_t size() const;
453
454 /**
455 * Returns `true` if and only if `str().empty() == true`.
456 * @return See above.
457 */
458 bool empty() const;
459
460 /**
461 * Returns `true` if and only if `!this->empty()`, and str() ends with the #S_SEPARATOR character.
462 * @return See above.
463 */
464 bool has_trailing_separator() const;
465
466 /**
467 * Returns `true` if and only if the first character is #S_SEPARATOR. By Flow-IPC convention, any name actually
468 * passed to a sys call in order to name a shared resource has `absolute() == true`. Note, however, that absolute()
469 * being `true` does not necessarily mean str() is the full name of a resource: it may well still be a fragment
470 * (a prefix) of some eventual name passed to a sys call. To obtain the full name one would append more stuff.
471 *
472 * @return See above.
473 */
474 bool absolute() const;
475
476 /// Makes it so `empty() == true`.
477 void clear();
478
479 /**
480 * Returns `true` if and only if the contained name/fragment is *sanitized* according to length, legal characters,
481 * and similar. More precisely, a sanitized `*this` satisfies all of the following:
482 * - There are no illegal characters. (E.g., in particular, some characters would not be accepted when naming
483 * a SHM object in Linux.)
484 * - In particular, only #S_SEPARATOR is used as a folder separator.
485 * - There is no more than 1 instance of #S_SEPARATOR character in a row.
486 * - size() does not exceed #S_MAX_LENGTH.
487 *
488 * ### Performance ###
489 * It is linear-time, with at most one scan through str().
490 *
491 * @return See above.
492 */
493 bool sanitized() const;
494
495 /**
496 * Best-effort attempt to turn sanitized() from `false` to `true`, unless it is already `true`; returns the final
497 * value of sanitized() indicating whether it was successful. If `false` returned, then the final value of str()
498 * will equal the initial value.
499 *
500 * This utility should be used very judiciously and with full knowledge of what it actually does. It should *not* be
501 * used "just in case" or "prophylactically" but only with full knowledge of where the current value of str() might
502 * originate. For example, it might come from some user input. Another example involves, perhaps, concatenating
503 * two path fragments in such a way as to potentially yield a double-#S_SEPARATOR situation: sanitize() after this
504 * would get collapse the separators into just 1 separator. Yet another example is simply that if one combines
505 * two paths which don't exceed #S_MAX_LENGTH, but the result might exceed it: running sanitize() and ensuring it
506 * returns `true` guaranteed one didn't accidentally exceed #S_MAX_LENGTH.
507 *
508 * In other words, use it when you want it to do what it, in fact, does. And that is:
509 * - Any '/' (forward-slash) character, as a special case, is transformed into #S_SEPARATOR.
510 * - After any character replacements above: Any sequence of 2 or more #S_SEPARATOR characters is collapsed into
511 * one #S_SEPARATOR.
512 *
513 * Things it does *not* do, except the above:
514 * - No legal or illegal character is changed (except '/' and separator collapsing).
515 * - It doesn't truncate to try to bring length to #S_MAX_LENGTH or less.
516 *
517 * However, the method's return value is still significant, in that if it is "still" `false`, then you know you have
518 * a real problem -- yet if it's `true`, then it didn't do anything destructive to make it so.
519 *
520 * Note, again, that the above replacements are "undone" if `false` is returned. In other words, the function
521 * won't sanitize "halfway."
522 *
523 * ### Performance ###
524 * There is at most one linear scan through str(). In addition, though only if actual sanitizing (by changing
525 * str()) might be necessary: an allocation, copy of str(), and deallocation may be performed.
526 * Overall, it is linear-time regardless, plus those potential alloc/dealloc ops.
527 *
528 * @return What sanitized() would return just before returning from the present function.
529 */
530 bool sanitize();
531
532 /**
533 * Appends a folder separator followed by the given other Shared_name.
534 * Functionally equivalent to `return *this += (string(1, S_SEPARATOR) + src_to_append.str());`.
535 * If there's any reason to suspect `this->str()` already ends in #S_SEPARATOR, and/or that the resulting name
536 * might be too long, execute sanitize() afterwards and ensure that returns `true`.
537 *
538 * It is stylistically (and possibly for performance) better to use this rather than manually appending
539 * a separator and then `src_to_append` (with `+=`).
540 *
541 * ### Rationale for not doing something smarter like avoiding a double-separator due to concatenation ###
542 * Basically we want things to be as fast as possible by default; and that is to make it as close to trivial
543 * string concatenation as possible. If more paranoia is required, we want the user to intentionally use
544 * sanitize() or sanitized().
545 *
546 * @param src_to_append
547 * Thing to append after appending separator.
548 * @return `*this`.
549 *
550 * @internal
551 * ### Rationale for having this `/=` as opposed to only the version taking `Source`, or vice versa ###
552 * If we had only the one taking Shared_name, then tried to use `+= X`, where `X` is `std::string` or `const char*`
553 * or util::String_view, it'd have to (implicitly) construct a Shared_name first, and that is a bit slower.
554 *
555 * If we had only the one taking `Source` (e.g., `String_view`), and user wanted to append a Shared_name, they'd
556 * have to use `X.str()` explicitly, losing a bit of syntactic sugar.
557 *
558 * Same deal with `+=` and the binary (`+`, `/`) derivations of these.
559 */
560 Shared_name& operator/=(const Shared_name& src_to_append);
561
562 /**
563 * Simply appends a folder separator followed by `raw_name_to_append` to the current value of str().
564 * Specifically the internal `std::string` is modified as: `s += S_SEPARATOR; s += raw_name_to_append`.
565 * As a result, whatever most-performant available (single-arg by definition) `operator+=` that `basic_string`
566 * makes available is forwarded-to for the 2nd `+=`. (E.g., C++17 has a `String_view_like` appender which is
567 * overload-resolved-to only when it most makes sense.)
568 *
569 * If there's any reason to suspect `this->str()` already ends in #S_SEPARATOR, and/or that the resulting name
570 * might be too long, execute sanitize() afterwards and ensure that returns `true`.
571 *
572 * It is stylistically (and possibly for performance) better to use this rather than manually appending
573 * a separator and then `src_to_append` (with `+=`).
574 *
575 * See Rationale(s) for the other operator/=(), as they apply here.
576 *
577 * @tparam Source
578 * See above.
579 * @param raw_name_to_append
580 * Thing to append after appending separator.
581 * @return `*this`.
582 */
583 template<typename Source>
584 Shared_name& operator/=(const Source& raw_name_to_append);
585
586 /**
587 * Similar to the overload that takes `const Source&`, but takes NUL-terminated string instead. See that doc header.
588 *
589 * @param raw_name_to_append
590 * Thing to append after appending separator.
591 * @return `*this`.
592 */
593 Shared_name& operator/=(const char* raw_name_to_append);
594
595 /**
596 * Appends the given other Shared_name.
597 * Functionally equivalent to `return *this += src_to_append.str());`.
598 * If there's any reason to suspect the resulting name might be too long, execute sanitize() afterwards and ensure
599 * that returns `true`.
600 *
601 * It is stylistically (and possibly for performance) better to use `/=` rather than manually appending
602 * a separator and then `src_to_append` (with this `+=`).
603 *
604 * See Rationale(s) for operator/=(), as they apply here.
605 *
606 * @param src_to_append
607 * Thing to append.
608 * @return `*this`.
609 */
610 Shared_name& operator+=(const Shared_name& src_to_append);
611
612 /**
613 * Simply appends `raw_name_to_append` to the current value of str(). Specifically the internal
614 * `std::string` is modified as: `s += raw_name_to_append`. As a result, whatever most-performant available
615 * (single-arg by definition) `operator+=` that `basic_string` makes available is forwarded-to.
616 * (E.g., C++17 has a `String_view_like` appender which is overload-resolved-to only when it most makes sense.)
617 *
618 * If there's any reason to suspect the resulting name might be too long, execute sanitize() afterwards and ensure
619 * that returns `true`.
620 *
621 * It is stylistically (and possibly for performance) better to use `/=` rather than manually appending
622 * a separator and then `src_to_append` (with this `+=`).
623 *
624 * See Rationale(s) for operator/=(), as they apply here.
625 *
626 * @tparam Source
627 * See above.
628 * @param raw_name_to_append
629 * Thing to append.
630 * @return `*this`.
631 */
632 template<typename Source>
633 Shared_name& operator+=(const Source& raw_name_to_append);
634
635 /**
636 * Similar to the overload that takes `const Source&`, but takes NUL-terminated string instead. See that doc header.
637 *
638 * @param raw_name_to_append
639 * Thing to append.
640 * @return `*this`.
641 */
642 Shared_name& operator+=(const char* raw_name_to_append);
643
644private:
645 // Friends.
646
647 // Friend for access to Shared_name.
648 friend void swap(Shared_name& val1, Shared_name& val2);
649
650 // Data.
651
652 /**
653 * The name or name fragment; see str().
654 * @todo Consider providing a ref-to-mutable accessor to Shared_name::m_raw_name (or just make `public`).
655 * There are pros and cons; the basic pro being that Shared_name is meant to be a very thin wrapper around
656 * `std::string`, so it might make sense to allow for in-place modification without supplying some kind of reduced
657 * subset of `string` API. Suggest doing this to-do if a practical desire comes about.
658 */
659 std::string m_raw_name;
660}; // class Shared_name
661
662// Free functions: in *_fwd.hpp.
663
664// Template implementations.
665
666template<typename Input_it>
667Shared_name::Shared_name(Input_it begin, Input_it end) :
668 m_raw_name(begin, end) // Copy it.
669{
670 // Nothing.
671}
672
673template<typename Source>
674Shared_name Shared_name::ct(const Source& src) // Static.
675{
676 Shared_name result;
677
678 /* As advertised: invoke whatever `string` ctor best applies. Well, actually, to avoid needing to move-assign
679 * (which involves a few scalar copies), use .assign() instead; but its complement of overloads equals that
680 * of the relevant complement of ctor overloads. */
681 result.m_raw_name.assign(src);
682
683 return result;
684}
685
686template<typename Source>
687Shared_name& Shared_name::operator+=(const Source& raw_name_to_append)
688{
689 // Existence/impl rationale: This is faster than if they had to: `+= Shared_name::ct(raw_name_to_append)`.
690
691 m_raw_name += raw_name_to_append;
692 return *this;
693}
694
695template<typename Source>
696Shared_name& Shared_name::operator/=(const Source& raw_name_to_append)
697{
698 // Existence/impl rationale: This is faster than if they had to: `/= Shared_name::ct(raw_name_to_append)`.
699
700 /* m_raw_name.reserve(m_raw_name.size() + 1 + raw_name_to_append.size()); // Tiny optimization, as 2 appends follow.
701 * Oops; can't do that; `Source` could be, e.g., `char`. @todo Reconsider. */
702
704 return operator+=(raw_name_to_append);
705}
706
707template<typename Source>
708Shared_name operator+(const Shared_name& src1, const Source& raw_src2)
709{
710 // Existence/impl rationale: This is faster than if they had to: `src1 + Shared_name::ct(raw_src2)`.
711
712 return Shared_name(src1) += raw_src2;
713}
714
715template<typename Source>
716Shared_name operator+(const Source& raw_src1, const Shared_name& src2)
717{
718 // Existence rationale: For symmetry with overload: (src1, raw_src2).
719
720 return Shared_name::ct(raw_src1) += src2;
721}
722
723template<typename Source>
724Shared_name operator/(const Shared_name& src1, const Source& raw_src2)
725{
726 // Existence/impl rationale: This is faster than if they had to: `src1 / Shared_name::ct(raw_src2)`.
727
728 return Shared_name(src1) /= raw_src2;
729}
730
731template<typename Source>
732Shared_name operator/(const Source& raw_src1, const Shared_name& src2)
733{
734 // Existence rationale: For symmetry with overload: (src1, raw_src2).
735
736 return Shared_name::ct(raw_src1) /= src2;
737}
738
739template<typename Persistent_object, typename Filter_func>
740unsigned int remove_each_persistent_if(flow::log::Logger* logger_ptr, const Filter_func& filter_func)
741{
742 // This is pretty much completely described by the doc header; so keeping comments light.
743
744 Error_code err_code;
745 unsigned int count = 0;
746
747 Persistent_object::for_each_persistent([&](const Shared_name& name)
748 {
749 if (filter_func(name))
750 {
751 Persistent_object::remove_persistent(logger_ptr, name, &err_code);
752 if (!err_code)
753 {
754 ++count;
755 }
756 // else { Probably permission error; but regardless just move past it as advertised. }
757 }
758 });
759
760 return count;
761} // remove_each_persistent_if()
762
763template<typename Persistent_object>
764unsigned int remove_each_persistent_with_name_prefix(flow::log::Logger* logger_ptr,
765 const Shared_name& name_prefix_or_empty)
766{
767 using util::String_view;
768
769 FLOW_LOG_SET_CONTEXT(logger_ptr, Log_component::S_TRANSPORT);
770 FLOW_LOG_TRACE("Will attempt to remove-persistent objects (type [" << typeid(Persistent_object).name() << "]) with "
771 "prefix [" << name_prefix_or_empty.str() << "] (<-- may be blank; then all are removed).");
772
773 const String_view name_prefix_view(name_prefix_or_empty.str());
774 const auto count = remove_each_persistent_if<Persistent_object>(get_logger(), [&](const Shared_name& name)
775 {
776 return name_prefix_view.empty() || String_view(name.str()).starts_with(name_prefix_view);
777 });
778
779 if (count != 0)
780 {
781 FLOW_LOG_INFO("Removed-persistent successfully [" << count << "] objects "
782 "(type [" << typeid(Persistent_object).name() << "]) with "
783 "prefix [" << name_prefix_or_empty << "] (<-- may be blank; then all were potentially removed). "
784 "Note this counts only successful removals (not ones that failed due to permissions, say). "
785 "Details potentially logged above (including any errors which are otherwise ignored).");
786 }
787
788 return count;
789} // remove_each_persistent_with_name_prefix()
790
791} // 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...
Shared_name & operator=(Shared_name &&src_moved)
Move-assigns from an existing Shared_name.
Shared_name & operator+=(const Shared_name &src_to_append)
Appends the given other Shared_name.
Definition: shared_name.cpp:77
Shared_name(const Shared_name &src)
Copy-constructs from an existing Shared_name.
Shared_name()
Constructs empty() name.
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 (including string, 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 const Shared_name S_EMPTY
A (default-cted) Shared_name. May be useful for functions returning const Shared_name&.
static const char S_SEPARATOR
Character we use, by convention, to separate conceptual folders within str().
bool has_trailing_separator() const
Returns true if and only if !this->empty(), and str() ends with the S_SEPARATOR character.
Shared_name(Shared_name &&src_moved)
Move-constructs from an existing Shared_name, which is made empty() if not already so.
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:91
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...
const std::string & str() const
Returns (sans copying) ref to immutable entire wrapped name string, suitable to pass into sys calls w...
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().
static const size_t S_MAX_LENGTH
Max value of size() such that, if str() used to name a supported shared resource, sys call safely won...
util::Shared_name Shared_name
Convenience alias for the commonly used type util::Shared_name.
Flow-IPC module containing miscellaneous general-use facilities that ubiquitously used by ~all Flow-I...
unsigned int remove_each_persistent_if(flow::log::Logger *logger_ptr, const Filter_func &filter_func)
Utility that invokes Persistent_object::for_each_persistent(name_prefix_or_empty) and synchronously i...
Shared_name operator/(const Shared_name &src1, const char *raw_src2)
Returns new object equal to Shared_name(src1) /= raw_src2.
unsigned int remove_each_persistent_with_name_prefix(flow::log::Logger *logger_ptr, const Shared_name &name_prefix_or_empty)
Utility that invokes remove_each_persistent_if() with the filter that returns true (yes,...
flow::util::String_view String_view
Short-hand for Flow's String_view.
Definition: util_fwd.hpp:109
Shared_name operator+(const Shared_name &src1, const char *raw_src2)
Returns new object equal to Shared_name(src1) += raw_src2.
Definition: shared_name.cpp:97
flow::Error_code Error_code
Short-hand for flow::Error_code which is very common.
Definition: common.hpp:297