randolf.ca  1.00
Randolf Richardson's C++ classes
Loading...
Searching...
No Matches
rtools
1#pragma once
2
3#include <array> // std::array
4#include <cstring> // std::strlen
5#include <ctime> // std::time
6#include <exception> // std::exception::runtime_error
7#include <iostream> // std::cout
8#include <stdexcept> // std::runtime_error
9#include <string> // std::string
10#include <unordered_map> // std::unordered_map
11#include <vector> // std::vector
12
13namespace randolf {
14
15 /*======================================================================*//**
16 @brief
17 Base64 character set, which includes the following characters:
18
19 @c ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
20 @see base64_decode
21 @see base64_encode
22 *///=========================================================================
23 static const std::string base64_plus_slash = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
24
25 /*======================================================================*//**
26 @brief
27 This @ref rtools class primarily provides a collection of static methods that
28 facilitate a variety of general-purpose computer programming needs. Separate
29 classes may also be added in the future for more sophisticated needs.
30 @par History
31 2023-May-17 rtools v1.00 by Randolf Richardson.
32 *///=========================================================================
33 class rtools {
34
35 public:
36 /*======================================================================*//**
37 @brief
38 Split string into an @c std::vector that's perfectly-sized to store all the
39 elements separated by whitespace, but not whitespace that's enclosed within
40 quotation marks (literal quotation marks - @c " - will not be intrepreted,
41 and will be treated as literal/non-functional non-whitespace characters).
42
43 Any leading and/or trailing whitespace characters will be ignored.
44
45 Multiple whitespace delimiters will be treated as a single delimiter.
46
47 Whitespace characters:
48 - 0: NULL
49 - 9: Tab
50 - 10: Linefeed
51 - 13: Carriage Return
52 - 32: Space
53
54 @returns Pointer to an array of "atoms" stored in an @c std::vector object
55 *///=========================================================================
56 static std::vector<std::string>* atomize(
57 /// Source string (to be split into atoms)
58 const char* str,
59 /// Length of string (in bytes), or 0 if @c str is an ASCIIZ string (NULL-terminated)
60 size_t len = 0) { // TODO: Add support for a "maximum_elements" parameter
61
62 // --------------------------------------------------------------------------
63 // Internal variables.
64 //
65 // Includes not measuring size of string if an ASCIIZ string was indicated
66 // because "if (len == 0) len = std::strlen(str);" is less optimal than what
67 // the main loop already does in this regard.
68 // --------------------------------------------------------------------------
69 const char* MAX = len > 0 ? str + len - 1 : (char*)-1; // Loop optimization
70 bool w = true; // Begin in whitespace mode to skip leading whitespace
71 std::string* atom = new std::string();
72
73 // --------------------------------------------------------------------------
74 // Pre-allocate std::vector with a size that's half the length of the string,
75 // because this is the maximum number of atoms there could be if each atom is
76 // 1 byte long and each delimiting whitespace character is also 1 byte long.
77 // --------------------------------------------------------------------------
78 std::vector<std::string>* ary = new std::vector<std::string>;
79 if (len > 0) ary->reserve(len >> (1 + 1)); // Ensure array-like efficiency
80
81 // --------------------------------------------------------------------------
82 // Main loop.
83 //
84 // Note: See notes about memory leak prevention above the "done:" label that
85 // immediately follows this loop.
86 // --------------------------------------------------------------------------
87 do { // TODO: Add support for quotation marks
88 switch (*str) {
89 case '\0': // Whitespace: NULL
90 if (len == 0) goto done; // End of ASCIIZ string
91 case ' ': // Whitespace: Space
92 case '\t': // Whitespace: Tab
93 case '\r': // Whitespace: Carriage Return
94 case '\n': // Whitespace: Linefeed
95 if (!w) {
96 ary->push_back(*atom); // Save current atom to vector
97 w = true; // Enable whitespace mode
98 } // -x- if !w -x-
99 break;
100 default:
101 if (w) {
102 atom = new std::string(); // Create new atom (string)
103 w = false; // Disable whitespace mode
104 } // -x- if w -x-
105 atom->append(str, 1); // Add this character to the atom
106 } // -x- swtich str -x-
107 } while (str++ < MAX); // -x- while str -x-
108
109 done:
110 // --------------------------------------------------------------------------
111 // If we're not in whitespace mode, and the current string is not empty, then
112 // add it to the vector as the final atom.
113 //
114 // Memory leak prevention (we don't need any free radicals taking up memory!
115 // {yes, I had fun writing this, and whether or not you like it, you can't
116 // deny the cleverness of this pun}): Because of the way the loop (above) is
117 // structured, there will never be a memory leak here resulting from trailing
118 // whitespace because a new atom is created only when a first non-whitespace
119 // character is encountered.
120 // --------------------------------------------------------------------------
121 if (!w && !atom->empty()) ary->push_back(*atom);
122
123 // --------------------------------------------------------------------------
124 // Shrink the array before returning it so sizing can be considered properly
125 // and unneeded memory can be freed.
126 // --------------------------------------------------------------------------
127 ary->shrink_to_fit(); // TODO: Make this optional with a flag called RTOOLS_SHRINK
128
129 return ary;
130 } // -x- atomize -x-
131
132 /*======================================================================*//**
133 @copydoc atomize(const char*, const size_t)
134 *///=========================================================================
135 static std::vector<std::string>* atomize(
136 /// Source string (to be split into atoms)
137 std::string str,
138 /// Length of string (in bytes), or 0 if @c str is an ASCIIZ string (NULL-terminated)
139 size_t len = 0) {
140 return atomize(str.c_str(), len > 0 ? len : str.size());
141 } // -x- atomize -x-
142
143 /*======================================================================*//**
144 @brief
145 Decode a Base64-encoded @c std::string.
146
147 All invalid characters are simply ignored.
148 @returns Decoded string
149 @see base64_encode
150 *///=========================================================================
151 static std::string base64_decode(
152 /// String to decode
153 const std::string &in,
154 /// Base64 character set to use
155 const std::string b = base64_plus_slash) {
156 std::string out;
157 std::vector<int> T(256, -1);
158 int val = 0;
159 int valb = -8;
160
161 for (int i = 0; i < 64; i++) T[b[i]] = i;
162 for (unsigned char c: in) {
163 if (T[c] == -1) break;
164 val = (val << 6) + T[c];
165 valb += 6;
166 if (valb >= 0) {
167 out.push_back(char((val >> valb) & 0xFF));
168 valb -= 8;
169 } // -x- if valb -x-
170 } // -x- for c -x-
171 return out;
172 } // -x- std::string base64_decode -x-
173
174 /*======================================================================*//**
175 @brief
176 Encode an @c std::string into Base64 format.
177
178 All invalid characters are simply ignored.
179 @returns Base64-encoded string
180 @see base64_decode
181 *///=========================================================================
182 static std::string base64_encode(
183 /// String to encode
184 const std::string &in,
185 /// Base64 character set to use
186 const std::string b = base64_plus_slash) {
187 std::string out;
188 int val = 0;
189 int valb =- 6;
190
191 for (unsigned char c: in) {
192 val = (val << 8) + c;
193 valb += 8;
194 while (valb >= 0) {
195 out.push_back(b[(val >> valb) & 0x3F]);
196 valb -= 6;
197 } // -x- while valb -x-
198 } // -x- for c -x-
199 if (valb >- 6) out.push_back(b[((val << 8) >> (valb + 8)) & 0x3F]);
200 while (out.size() % 4) out.push_back('=');
201 return out;
202 } // -x- std::string base64_encode -x-
203
204 /*======================================================================*//**
205 @brief
206 Insert commas into the last numeric sequence of digits in the supplied string
207 and insert spaces before that (commas and spaces are configurable). If a
208 decimal point is found, then comma insertions will occur before that (this is
209 also configurable).
210 @returns Numeric value as a char* array converted to a properly-delimited
211 string as an std::string
212 *///=========================================================================
213 static std::string insert_commas(
214 /// Pointer to ASCII representation of numeric value
215 const char* value,
216 /// Length of value (in bytes), or 0 to auto-detect length if value string is an ASCIIZ string
217 size_t len = 0,
218 /// Don't insert any commas after the last period (or whatever string is set as the @c dot character)
219 bool skip_dot = true,
220 /// Number of digits between commas
221 const int digits = 3,
222 /// Pointer to ASCIIZ comma character string (nullptr = disabled)
223 const char* comma = ",",
224 /// Pointer to ASCIIZ space character string (nullptr = disabled) used instead of commas for non-digit fill-ins where commas would normally be inserted
225 const char* space = " ",
226 /// Period character used when @c skip_dot is enabled
227 const char dot = '.') noexcept {
228
229 // --------------------------------------------------------------------------
230 // Measure size of format string if an ASCIIZ string was indicated.
231 // --------------------------------------------------------------------------
232 if (len == 0) len = std::strlen(value);
233
234 // --------------------------------------------------------------------------
235 // Find the dot, and adjust len accordingly.
236 // --------------------------------------------------------------------------
237 for (int i = len - 1; i > 0; i--) {
238 if (value[i] == dot) {
239 len = i;
240 break;
241 } // -x- if dot -x-
242 } // -x- for i -x-
243
244 // --------------------------------------------------------------------------
245 // Internal variables.
246 // --------------------------------------------------------------------------
247 std::string v(value, len);
248 const int m = len % digits; // Modulus for every 3rd character
249 bool blank = false; // Add blank space instead of comma
250
251 // --------------------------------------------------------------------------
252 // Insert commas as long as there are digits.
253 // --------------------------------------------------------------------------
254 for (int i = len - 1; i > 0; i--) {
255 if (v[i] < '0' || v[i] > '9') blank = true; // Not a digit, so we're switching from commas to blanks (spaces)
256 if ((i % digits) == m) { // This is where a separator belongs
257 if (!blank && comma != nullptr) v.insert(i, comma); // Insert comma, if one is defined
258 else if (blank && space != nullptr) v.insert(i, space); // Insert space, if one is defined
259 } // -x- if m -x-
260 } // -x- for i -x-
261
262 return v;
263 } // -x- std::string insert_commas -x-
264
265 /*======================================================================*//**
266 @brief
267 Parses a SASL exchange, returning an @c std::unordered_map of the key-value
268 pairs that were encountered.
269 @throws std::runtime_error If a SASL message is improperly formatted (the
270 error message includes the offset where the format problem occurred)
271 *///=========================================================================
272 static std::unordered_map<std::string, std::vector<std::string>> parse_sasl_exchange(
273 /// Unparsed SASL exchange string (must not be Base64-encoded)
274 const std::string sasl,
275 /// Ensure the following keys exist, each with one empty string: nonce, nc, cnonce, qop, realm, username, digest-uri, authzid
276 const bool add_missing_keys = false) {
277
278 // --------------------------------------------------------------------------
279 // Internal variables.
280 // --------------------------------------------------------------------------
281 std::unordered_map<std::string, std::vector<std::string>> map;
282 char ch; // Used in string parsing
283 std::string temp; // Used to build current element
284 std::string key; // Key name
285 bool keyMode = true; // TRUE = parsing key name; FALSE = parsing value
286 bool quoteMode = false; // Used for tracking "quote mode" with quotation mark usage
287 int offset = -1; // Offset within the unparsed string of the character currently being processed
288 const int MAX = sasl.size();
289
290 // --------------------------------------------------------------------------
291 // Parse the string, one character at a time.
292 // --------------------------------------------------------------------------
293 for (int offset = 0; offset < MAX; offset++) {
294 switch (ch = sasl[offset]) {
295 case '"': // Quotation mark
296 if (keyMode) throw std::runtime_error("Key names can't contain quotation marks at offset " + std::to_string(offset));
297 if (!quoteMode && temp.length() > 0) throw std::runtime_error("Malformed quoted value at offset " + std::to_string(offset));
298 quoteMode = !quoteMode; // Toggle "quote mode"
299 temp.push_back(ch); // Save quotation mark
300 break;
301 case '\\': // Back-slash
302 if (keyMode) throw std::runtime_error("Key names can't contain escaped literal characters at offset " + std::to_string(offset));
303 ch = sasl[++offset]; // Get next character after incrementing offset
304 if (offset >= MAX) throw std::runtime_error("Literal character is missing at offset " + std::to_string(offset));
305 if (ch < ' ' || ch == 127) throw std::runtime_error("Invalid charater at offset " + std::to_string(offset)); // Any characters except CTLs and separators
306 temp.push_back('\\'); // Save backslash
307 temp.push_back(ch); // Save literal character
308 break;
309 case ' ': // Space
310 if (!quoteMode) throw std::runtime_error("White space characters not permitted at offset " + std::to_string(offset));
311 temp.push_back(ch);
312 break;
313 case '=': // Equal sign (signifies end of key, and beginning of value)
314 if (quoteMode) {
315 temp.push_back(ch);
316 break;
317 }
318 if (!keyMode) throw std::runtime_error("Invalid character at offset " + std::to_string(offset));
319 keyMode = !keyMode; // Toggle flag
320 if (temp.length() == 0) throw std::runtime_error("Missing key name at offset " + std::to_string(offset));
321 key = temp; // Save string for later
322 temp = ""; // Clear temporary string
323 break;
324 case ',': // Comma delimiter (signifies end of value)
325 if (quoteMode) {
326 temp.push_back(ch);
327 break;
328 }
329 if (keyMode) throw std::runtime_error("Invalid character at offset " + std::to_string(offset));
330 keyMode = !keyMode; // Toggle flag
331 if (temp[0] == '"' && temp[temp.length() - 1] != '"') throw std::runtime_error("Malformed quoted value at offset " + std::to_string(offset));
332 map[key].push_back(temp);
333 temp = "";
334 break;
335 default: // Everything else is a literal character
336 if (ch < ' ' || ch == 127) throw std::runtime_error("Invalid character at offset " + std::to_string(offset)); // Any characters except CTLs and separators
337 if (keyMode && (std::string("()<>@,;:\\\"/[]?={} ").find_first_of(ch) != std::string::npos)) throw std::runtime_error("Invalid character at offset " + std::to_string(offset));
338 temp.push_back(ch);
339 break;
340 } // -x- switch ch -x-
341 } // -x- for ch -x-
342
343 // --------------------------------------------------------------------------
344 // Syntax checks. There is no need to check for additional data because we
345 // added a comma to the unparsed_string -- if the original unparsed_string
346 // had a trailing comma, then an exception would have been thrown already.
347 // --------------------------------------------------------------------------
348 if (quoteMode) throw std::runtime_error("Incomplete value at offset " + std::to_string(offset));
349
350 // --------------------------------------------------------------------------
351 // Save last key-value pair if we're not in keyMode.
352 // --------------------------------------------------------------------------
353 if (!keyMode) map[key].push_back(temp);
354
355 // --------------------------------------------------------------------------
356 // Add missing keys, each with one empty string.
357 // --------------------------------------------------------------------------
358 if (add_missing_keys) {
359 if (map.try_emplace("nonce", std::vector<std::string>()).second) { map["nonce" ].push_back(""); };
360 if (map.try_emplace("nc", std::vector<std::string>()).second) { map["nc" ].push_back(""); };
361 if (map.try_emplace("cnonce", std::vector<std::string>()).second) { map["cnonce" ].push_back(""); };
362 if (map.try_emplace("qop", std::vector<std::string>()).second) { map["qop" ].push_back(""); };
363 if (map.try_emplace("realm", std::vector<std::string>()).second) { map["realm" ].push_back(""); };
364 if (map.try_emplace("username", std::vector<std::string>()).second) { map["username" ].push_back(""); };
365 if (map.try_emplace("digest-uri", std::vector<std::string>()).second) { map["digest-uri"].push_back(""); };
366 if (map.try_emplace("authzid", std::vector<std::string>()).second) { map["authzid" ].push_back(""); };
367 } // -x- if add_missing_keys -x-
368
369 // --------------------------------------------------------------------------
370 // Return the newly-created unordered_map.
371 // --------------------------------------------------------------------------
372 return map;
373
374 } // -x- std::unordered_map<std::string, std::vector<std::string>> parse_sasl_exchange -x-
375
376 /*======================================================================*//**
377 @copydoc split(const char, const char*, const size_t)
378 *///=========================================================================
379 static std::vector<std::string> split(
380 /// Character to use for the delimiter
381 const char delimiter,
382 /// Source string (to be split into atoms)
383 std::string str,
384 /// Length of string (in bytes), or 0 if using the full length of the string
385 const size_t len = -1) {
386 return split(delimiter, str.c_str(), len >= -1 ? str.size() : len);
387 } // -x- std::vector<std::string> split -x-
388
389 /*======================================================================*//**
390 @brief
391 Split string into an @c std::vector that's perfectly-sized to store all the
392 elements separated by a delimiter character. If no delimiters are
393 encountered, the resulting vector will contain the entire string as its only
394 element. If the string is empty, the resulting vector will contain an empty
395 string as its only element.
396
397 @pre
398 Using (char)0 as a delimiter necessitates specifying the length of the source
399 string, otherwise the resulting vector will contain only the first element
400 (this behaviour might change in the future, so don't rely on it).
401
402 @returns Pointer to an array of "atoms" (strings) stored in an @c std::vector
403 object
404 *///=========================================================================
405 static std::vector<std::string> split(
406 /// Character to use for the delimiter
407 const char delimiter,
408 /// Source string (to be split)
409 const char* str,
410 /// Length of string (in bytes), or -1 if @c str is an ASCIIZ string (NULL-terminated)
411 const size_t len = -1) { // TODO: Add support for a "maximum_elements" parameter
412
413 // --------------------------------------------------------------------------
414 // Internal variables.
415 //
416 // Includes not measuring size of string if an ASCIIZ string was indicated
417 // because "if (len == -1) len = std::strlen(str);" is less optimal than what
418 // the main loop already does in this regard.
419 // --------------------------------------------------------------------------
420 const char* MAX = len == -1 ? (char*)-1 : str + len; // Loop pre-optimization
421 std::string* atom = new std::string();
422
423 // --------------------------------------------------------------------------
424 // Pre-allocate std::vector with a size that's half the length of the string,
425 // because this is the maximum number of atoms there could be if each atom is
426 // 1 byte long and each delimiting whitespace character is also 1 byte long.
427 // --------------------------------------------------------------------------
428 std::vector<std::string> ary;
429 if (len > 0) ary.reserve(len >> (1 + 1)); // Ensure array-like efficiency
430
431 // --------------------------------------------------------------------------
432 // Main loops.
433 //
434 // Optimization: By using separate loops for a non-NULL delimiter from the
435 // NULL delimiter, both loops are faster and less complicated.
436 // --------------------------------------------------------------------------
437 if (delimiter != (char)0) { // Process using non-NULL delimiter
438 do {
439 if (*str == delimiter) { // Delimiter character
440 ary.push_back(*atom); // Save current atom to vector
441 atom = new std::string(); // Create new atom (string)
442 } else {
443 atom->append(str, 1); // Add this character to the atom
444 } // -x- if delimiter -x-
445 } while (*str != (char)0 && ++str < MAX); // -x- while str -x-
446
447 // --------------------------------------------------------------------------
448 // If we're not in delimited mode, and the current string is not empty, then
449 // add it to the vector as the final atom.
450 //
451 // Memory leak prevention (we don't need any free radicals taking up memory!
452 // {yes, I had fun writing this, and whether or not you like it, you can't
453 // deny the cleverness of this pun}): Because of the way the loop (above) is
454 // structured, there will never be a memory leak here resulting from trailing
455 // non-delimiter characters because a new atom is created only when a first
456 // delimiter character is encountered.
457 // --------------------------------------------------------------------------
458 ary.push_back(*atom);
459
460 // --------------------------------------------------------------------------
461 // Process NULL delimiter.
462 // --------------------------------------------------------------------------
463 } else { // Process using NULL delimiter
464 do {
465 if (*str == (char)0) { // NULL delimiter
466 if (len == 0) break;
467 ary.push_back(*atom); // Save current atom to vector
468 atom = new std::string(); // Create new atom (string)
469 } else { // -x- if *str -x-
470 atom->append(str, 1); // Add this character to the atom
471 } // -x- if (char)0 -x-
472 } while (str++ < MAX); // -x- while str -x-
473 } // -x- if delimiter -x-
474
475 // --------------------------------------------------------------------------
476 // Shrink the array before returning it so sizing can be considered properly
477 // and unneeded memory can be freed.
478 // --------------------------------------------------------------------------
479 ary.shrink_to_fit(); // TODO: Make this optional with a flag called RTOOLS_SHRINK
480
481 return ary;
482 } // -x- std::vector<std::string> split -x-
483
484 /*======================================================================*//**
485 @brief
486 Convert an array of octets (8-bit bytes) to hexadecimal.
487
488 @returns std::string of hexadecimal characters (in lower case)
489 *///=========================================================================
490 static std::string to_hex(
491 /// Binary data to convert to hexadecimal
492 const void* data,
493 /// Length of array (in 8-bit bytes), which can be as short as 0; if -1, then
494 /// the length of the data will be measured as an ASCIIZ string; default is 1
495 /// if not specified since this is the safest option
496 size_t len = 1) noexcept {
497 std::string h; // Target string
498 char buf[3]; // Temporary buffer for use by snprintf()
499 if (len == -1) len = std::strlen((const char*)data); // Measure as if ASCIIZ string if length is 0
500 for(int i = 0; i < len; i++) {
501 snprintf(buf, (size_t)3, "%02x", ((const unsigned char*)data)[i]);
502 h.append(buf);
503 } // -x- for i -x-
504 return h;
505 } // -x- std::string to_hex -x-
506
507 /*======================================================================*//**
508 @brief
509 Convert an std::string's internal array of octets (8-bit bytes) to
510 hexadecimal.
511
512 @returns std::string of hexadecimal characters (in lower case)
513 *///=========================================================================
514 static std::string to_hex(
515 /// Binary data to convert to hexadecimal
516 std::string data) noexcept { return to_hex(data.data(), data.size()); } // -x- std::string to_hex -x-
517
518 /*======================================================================*//**
519 @brief
520 Convert a 32-bit integer to hexadecimal.
521
522 This method is needed because std::to_string() doesn't include an option to
523 specify the radix.
524 @returns Up to 8 hexadecimal characters
525 *///=========================================================================
526 static std::string to_hex(
527 /// Integer to convert to hexadecimal
528 const int i) noexcept {
529 std::string h;
530 h.resize(9); // 32-bit integer needs 8 nybbles to be represented in hexadecimal plus a NULL terminator
531 h.resize(snprintf(h.data(), h.size(), "%x", i)); // Convert to hexadecimal, and truncate NULL terminator
532 return h;
533 } // -x- std::string to_hex -x-
534
535 /*======================================================================*//**
536 @brief
537 Convert a 32-bit unsigned integer to hexadecimal.
538
539 This method is needed because std::to_string() doesn't include an option to
540 specify the radix.
541 @returns Up to 8 hexadecimal characters
542 *///=========================================================================
543 static std::string to_hex(
544 /// Integer to convert to hexadecimal
545 const unsigned int i) noexcept {
546 std::string h;
547 h.resize(9); // 32-bit integer needs 8 nybbles to be represented in hexadecimal plus a NULL terminator
548 h.resize(snprintf(h.data(), h.size(), "%x", i)); // Convert to hexadecimal, and truncate NULL terminator
549 return h;
550 } // -x- std::string to_hex -x-
551
552 /*======================================================================*//**
553 @brief
554 Convert ASCII characters in an std::string to lower case. UTF-8 characters
555 are not converted.
556 @returns Copy of std::string, in lower-case form
557 @see to_upper
558 *///=========================================================================
559 static std::string to_lower(
560 /// Source string
561 std::string& str,
562 /// Perform in-place conversion (default is FALSE / non-destructive)
563 bool in_place_conversion = false,
564 /// Begin conversion from this position (0 = first character)
565 const int begin = 0,
566 /// Number of characters to convert (-1 = maximum number of characters)
567 const int len = -1) noexcept {
568
569 // --------------------------------------------------------------------------
570 // Internal variables.
571 // --------------------------------------------------------------------------
572 const int MAX = len == -1 ? str.length() : begin + len; // Optimization: For faster loop operations
573
574 // --------------------------------------------------------------------------
575 // Perform in-place conversion.
576 // --------------------------------------------------------------------------
577 if (in_place_conversion) {
578 for (int i = begin; i < MAX; i++) {
579 char ch = str.at(i);
580 if (ch >= 'A' && ch <= 'Z') str.at(i) = ch += 32; // Convert to lower-case
581 } // -x- for i -x-
582 return str; // Return updated string
583 } // -x- if in_place_conversion -x-
584
585 // --------------------------------------------------------------------------
586 // Internal variables.
587 // --------------------------------------------------------------------------
588 std::string new_string = str; // Copy string (this allocates additional memory)
589
590 // --------------------------------------------------------------------------
591 // Perform isolated conversion.
592 // --------------------------------------------------------------------------
593 for (int i = begin; i < MAX; i++) {
594 char ch = str.at(i);
595 if (ch >= 'A' && ch <= 'Z') new_string.at(i) = ch += 32; // Convert to lower-case
596 } // -x- for i -x-
597 return new_string; // Return new string
598
599 } // -x- std::string to_lower -x-
600
601 /*======================================================================*//**
602 @brief
603 Convert ASCII characters in an std::string to upper case. UTF-8 characters
604 are not converted.
605 @returns Copy of std::string, in upper-case form
606 @see to_lower
607 *///=========================================================================
608 static std::string to_upper(
609 /// Source string
610 std::string& str,
611 /// Perform in-place conversion (default is FALSE / non-destructive)
612 bool in_place_conversion = false,
613 /// Begin conversion from this position (0 = first character)
614 const int begin = 0,
615 /// Number of characters to convert (-1 = maximum number of characters)
616 const int len = -1) noexcept {
617
618 // --------------------------------------------------------------------------
619 // Internal variables.
620 // --------------------------------------------------------------------------
621 const int MAX = len == -1 ? str.length() : begin + len; // Optimization: For faster loop operations
622
623 // --------------------------------------------------------------------------
624 // Perform in-place conversion.
625 // --------------------------------------------------------------------------
626 if (in_place_conversion) {
627 for (int i = begin; i < MAX; i++) {
628 char ch = str.at(i);
629 if (ch >= 'a' && ch <= 'z') str.at(i) = ch -= 32; // Convert to upper-case
630 } // -x- for i -x-
631 return str; // Return updated string
632 } // -x- if in_place_conversion -x-
633
634 // --------------------------------------------------------------------------
635 // Internal variables.
636 // --------------------------------------------------------------------------
637 std::string new_string = str; // Copy string (this allocates additional memory)
638
639 // --------------------------------------------------------------------------
640 // Perform isolated conversion.
641 // --------------------------------------------------------------------------
642 for (int i = begin; i < MAX; i++) {
643 char ch = str.at(i);
644 if (ch >= 'a' && ch <= 'z') new_string.at(i) = ch -= 32; // Convert to upper-case
645 } // -x- for i -x-
646 return new_string; // Return new string
647
648 } // -x- std::string to_upper -x-
649
650 /*======================================================================*//**
651 @brief
652 Removes the outer-most/enclosing set of quotation marks from the beginning
653 and end of the specified String, but only if both are present.
654 @returns Copy of std::string, with quotation marks removed (if both were
655 present)
656 *///=========================================================================
657 static std::string trim_quotes(
658 /// Source string
659 std::string str) noexcept {
660
661 // --------------------------------------------------------------------------
662 // Internal variables.
663 // --------------------------------------------------------------------------
664 const int LAST = str.length() - 1; // Optimization: For faster loop operations
665 if (LAST < 2) return str; // Less than two characters, so return
666
667 // --------------------------------------------------------------------------
668 // Process string.
669 // --------------------------------------------------------------------------
670 std::string new_string = (str[0] == '"' && str[LAST] == '"') ? str.substr(1, LAST - 1) : str; // This allocates additional memory
671 return new_string; // Return new string
672
673 } // -x- std::string trim_quotes -x-
674
675 /*======================================================================*//**
676 @brief
677 Wipe the contents of the supplied string with random data by XOR'ing random
678 unsigned char values with every character in the string. The clear() method
679 is not used because it's a waste of CPU cycles for a string that's just going
680 to be de-allocated anyway.
681 @warning
682 This method calls @c srand() with high resolution time once before starting
683 the loop that calls the @c std::rand() function. (Only the first 8 bits
684 returned by @c std::rand() are used; the remaining higher bits are not used.)
685 *///=========================================================================
686 static void wipe(
687 /// String to wipe
688 std::string& str,
689 /// Number of passes (default is 1)
690 unsigned int passes = 1) { wipe(str.data(), str.capacity(), passes); } // -x- void wipe -x-
691
692 /*======================================================================*//**
693 @brief
694 Wipe the contents of the supplied data with random data by XOR'ing random
695 unsigned char values with every character in the string.
696 @warning
697 This method calls @c srand() with high resolution time once before starting
698 the loop that calls the @c std::rand() function. (Only the first 8 bits
699 returned by @c std::rand() are used; the remaining higher bits are not used.)
700 *///=========================================================================
701 static void wipe(
702 /// String to wipe
703 char* data,
704 /// Length of string (-1 = ASCIIZ string)
705 size_t len = -1,
706 /// Number of passes (default is 1)
707 unsigned int passes = 1) {
708
709 // --------------------------------------------------------------------------
710 // Internal variables.
711 // --------------------------------------------------------------------------
712 if (len == -1) len = std::strlen(data);
713 std::timespec ts;
714 std::timespec_get(&ts, TIME_UTC);
715
716 // --------------------------------------------------------------------------
717 // Seed random number generator with high-resolution time data.
718 // --------------------------------------------------------------------------
719 std::srand(ts.tv_sec * ts.tv_nsec);
720
721 // --------------------------------------------------------------------------
722 // Data wipe loop.
723 // --------------------------------------------------------------------------
724 while (passes-- > 0) {
725 for (int i = len; i >= 0; i--)
726 ((unsigned char*)data)[i] ^= (unsigned char)std::rand();
727 } // -x- while passes -x-
728
729 } // -x- void wipe -x-
730
731 }; // -x- class rtools -x-
732
733} // -x- namespace randolf -x-