3#include <algorithm> // std::max and std::min
4#include <array> // std::array
5#include <cstring> // std::strlen
6#include <ctime> // std::time
7#include <exception> // std::exception::runtime_error
8#include <iostream> // std::cout
9#include <stdexcept> // std::runtime_error
10#include <string> // std::string
11#include <unordered_map> // std::unordered_map
12#include <vector> // std::vector
16 /*======================================================================*//**
18 This @ref rtools class primarily provides a collection of static methods that
19 facilitate a variety of general-purpose computer programming needs. Separate
20 classes may also be added in the future for more sophisticated needs.
22 - 2023-May-17 v1.00 Initial version
23 - 2024-Oct-23 v1.00 Added three more well-known base64 sets, added support
24 for base64 encoding/decoding to use ASCIIZ strings
25 for inputs, and made various minor improvements to the
26 documentation since the previous update
27 - 2024-Nov-24 v1.00 Added @c delimiter parameter to two @ref to_hex methods
28 - 2024-Nov-25 v1.00 Added support for negative positions in @ref to_lower
29 and @ref to_upper methods so that they count backward
30 from the end of the string
31 - 2025-Jan-20 v1.00 Removed atomize methods after rebuilding these in a
32 separate class called @ref randolf::Atomize
34 @author Randolf Richardson
35 *///=========================================================================
39 /*======================================================================*//**
41 This character set is suggested by RFC4648 (see page 8) as "safe" for use in
45 *///=========================================================================
46 inline static const char base64_set_minus_underscore[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
48 /*======================================================================*//**
50 This character set is normally used to encode IMAP4 mailbox names.
53 *///=========================================================================
54 inline static const char base64_set_plus_comma[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
56 /*======================================================================*//**
58 This character set is the default in every @c base64_ method in this library
59 because it's the most commonly used for base64 encoding.
61 Although other well-known base64 character sets are included here, or you can
62 create your own, which has a simple format -- it must be 64 characters long
63 without a NULL terminator (additional characters will be ignored), and each
64 character can only be specified once (or else encoding or decoding will fail
65 to render consistent results; there's no checking performed beforehand since
66 the software developers providing these customized character sets are trusted
67 to not introduce such problems).
70 *///=========================================================================
71 inline static const char base64_set_plus_slash[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
73 /*======================================================================*//**
75 This is an alternate character set that is rarely used, but is mentioned in
76 RFC4648 (see page 7, second paragraph of section 5).
78 RFC4648 incorrectly specifies the 63rd character when it's the 64th character
79 (the slash) that's being replaced with a tilde. This error likely came from
80 not counting the zero "value" label when referencing the set in which the 1st
81 character is labeled as having a value of 0.
84 *///=========================================================================
85 inline static const char base64_set_plus_tilde[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+~";
87 /*======================================================================*//**
89 Decode a Base64-encoded @c ASCIIZ string.
91 All invalid characters are simply ignored.
92 @returns Decoded string
94 @see base64_set_plus_slash
95 *///=========================================================================
96 static std::string base64_decode(
97 /// ASCIIZ string to decode
99 /// Base64 character set to use
100 const std::string b = base64_set_plus_slash) {
102 std::vector<int> T(256, -1);
105 for (int i = 0; i < 64; i++) T[b[i]] = i;
108 while ((c = (unsigned char)*in++) != 0) {
109 if (T[c] == -1) break;
110 val = (val << 6) + T[c];
113 out.push_back(char((val >> valb) & 0xFF));
118 } // -x- std::string base64_decode -x-
120 /*======================================================================*//**
122 Decode a Base64-encoded @c std::string.
124 All invalid characters are simply ignored.
125 @returns Decoded string
127 @see base64_set_plus_slash
128 *///=========================================================================
129 static std::string base64_decode(
131 const std::string &in,
132 /// Base64 character set to use
133 const std::string b = base64_set_plus_slash) {
135 std::vector<int> T(256, -1);
138 for (int i = 0; i < 64; i++) T[b[i]] = i;
140 for (unsigned char c: in) {
141 if (T[c] == -1) break;
142 val = (val << 6) + T[c];
145 out.push_back(char((val >> valb) & 0xFF));
150 } // -x- std::string base64_decode -x-
152 /*======================================================================*//**
154 Encode an @c ASCIIZ string into Base64 format.
156 All invalid characters are simply ignored.
157 @returns Base64-encoded string
159 @see base64_set_plus_slash
160 *///=========================================================================
161 static std::string base64_encode(
164 /// Base64 character set to use
165 const std::string b = base64_set_plus_slash) {
171 while ((c = (unsigned char)*in++) != 0) {
172 val = (val << 8) + c;
175 out.push_back(b[(val >> valb) & 0x3F]);
177 } // -x- while valb -x-
179 if (valb >- 6) out.push_back(b[((val << 8) >> (valb + 8)) & 0x3F]);
180 while (out.size() % 4) out.push_back('=');
182 } // -x- std::string base64_encode -x-
184 /*======================================================================*//**
186 Encode an @c std::string into Base64 format.
188 All invalid characters are simply ignored.
189 @returns Base64-encoded string
191 @see base64_set_plus_slash
192 *///=========================================================================
193 static std::string base64_encode(
195 const std::string &in,
196 /// Base64 character set to use
197 const std::string b = base64_set_plus_slash) {
202 for (unsigned char c: in) {
203 val = (val << 8) + c;
206 out.push_back(b[(val >> valb) & 0x3F]);
208 } // -x- while valb -x-
210 if (valb >- 6) out.push_back(b[((val << 8) >> (valb + 8)) & 0x3F]);
211 while (out.size() % 4) out.push_back('=');
213 } // -x- std::string base64_encode -x-
215 /*======================================================================*//**
217 Insert commas into the last numeric sequence of digits in the supplied string
218 and insert spaces before that (commas and spaces are configurable). If a
219 decimal point is found, then comma insertions will only occur before that
220 (this is also configurable).
221 @returns Numeric value as a char* array converted to a properly-delimited
222 string as an std::string
223 *///=========================================================================
224 static std::string insert_commas(
225 /// Pointer to ASCII representation of numeric value
227 /// Length of value (in bytes), or 0 to auto-detect length if value string is an ASCIIZ string
229 /// Don't insert any commas after the last period (or whatever string is set as the @c dot character)
230 bool skip_dot = true,
231 /// Number of digits between commas
232 const int digits = 3,
233 /// Pointer to ASCIIZ comma character string (nullptr = disabled)
234 const char* comma = ",",
235 /// Pointer to ASCIIZ space character string (nullptr = disabled) used instead of commas for non-digit fill-ins where commas would normally be inserted
236 const char* space = " ",
237 /// Period character used when @c skip_dot is enabled
238 const char dot = '.') noexcept {
240 // --------------------------------------------------------------------------
241 // Measure size of format string if an ASCIIZ string was indicated.
242 // --------------------------------------------------------------------------
243 if (len == 0) len = std::strlen(value);
245 // --------------------------------------------------------------------------
246 // Find the dot, and adjust len accordingly.
247 // --------------------------------------------------------------------------
248 for (int i = len - 1; i > 0; i--) {
249 if (value[i] == dot) {
255 // --------------------------------------------------------------------------
256 // Internal variables.
257 // --------------------------------------------------------------------------
258 std::string v(value, len);
259 const int m = len % digits; // Modulus for every 3rd character
260 bool blank = false; // Add blank space instead of comma
262 // --------------------------------------------------------------------------
263 // Insert commas as long as there are digits.
264 // --------------------------------------------------------------------------
265 for (int i = len - 1; i > 0; i--) {
266 if (v[i] < '0' || v[i] > '9') blank = true; // Not a digit, so we're switching from commas to blanks (spaces)
267 if ((i % digits) == m) { // This is where a separator belongs
268 if (!blank && comma != nullptr) v.insert(i, comma); // Insert comma, if one is defined
269 else if (blank && space != nullptr) v.insert(i, space); // Insert space, if one is defined
274 } // -x- std::string insert_commas -x-
276 /*======================================================================*//**
278 Parses a SASL exchange, returning an @c std::unordered_map of the key-value
279 pairs that were encountered.
280 @throws std::runtime_error If a SASL message is improperly formatted (the
281 error message includes the offset where the format problem occurred)
282 @returns Key-value pairs in an unordered map where the key is an std::string
283 object and the value is a vector of std::string objects
284 *///=========================================================================
285 static std::unordered_map<std::string, std::vector<std::string>> parse_sasl_exchange(
286 /// Unparsed SASL exchange string (must not be Base64-encoded)
287 const std::string sasl,
288 /// Ensure the following keys exist, each with one empty string: nonce, nc, cnonce, qop, realm, username, digest-uri, authzid
289 const bool add_missing_keys = false) {
291 // --------------------------------------------------------------------------
292 // Internal variables.
293 // --------------------------------------------------------------------------
294 std::unordered_map<std::string, std::vector<std::string>> map;
295 char ch; // Used in string parsing
296 std::string temp; // Used to build current element
297 std::string key; // Key name
298 bool keyMode = true; // TRUE = parsing key name; FALSE = parsing value
299 bool quoteMode = false; // Used for tracking "quote mode" with quotation mark usage
300 int offset = -1; // Offset within the unparsed string of the character currently being processed
301 const int MAX = sasl.size();
303 // --------------------------------------------------------------------------
304 // Parse the string, one character at a time.
305 // --------------------------------------------------------------------------
306 for (int offset = 0; offset < MAX; offset++) {
307 switch (ch = sasl[offset]) {
308 case '"': // Quotation mark
309 if (keyMode) throw std::runtime_error("Key names can't contain quotation marks at offset " + std::to_string(offset));
310 if (!quoteMode && temp.length() > 0) throw std::runtime_error("Malformed quoted value at offset " + std::to_string(offset));
311 quoteMode = !quoteMode; // Toggle "quote mode"
312 temp.push_back(ch); // Save quotation mark
314 case '\\': // Back-slash
315 if (keyMode) throw std::runtime_error("Key names can't contain escaped literal characters at offset " + std::to_string(offset));
316 ch = sasl[++offset]; // Get next character after incrementing offset
317 if (offset >= MAX) throw std::runtime_error("Literal character is missing at offset " + std::to_string(offset));
318 if (ch < ' ' || ch == 127) throw std::runtime_error("Invalid charater at offset " + std::to_string(offset)); // Any characters except CTLs and separators
319 temp.push_back('\\'); // Save backslash
320 temp.push_back(ch); // Save literal character
323 if (!quoteMode) throw std::runtime_error("White space characters not permitted at offset " + std::to_string(offset));
326 case '=': // Equal sign (signifies end of key, and beginning of value)
331 if (!keyMode) throw std::runtime_error("Invalid character at offset " + std::to_string(offset));
332 keyMode = !keyMode; // Toggle flag
333 if (temp.length() == 0) throw std::runtime_error("Missing key name at offset " + std::to_string(offset));
334 key = temp; // Save string for later
335 temp = ""; // Clear temporary string
337 case ',': // Comma delimiter (signifies end of value)
342 if (keyMode) throw std::runtime_error("Invalid character at offset " + std::to_string(offset));
343 keyMode = !keyMode; // Toggle flag
344 if (temp[0] == '"' && temp[temp.length() - 1] != '"') throw std::runtime_error("Malformed quoted value at offset " + std::to_string(offset));
345 map[key].push_back(temp);
348 default: // Everything else is a literal character
349 if (ch < ' ' || ch == 127) throw std::runtime_error("Invalid character at offset " + std::to_string(offset)); // Any characters except CTLs and separators
350 if (keyMode && (std::string("()<>@,;:\\\"/[]?={} ").find_first_of(ch) != std::string::npos)) throw std::runtime_error("Invalid character at offset " + std::to_string(offset));
353 } // -x- switch ch -x-
356 // --------------------------------------------------------------------------
357 // Syntax checks. There is no need to check for additional data because we
358 // added a comma to the unparsed_string -- if the original unparsed_string
359 // had a trailing comma, then an exception would have been thrown already.
360 // --------------------------------------------------------------------------
361 if (quoteMode) throw std::runtime_error("Incomplete value at offset " + std::to_string(offset));
363 // --------------------------------------------------------------------------
364 // Save last key-value pair if we're not in keyMode.
365 // --------------------------------------------------------------------------
366 if (!keyMode) map[key].push_back(temp);
368 // --------------------------------------------------------------------------
369 // Add missing keys, each with one empty string.
370 // --------------------------------------------------------------------------
371 if (add_missing_keys) {
372 if (map.try_emplace("nonce", std::vector<std::string>()).second) { map["nonce" ].push_back(""); };
373 if (map.try_emplace("nc", std::vector<std::string>()).second) { map["nc" ].push_back(""); };
374 if (map.try_emplace("cnonce", std::vector<std::string>()).second) { map["cnonce" ].push_back(""); };
375 if (map.try_emplace("qop", std::vector<std::string>()).second) { map["qop" ].push_back(""); };
376 if (map.try_emplace("realm", std::vector<std::string>()).second) { map["realm" ].push_back(""); };
377 if (map.try_emplace("username", std::vector<std::string>()).second) { map["username" ].push_back(""); };
378 if (map.try_emplace("digest-uri", std::vector<std::string>()).second) { map["digest-uri"].push_back(""); };
379 if (map.try_emplace("authzid", std::vector<std::string>()).second) { map["authzid" ].push_back(""); };
380 } // -x- if add_missing_keys -x-
382 // --------------------------------------------------------------------------
383 // Return the newly-created unordered_map.
384 // --------------------------------------------------------------------------
387 } // -x- std::unordered_map<std::string, std::vector<std::string>> parse_sasl_exchange -x-
389 /*======================================================================*//**
390 @copydoc split(const char, const char*, const size_t)
391 *///=========================================================================
392 static std::vector<std::string> split(
393 /// Character to use for the delimiter
394 const char delimiter,
395 /// Source string (to be split into atoms)
397 /// Length of string (in bytes), or 0 if using the full length of the string
398 const size_t len = -1) {
399 return split(delimiter, str.data(), len >= -1 ? str.size() : len);
400 } // -x- std::vector<std::string> split -x-
402 /*======================================================================*//**
404 Split string into an @c std::vector that's perfectly-sized to store all the
405 elements separated by a delimiter character. If no delimiters are
406 encountered, the resulting vector will contain the entire string as its only
407 element. If the string is empty, the resulting vector will contain an empty
408 string as its only element.
411 Using (char)0 as a delimiter necessitates specifying the length of the source
412 string, otherwise the resulting vector will contain only the first element
413 (this behaviour might change in the future, so don't rely on it).
415 @returns Pointer to an array of "atoms" (strings) stored in an @c std::vector
417 *///=========================================================================
418 static std::vector<std::string> split(
419 /// Character to use for the delimiter
420 const char delimiter,
421 /// Source string (to be split)
423 /// Length of string (in bytes), or -1 if @c str is an ASCIIZ string (NULL-terminated)
424 const size_t len = -1) { // TODO: Add support for a "maximum_elements" parameter
426 // --------------------------------------------------------------------------
427 // Internal variables.
429 // Includes not measuring size of string if an ASCIIZ string was indicated
430 // because "if (len == -1) len = std::strlen(str);" is less optimal than what
431 // the main loop already does in this regard.
432 // --------------------------------------------------------------------------
433 const char* MAX = len == -1 ? (char*)-1 : str + len; // Loop pre-optimization
434 std::string* atom = new std::string();
436 // --------------------------------------------------------------------------
437 // Pre-allocate std::vector with a size that's half the length of the string,
438 // because this is the maximum number of atoms there could be if each atom is
439 // 1 byte long and each delimiting whitespace character is also 1 byte long.
440 // --------------------------------------------------------------------------
441 std::vector<std::string> ary;
442 if (len > 0) ary.reserve(len >> (1 + 1)); // Ensure array-like efficiency
444 // --------------------------------------------------------------------------
447 // Optimization: By using separate loops for a non-NULL delimiter from the
448 // NULL delimiter, both loops are faster and less complicated.
449 // --------------------------------------------------------------------------
450 if (delimiter != (char)0) { // Process using non-NULL delimiter
452 if (*str == delimiter) { // Delimiter character
453 ary.push_back(*atom); // Save current atom to vector
454 atom = new std::string(); // Create new atom (string)
456 atom->append(str, 1); // Add this character to the atom
457 } // -x- if delimiter -x-
458 } while (*str != (char)0 && ++str < MAX); // -x- while str -x-
460 // --------------------------------------------------------------------------
461 // If we're not in delimited mode, and the current string is not empty, then
462 // add it to the vector as the final atom.
464 // Memory leak prevention (we don't need any free radicals taking up memory!
465 // {yes, I had fun writing this, and whether or not you like it, you can't
466 // deny the cleverness of this pun}): Because of the way the loop (above) is
467 // structured, there will never be a memory leak here resulting from trailing
468 // non-delimiter characters because a new atom is created only when a first
469 // delimiter character is encountered.
470 // --------------------------------------------------------------------------
471 ary.push_back(*atom);
473 // --------------------------------------------------------------------------
474 // Process NULL delimiter.
475 // --------------------------------------------------------------------------
476 } else { // Process using NULL delimiter
478 if (*str == (char)0) { // NULL delimiter
480 ary.push_back(*atom); // Save current atom to vector
481 atom = new std::string(); // Create new atom (string)
482 } else { // -x- if *str -x-
483 atom->append(str, 1); // Add this character to the atom
484 } // -x- if (char)0 -x-
485 } while (str++ < MAX); // -x- while str -x-
486 } // -x- if delimiter -x-
488 // --------------------------------------------------------------------------
489 // Shrink the array before returning it so sizing can be considered properly
490 // and unneeded memory can be freed.
491 // --------------------------------------------------------------------------
492 ary.shrink_to_fit(); // TODO: Make this optional with a flag called RTOOLS_SHRINK
495 } // -x- std::vector<std::string> split -x-
497 /*======================================================================*//**
499 Convert an array of octets (8-bit bytes) to hexadecimal.
501 @returns std::string of hexadecimal characters (in lower case)
502 *///=========================================================================
503 static std::string to_hex(
504 /// Binary data to convert to hexadecimal
506 /// Length of array (in 8-bit bytes), which can be as short as 0; if -1, then
507 /// the length of the data will be measured as an ASCIIZ string; default is 1
508 /// if not specified since this is the safest option
510 /// Delimiter character sequence (ASCIIZ string) to insert between multiple
511 /// pairs of nybbles@n
512 /// @c nullptr = no delimiter (default)
513 const char* delimiter = nullptr) noexcept {
514 std::string h; // Target string
515 char buf[3]; // Temporary buffer for use by snprintf()
516 if (len == -1) len = std::strlen((const char*)data); // Measure as if ASCIIZ string if length is 0
517 for(int i = 0; i < len; i++) {
518 if (delimiter != nullptr && i > 0) h.append(delimiter);
519 snprintf(buf, (size_t)3, "%02x", ((const unsigned char*)data)[i]);
523 } // -x- std::string to_hex -x-
525 /*======================================================================*//**
527 Convert an std::string's internal array of octets (8-bit bytes) to
530 @returns std::string of hexadecimal characters (in lower case)
531 *///=========================================================================
532 static std::string to_hex(
533 /// Binary data to convert to hexadecimal
535 /// Delimiter character sequence (ASCIIZ string) to insert between multiple
536 /// pairs of nybbles@n
537 /// @c nullptr = no delimiter (default)
538 const char* delimiter = nullptr) noexcept { return to_hex(data.data(), data.size(), delimiter); } // -x- std::string to_hex -x-
540 /*======================================================================*//**
542 Convert a 32-bit integer to hexadecimal.
544 This method is needed because std::to_string() doesn't include an option to
546 @returns Up to 8 hexadecimal characters
547 *///=========================================================================
548 static std::string to_hex(
549 /// Integer to convert to hexadecimal
550 const int i) noexcept {
552 h.resize(9); // 32-bit integer needs 8 nybbles to be represented in hexadecimal plus a NULL terminator
553 h.resize(snprintf(h.data(), h.size(), "%x", i)); // Convert to hexadecimal, and truncate NULL terminator
555 } // -x- std::string to_hex -x-
557 /*======================================================================*//**
559 Convert a 32-bit unsigned integer to hexadecimal.
561 This method is needed because std::to_string() doesn't include an option to
563 @returns Up to 8 hexadecimal characters
564 *///=========================================================================
565 static std::string to_hex(
566 /// Integer to convert to hexadecimal
567 const unsigned int i) noexcept {
569 h.resize(9); // 32-bit integer needs 8 nybbles to be represented in hexadecimal plus a NULL terminator
570 h.resize(snprintf(h.data(), h.size(), "%x", i)); // Convert to hexadecimal, and truncate NULL terminator
572 } // -x- std::string to_hex -x-
574 /*======================================================================*//**
576 Convert ASCII characters in an std::string to lower case. UTF-8 characters
578 @returns Copy of std::string, in lower-case form
580 *///=========================================================================
581 static std::string to_lower(
584 /// Perform in-place conversion (default is FALSE / non-destructive)
585 bool in_place_conversion = false,
586 /// Begin conversion from this position (0 = first character)@n
587 /// Negative positions are caculated backward from the end of the string
589 /// Number of characters to convert (values exceeding string length will not
590 /// cause any exceptions as the excess will be effectively ignored)@n
591 /// -1 = maximum number of characters (a.k.a., until end of string)
592 const int len = -1) noexcept {
594 // --------------------------------------------------------------------------
595 // Internal variables.
596 // --------------------------------------------------------------------------
597 const int BEGIN = begin >= 0 ? begin : str.length() + begin; // Calculate negative values from end of string
598 const int MAX = len == -1 ? str.length() : std::min(str.length(), (size_t)(begin + len)); // Optimization: For faster loop operations
600 // --------------------------------------------------------------------------
601 // Perform in-place conversion.
602 // --------------------------------------------------------------------------
603 if (in_place_conversion) {
604 for (int i = BEGIN; i < MAX; i++) {
606 if (ch >= 'A' && ch <= 'Z') str.at(i) = ch += 32; // Convert to lower-case
608 return str; // Return updated string
609 } // -x- if in_place_conversion -x-
611 // --------------------------------------------------------------------------
612 // Internal variables.
613 // --------------------------------------------------------------------------
614 std::string new_string = str; // Copy string (this allocates additional memory)
616 // --------------------------------------------------------------------------
617 // Perform isolated conversion.
618 // --------------------------------------------------------------------------
619 for (int i = BEGIN; i < MAX; i++) {
621 if (ch >= 'A' && ch <= 'Z') new_string.at(i) = ch += 32; // Convert to lower-case
623 return new_string; // Return new string
625 } // -x- std::string to_lower -x-
627 /*======================================================================*//**
629 Convert ASCII characters in an std::string to upper case. UTF-8 characters
631 @returns Copy of std::string, in upper-case form
633 *///=========================================================================
634 static std::string to_upper(
637 /// Perform in-place conversion (default is FALSE / non-destructive)
638 bool in_place_conversion = false,
639 /// Begin conversion from this position (0 = first character)@n
640 /// Negative positions are caculated backward from the end of the string
642 /// Number of characters to convert (values exceeding string length will not
643 /// cause any exceptions as the excess will be effectively ignored)@n
644 /// -1 = maximum number of characters (a.k.a., until end of string)
645 const int len = -1) noexcept {
647 // --------------------------------------------------------------------------
648 // Internal variables.
649 // --------------------------------------------------------------------------
650 const int BEGIN = begin >= 0 ? begin : str.length() + begin; // Calculate negative values from end of string
651 const int MAX = len == -1 ? str.length() : std::min(str.length(), (size_t)(begin + len)); // Optimization: For faster loop operations
653 // --------------------------------------------------------------------------
654 // Perform in-place conversion.
655 // --------------------------------------------------------------------------
656 if (in_place_conversion) {
657 for (int i = BEGIN; i < MAX; i++) {
659 if (ch >= 'a' && ch <= 'z') str.at(i) = ch -= 32; // Convert to upper-case
661 return str; // Return updated string
662 } // -x- if in_place_conversion -x-
664 // --------------------------------------------------------------------------
665 // Internal variables.
666 // --------------------------------------------------------------------------
667 std::string new_string = str; // Copy string (this allocates additional memory)
669 // --------------------------------------------------------------------------
670 // Perform isolated conversion.
671 // --------------------------------------------------------------------------
672 for (int i = BEGIN; i < MAX; i++) {
674 if (ch >= 'a' && ch <= 'z') new_string.at(i) = ch -= 32; // Convert to upper-case
676 return new_string; // Return new string
678 } // -x- std::string to_upper -x-
680 /*======================================================================*//**
682 Removes the outer-most/enclosing set of quotation marks from the beginning
683 and end of the specified String, but only if both are present.
684 @returns Copy of std::string, with quotation marks removed (if both were
686 *///=========================================================================
687 static std::string trim_quotes(
689 std::string str) noexcept {
691 // --------------------------------------------------------------------------
692 // Internal variables.
693 // --------------------------------------------------------------------------
694 const int LAST = str.length() - 1; // Optimization: For faster loop operations
695 if (LAST < 2) return str; // Less than two characters, so return
697 // --------------------------------------------------------------------------
699 // --------------------------------------------------------------------------
700 std::string new_string = (str[0] == '"' && str[LAST] == '"') ? str.substr(1, LAST - 1) : str; // This allocates additional memory
701 return new_string; // Return new string
703 } // -x- std::string trim_quotes -x-
705 /*======================================================================*//**
707 Wipe the contents of the supplied string with random data by XOR'ing random
708 unsigned char values with every character in the string. The clear() method
709 is not used because it's a waste of CPU cycles for a string that's just going
710 to be de-allocated anyway.
712 This method calls @c srand() with high resolution time once before starting
713 the loop that calls the @c std::rand() function. (Only the first 8 bits
714 returned by @c std::rand() are used; the remaining higher bits are not used.)
715 *///=========================================================================
719 /// Number of passes (default is 1)
720 unsigned int passes = 1) { wipe(str.data(), str.capacity(), passes); } // -x- void wipe -x-
722 /*======================================================================*//**
724 Wipe the contents of the supplied data with random data by XOR'ing random
725 unsigned char values with every character in the string.
727 This method calls @c srand() with high resolution time once before starting
728 the loop that calls the @c std::rand() function. (Only the first 8 bits
729 returned by @c std::rand() are used; the remaining higher bits are not used.)
730 *///=========================================================================
734 /// Length of string (-1 = ASCIIZ string)
736 /// Number of passes (default is 1)
737 unsigned int passes = 1) {
739 // --------------------------------------------------------------------------
740 // Internal variables.
741 // --------------------------------------------------------------------------
742 if (len == -1) len = std::strlen(data);
744 std::timespec_get(&ts, TIME_UTC);
746 // --------------------------------------------------------------------------
747 // Seed random number generator with high-resolution time data.
748 // --------------------------------------------------------------------------
749 std::srand(ts.tv_sec * ts.tv_nsec);
751 // --------------------------------------------------------------------------
753 // --------------------------------------------------------------------------
754 while (passes-- > 0) {
755 for (int i = len; i >= 0; i--)
756 ((unsigned char*)data)[i] ^= (unsigned char)std::rand();
757 } // -x- while passes -x-
759 } // -x- void wipe -x-
761 }; // -x- class rtools -x-
763} // -x- namespace randolf -x-