Vuo 2.4.4
Loading...
Searching...
No Matches
VuoStringUtilities.cc
Go to the documentation of this file.
1
10#include <iomanip>
11#include <sstream>
12#include <CommonCrypto/CommonDigest.h>
13#include <CoreFoundation/CoreFoundation.h>
14#include "VuoException.hh"
15#include "VuoStringUtilities.hh"
16
17extern "C" {
18#include "mkdio.h"
19}
20
24bool VuoStringUtilities::beginsWith(const string &wholeString, const string &beginning)
25{
26 return wholeString.rfind(beginning, 0) == 0;
27}
28
32bool VuoStringUtilities::beginsWith(const char *wholeString, const char *beginning)
33{
34 return strlen(wholeString) >= strlen(beginning) && strstr(wholeString, beginning) == wholeString;
35}
36
40bool VuoStringUtilities::endsWith(const string &wholeString, const string &ending)
41{
42 if (wholeString.length() < ending.length())
43 return false;
44
45 return wholeString.compare(wholeString.length()-ending.length(), ending.length(), ending) == 0;
46}
47
52string VuoStringUtilities::substrAfter(const string &wholeString, const string &beginning)
53{
54 if (! beginsWith(wholeString, beginning))
55 return "";
56
57 return wholeString.substr(beginning.length());
58}
59
64string VuoStringUtilities::substrBefore(const string &wholeString, const string &ending)
65{
66 if (! endsWith(wholeString, ending))
67 return "";
68
69 return wholeString.substr(0, wholeString.length()-ending.length());
70}
71
76string VuoStringUtilities::replaceAll(const string &wholeString, char originalChar, char replacementChar)
77{
78 string outString = wholeString;
79 size_t pos = 0;
80 while ((pos = wholeString.find_first_of(originalChar, pos)) != string::npos)
81 {
82 outString[pos] = replacementChar;
83 pos = pos + 1;
84 }
85 return outString;
86}
87
93size_t VuoStringUtilities::replaceAll(string &wholeString, const string &originalSubstring, const string &replacementSubstring)
94{
95 size_t replacementCount = 0;
96 size_t startPos = 0;
97 while ((startPos = wholeString.find(originalSubstring, startPos)) != string::npos)
98 {
99 wholeString.replace(startPos, originalSubstring.length(), replacementSubstring);
100 startPos += replacementSubstring.length();
101 ++replacementCount;
102 }
103 return replacementCount;
104}
105
109vector<string> VuoStringUtilities::split(const string &wholeString, char delimiter)
110{
111 vector<string> tokens;
112 istringstream iss(wholeString);
113 string token;
114 while( getline(iss, token, delimiter) )
115 tokens.push_back(token);
116 return tokens;
117}
118
122string VuoStringUtilities::join(const vector<string> &partialStrings, char delimiter)
123{
124 string delimiterStr(1, delimiter);
125 return join(partialStrings, delimiterStr);
126}
127
131string VuoStringUtilities::join(const vector<string> &partialStrings, const string &delimiter)
132{
133 string wholeString;
134 for (auto i = partialStrings.cbegin(); i != partialStrings.cend(); )
135 {
136 wholeString += *i;
137 if (++i != partialStrings.cend())
138 wholeString += delimiter;
139 }
140 return wholeString;
141}
142
146string VuoStringUtilities::join(const set<string> &partialStrings, const string &delimiter)
147{
148 string wholeString;
149 for (auto i = partialStrings.cbegin(); i != partialStrings.cend(); )
150 {
151 wholeString += *i;
152 if (++i != partialStrings.cend())
153 wholeString += delimiter;
154 }
155 return wholeString;
156}
157
165string VuoStringUtilities::trim(const string &originalString)
166{
167 string whitespace = " \t\v\n\r\f";
168
169 string::size_type begin = originalString.find_first_not_of(whitespace);
170 if (begin == std::string::npos)
171 return "";
172
173 string::size_type end = originalString.find_last_not_of(whitespace);
174
175 return originalString.substr(begin, end - begin + 1);
176}
177
181void VuoStringUtilities::doForEachLine(const string &multiLineString, std::function<void(const char *)> doForLine)
182{
183 // https://stackoverflow.com/questions/17983005/c-how-to-read-a-string-line-by-line
184
185 const char linebreak = '\n';
186
187 int bufferLength = 1024;
188 char *line = (char *)malloc(bufferLength + 1);
189
190 const char *lineStart = multiLineString.c_str();
191 while (lineStart)
192 {
193 const char *lineEnd = strchr(lineStart, linebreak);
194 int lineLength = lineEnd ? (lineEnd - lineStart) : strlen(lineStart);
195
196 if (lineLength > bufferLength)
197 {
198 free(line);
199 line = (char *)malloc(lineLength + 1);
200 bufferLength = lineLength;
201 }
202
203 memcpy(line, lineStart, lineLength);
204 line[lineLength] = 0;
205
206 doForLine(line);
207
208 lineStart = lineEnd ? (lineEnd + 1) : nullptr;
209 }
210
211 free(line);
212}
213
220string VuoStringUtilities::buildCompositionIdentifier(const string &parentCompositionIdentifier, const string &nodeIdentifier)
221{
222 return parentCompositionIdentifier + "/" + nodeIdentifier;
223}
224
229string VuoStringUtilities::buildPortIdentifier(const string &nodeIdentifier, const string &portName)
230{
231 return nodeIdentifier + ":" + portName;
232}
233
238string VuoStringUtilities::prefixSymbolName(const string &symbolName, const string &moduleKey)
239{
240 return transcodeToIdentifier(moduleKey) + "__" + symbolName;
241}
242
250{
251 CFMutableStringRef strCF = CFStringCreateMutable(NULL, 0);
252 CFStringAppendCString(strCF, str.c_str(), kCFStringEncodingUTF8);
253
254 CFStringNormalize(strCF, kCFStringNormalizationFormD); // decomposes combining characters, so accents/diacritics are separated from their letters
255
256 CFIndex strLength = CFStringGetLength(strCF);
257 UniChar *strBuf = (UniChar *)malloc(strLength * sizeof(UniChar));
258 if (!strBuf)
259 {
260 CFRelease(strCF);
261 return string();
262 }
263 CFStringGetCharacters(strCF, CFRangeMake(0, strLength), strBuf);
264
265 CFStringRef empty = CFStringCreateWithCString(NULL, "", kCFStringEncodingUTF8);
266 CFStringRef underscore = CFStringCreateWithCString(NULL, "_", kCFStringEncodingUTF8);
267 CFStringRef doubleUnderscore = CFStringCreateWithCString(NULL, "__", kCFStringEncodingUTF8);
268 if (!empty || !underscore || !doubleUnderscore)
269 {
270 CFRelease(strCF);
271 if (empty)
272 CFRelease(empty);
273 if (underscore)
274 CFRelease(underscore);
275 if (doubleUnderscore)
276 CFRelease(doubleUnderscore);
277 return string();
278 }
279
280 for (CFIndex i = strLength-1; i >= 0; --i)
281 {
282 UniChar c = strBuf[i];
283
284 CFStringRef replacement = NULL;
285 if (c > 127) // non-ASCII
286 replacement = empty;
287 else if (c == '.' || isspace(c))
288 replacement = underscore;
289 else if (c == '/' || c == ':')
290 replacement = doubleUnderscore;
291 else if (! isValidCharInIdentifier(c))
292 replacement = empty;
293
294 if (replacement)
295 CFStringReplace(strCF, CFRangeMake(i, 1), replacement);
296 }
297
298 string ret = makeFromCFString(strCF);
299
300 CFRelease(strCF);
301 CFRelease(empty);
302 CFRelease(underscore);
303 CFRelease(doubleUnderscore);
304 free(strBuf);
305
306 return ret;
307}
308
314{
315 return isalnum(ch) || ch == '_';
316}
317
321string VuoStringUtilities::transcodeToGraphvizIdentifier(const string &originalString)
322{
323 string escapedString = originalString;
324 for (string::size_type i = 0; (i = escapedString.find("\\", i)) != std::string::npos; i += 2)
325 escapedString.replace(i, 1, "\\\\");
326 for (string::size_type i = 0; (i = escapedString.find("\"", i)) != std::string::npos; i += 2)
327 escapedString.replace(i, 1, "\\\"");
328 for (string::size_type i = 0; (i = escapedString.find("{", i)) != std::string::npos; i += 2)
329 escapedString.replace(i, 1, "\\{");
330 for (string::size_type i = 0; (i = escapedString.find("}", i)) != std::string::npos; i += 2)
331 escapedString.replace(i, 1, "\\}");
332 for (string::size_type i = 0; (i = escapedString.find("<", i)) != std::string::npos; i += 2)
333 escapedString.replace(i, 1, "\\<");
334 for (string::size_type i = 0; (i = escapedString.find(">", i)) != std::string::npos; i += 2)
335 escapedString.replace(i, 1, "\\>");
336 for (string::size_type i = 0; (i = escapedString.find("|", i)) != std::string::npos; i += 2)
337 escapedString.replace(i, 1, "\\|");
338 for (string::size_type i = 0; (i = escapedString.find(" ", i)) != std::string::npos; i += 3)
339 escapedString.replace(i, 2, " \\ ");
340 return escapedString;
341}
342
346string VuoStringUtilities::transcodeFromGraphvizIdentifier(const string &graphvizIdentifier)
347{
348 string unescapedString;
349 bool inEscape = false;
350 for (string::const_iterator i = graphvizIdentifier.begin(); i != graphvizIdentifier.end(); ++i)
351 {
352 if (inEscape)
353 {
354 inEscape = false;
355 unescapedString += *i;
356 continue;
357 }
358
359 if (*i == '\\')
360 {
361 inEscape = true;
362 continue;
363 }
364
365 unescapedString += *i;
366 }
367 return unescapedString;
368}
369
375string VuoStringUtilities::formUniqueIdentifier(set<string> &takenIdentifiers,
376 const string &preferredIdentifier, const string &identifierPrefix)
377{
378 auto isIdentifierAvailable = [&takenIdentifiers] (const string &identifier)
379 {
380 return takenIdentifiers.find(identifier) == takenIdentifiers.end();
381 };
382
383 string uniqueIdentifier = formUniqueIdentifier(isIdentifierAvailable, preferredIdentifier, identifierPrefix);
384 takenIdentifiers.insert(uniqueIdentifier);
385 return uniqueIdentifier;
386}
387
393string VuoStringUtilities::formUniqueIdentifier(std::function<bool(const string &)> isIdentifierAvailable,
394 const string &preferredIdentifier, const string &identifierPrefix)
395{
396 string unique = preferredIdentifier;
397 string prefix = (! identifierPrefix.empty() ? identifierPrefix : preferredIdentifier);
398 int suffix = 2;
399
400 while (! isIdentifierAvailable(unique))
401 {
402 ostringstream oss;
403 oss << prefix << suffix++;
404 unique = oss.str();
405 }
406
407 return unique;
408}
409
415string VuoStringUtilities::generateHtmlFromMarkdown(const string &markdownString)
416{
417 MMIOT *doc = mkd_string(markdownString.c_str(), markdownString.length(), MKD_NOPANTS);
418 mkd_compile(doc, 0);
419 char *html;
420 mkd_document(doc, &html);
421 string htmlString(html);
422 mkd_cleanup(doc);
423
424 // Remove the final linebreak from code blocks,
425 // since Qt (unlike typical browser rendering engines) considers that whitespace significant.
426 replaceAll(htmlString, "\n</code></pre>", "</code></pre>");
427
428 return htmlString;
429}
430
436string VuoStringUtilities::generateHtmlFromMarkdownLine(const string &markdownString)
437{
438 size_t length = markdownString.length();
439 if (!length)
440 return "";
441
442 char *html;
443 mkd_line((char *)markdownString.c_str(), length, &html, MKD_NOPANTS);
444 string htmlString(html);
445 free(html);
446 return htmlString;
447}
448
462string VuoStringUtilities::convertToCamelCase(const string &originalString,
463 bool forceFirstLetterToUpper, bool forceFirstLetterToLower, bool forceInterveningLettersToLower,
464 bool allowSeparatorDots)
465{
466 string camelCaseString;
467 bool first = true;
468 bool uppercaseNext = forceFirstLetterToUpper;
469 bool lowercaseNext = forceFirstLetterToLower;
470 bool previousWasDot = false;
471 for (string::const_iterator i = originalString.begin(); i != originalString.end(); ++i)
472 {
473 if (first && !isalpha(*i))
474 continue;
475 first = false;
476
477 bool isDot = *i == '.';
478 if (allowSeparatorDots && isDot)
479 {
480 if (previousWasDot)
481 continue;
482 uppercaseNext = false;
483 }
484 else if (!isalnum(*i))
485 {
486 uppercaseNext = true;
487 continue;
488 }
489
490 if (uppercaseNext)
491 camelCaseString += toupper(*i);
492 else if (lowercaseNext)
493 camelCaseString += tolower(*i);
494 else
495 camelCaseString += *i;
496
497 uppercaseNext = false;
498 lowercaseNext = forceInterveningLettersToLower;
499 previousWasDot = isDot;
500 }
501
502 // Trim trailing dots.
503 if (allowSeparatorDots)
504 while (endsWith(camelCaseString, "."))
505 camelCaseString = substrBefore(camelCaseString, ".");
506
507 return camelCaseString;
508}
509
516string VuoStringUtilities::expandCamelCase(const string &camelCaseString)
517{
518 // Only apply these transformations if the whole string matches,
519 // since they may appear as substrings in contexts where they shouldn't be all-caps.
520 if (camelCaseString == "x")
521 return "X";
522 else if (camelCaseString == "y")
523 return "Y";
524 else if (camelCaseString == "z")
525 return "Z";
526 else if (camelCaseString == "w")
527 return "W";
528 else if (camelCaseString == "xy")
529 return "XY";
530 else if (camelCaseString == "osc")
531 return "OSC";
532
533 string out;
534 out += toupper(camelCaseString[0]);
535
536 size_t length = camelCaseString.length();
537 for (int i = 1; i < length; ++i)
538 {
539 char c = camelCaseString[i];
540 if (isupper(c) || (isdigit(c) && !isdigit(camelCaseString[i-1])))
541 out += " ";
542 out += c;
543 }
544
545 string allCaps[]{
546 "2d",
547 "3d",
548 "4d",
549 "Xyzw",
550 "Xyz",
551 "Rgbaw",
552 "Rgba",
553 "Rgbw",
554 "Rgb",
555 "Wwcw",
556 "Cmy",
557 "Hsl",
558 "Hdmi",
559 "Sdi",
560 "Ntsc",
561// "Pal", // Appears in Leap Motion "Palm Velocity".
562 "Url",
563 "Midi",
564 "Rss",
565 "Csv",
566 "Tsv",
567 "Ascii",
568 "Json",
569 "Xml",
570 "Dmx",
571 };
572 for (auto lower : allCaps)
573 {
574 string upper = lower;
575 std::transform(upper.begin(), upper.end(), upper.begin(), ::toupper);
576 VuoStringUtilities::replaceAll(out, lower, upper);
577 }
578
579 return out;
580}
581
586{
587 if (!cfs)
588 return "";
589
590 CFStringRef cfString = (CFStringRef)cfs;
591
592 // https://stackoverflow.com/questions/1609565/whats-the-cfstring-equiv-of-nsstrings-utf8string
593 const char *utf8StringPtr = CFStringGetCStringPtr(cfString, kCFStringEncodingUTF8);
594 if (utf8StringPtr)
595 return utf8StringPtr;
596 else
597 {
598 CFIndex maxBytes = CFStringGetMaximumSizeForEncoding(CFStringGetLength(cfString), kCFStringEncodingUTF8) + 1;
599 char *t = (char *)calloc(1, maxBytes);
600 CFStringGetCString(cfString, t, maxBytes, kCFStringEncodingUTF8);
601 string s(t);
602 free(t);
603 return s;
604 }
605}
606
611{
612 static const char alphanum[] =
613 "0123456789"
614 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
615 "abcdefghijklmnopqrstuvwxyz";
616
617 string hash(length, 0);
618 for (int i = 0; i < length; ++i)
619 hash[i] = alphanum[arc4random_uniform(sizeof(alphanum)-1)];
620
621 return hash;
622}
623
624const std::locale VuoStringUtilities::locale;
625const std::collate<char> &VuoStringUtilities::collate = std::use_facet<std::collate<char> >(VuoStringUtilities::locale);
626
633{
634 unsigned char hash[CC_SHA256_DIGEST_LENGTH];
635 if (!CC_SHA256(s.c_str(), s.length(), hash))
636 throw VuoException("Error: CC_SHA256 failed.");
637
639 oss << setfill('0') << hex;
640 for (int i = 0; i < CC_SHA256_DIGEST_LENGTH; ++i)
641 oss << setw(2) << (int)hash[i];
642 return oss.str();
643}