You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

241 lines
5.1KB

  1. #include <cctype> // for tolower and toupper
  2. #include <algorithm> // for transform and equal
  3. #include <libgen.h> // for dirname and basename
  4. #include <stdarg.h>
  5. #if defined ARCH_WIN
  6. #include <windows.h> // for MultiByteToWideChar
  7. #endif
  8. #include <string.hpp>
  9. namespace rack {
  10. namespace string {
  11. std::string f(const char* format, ...) {
  12. va_list args;
  13. va_start(args, format);
  14. std::string s = fV(format, args);
  15. va_end(args);
  16. return s;
  17. }
  18. std::string fV(const char* format, va_list args) {
  19. // va_lists cannot be reused but we need it twice, so clone args.
  20. va_list args2;
  21. va_copy(args2, args);
  22. // Compute size of required buffer
  23. int size = vsnprintf(NULL, 0, format, args);
  24. if (size < 0)
  25. return "";
  26. // Create buffer
  27. std::string s;
  28. s.resize(size);
  29. vsnprintf(&s[0], size + 1, format, args2);
  30. return s;
  31. }
  32. std::string lowercase(const std::string& s) {
  33. std::string r = s;
  34. std::transform(r.begin(), r.end(), r.begin(), [](unsigned char c) {
  35. return std::tolower(c);
  36. });
  37. return r;
  38. }
  39. std::string uppercase(const std::string& s) {
  40. std::string r = s;
  41. std::transform(r.begin(), r.end(), r.begin(), [](unsigned char c) {
  42. return std::toupper(c);
  43. });
  44. return r;
  45. }
  46. std::string trim(const std::string& s) {
  47. const std::string whitespace = " \n\r\t";
  48. size_t first = s.find_first_not_of(whitespace);
  49. if (first == std::string::npos)
  50. return "";
  51. size_t last = s.find_last_not_of(whitespace);
  52. if (last == std::string::npos)
  53. return "";
  54. return s.substr(first, last - first + 1);
  55. }
  56. std::string ellipsize(const std::string& s, size_t len) {
  57. if (s.size() <= len)
  58. return s;
  59. else
  60. return s.substr(0, len - 3) + "...";
  61. }
  62. std::string ellipsizePrefix(const std::string& s, size_t len) {
  63. if (s.size() <= len)
  64. return s;
  65. else
  66. return "..." + s.substr(s.size() - (len - 3));
  67. }
  68. bool startsWith(const std::string& str, const std::string& prefix) {
  69. if (str.size() < prefix.size())
  70. return false;
  71. return std::equal(prefix.begin(), prefix.end(), str.begin());
  72. }
  73. bool endsWith(const std::string& str, const std::string& suffix) {
  74. if (str.size() < suffix.size())
  75. return false;
  76. return std::equal(suffix.begin(), suffix.end(), str.end() - suffix.size());
  77. }
  78. float fuzzyScore(const std::string& s, const std::string& query) {
  79. size_t pos = s.find(query);
  80. if (pos == std::string::npos)
  81. return 0.f;
  82. return (float)(query.size() + 1) / (s.size() + 1);
  83. }
  84. std::string toBase64(const uint8_t* data, size_t dataLen) {
  85. static const char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  86. size_t numBlocks = (dataLen + 2) / 3;
  87. size_t strLen = numBlocks * 4;
  88. std::string str;
  89. str.reserve(strLen);
  90. for (size_t b = 0; b < numBlocks; b++) {
  91. // Encode block
  92. uint32_t block = 0;
  93. int i;
  94. for (i = 0; i < 3 && 3 * b + i < dataLen; i++) {
  95. block |= uint32_t(data[3 * b + i]) << (8 * (2 - i));
  96. }
  97. // Decode block
  98. str += alphabet[(block >> 18) & 0x3f];
  99. str += alphabet[(block >> 12) & 0x3f];
  100. str += (i > 1) ? alphabet[(block >> 6) & 0x3f] : '=';
  101. str += (i > 2) ? alphabet[(block >> 0) & 0x3f] : '=';
  102. }
  103. return str;
  104. }
  105. std::string toBase64(const std::vector<uint8_t>& data) {
  106. return toBase64(data.data(), data.size());
  107. }
  108. std::vector<uint8_t> fromBase64(const std::string& str) {
  109. std::vector<uint8_t> data;
  110. uint32_t block = 0;
  111. int i = 0;
  112. int padding = 0;
  113. for (char c : str) {
  114. uint8_t d = 0;
  115. if ('A' <= c && c <= 'Z') {
  116. d = c - 'A';
  117. }
  118. else if ('a' <= c && c <= 'z') {
  119. d = c - 'a' + 26;
  120. }
  121. else if ('0' <= c && c <= '9') {
  122. d = c - '0' + 52;
  123. }
  124. else if (c == '+') {
  125. d = 62;
  126. }
  127. else if (c == '/') {
  128. d = 63;
  129. }
  130. else if (c == '=') {
  131. padding++;
  132. }
  133. else {
  134. // Ignore whitespace and non-base64 characters
  135. continue;
  136. }
  137. block |= uint32_t(d) << (6 * (3 - i));
  138. i++;
  139. if (i >= 4) {
  140. // Decode block
  141. data.push_back((block >> (8 * (2 - 0))) & 0xff);
  142. if (padding < 2)
  143. data.push_back((block >> (8 * (2 - 1))) & 0xff);
  144. if (padding < 1)
  145. data.push_back((block >> (8 * (2 - 2))) & 0xff);
  146. // Reset block
  147. block = 0;
  148. i = 0;
  149. padding = 0;
  150. }
  151. }
  152. return data;
  153. }
  154. bool CaseInsensitiveCompare::operator()(const std::string& a, const std::string& b) const {
  155. for (size_t i = 0;; i++) {
  156. char ai = std::tolower(a[i]);
  157. char bi = std::tolower(b[i]);
  158. if (ai < bi)
  159. return true;
  160. if (ai > bi)
  161. return false;
  162. if (!ai || !bi)
  163. return false;
  164. }
  165. }
  166. #if defined ARCH_WIN
  167. std::string UTF16toUTF8(const std::wstring& w) {
  168. if (w.empty())
  169. return "";
  170. // Compute length of output buffer
  171. int len = WideCharToMultiByte(CP_UTF8, 0, &w[0], w.size(), NULL, 0, NULL, NULL);
  172. assert(len > 0);
  173. std::string s;
  174. // Allocate enough space for null character
  175. s.resize(len);
  176. len = WideCharToMultiByte(CP_UTF8, 0, &w[0], w.size(), &s[0], len, 0, 0);
  177. assert(len > 0);
  178. return s;
  179. }
  180. std::wstring UTF8toUTF16(const std::string& s) {
  181. if (s.empty())
  182. return L"";
  183. // Compute length of output buffer
  184. int len = MultiByteToWideChar(CP_UTF8, 0, &s[0], s.size(), NULL, 0);
  185. assert(len > 0);
  186. std::wstring w;
  187. // Allocate enough space for null character
  188. w.resize(len);
  189. len = MultiByteToWideChar(CP_UTF8, 0, &s[0], s.size(), &w[0], len);
  190. assert(len > 0);
  191. return w;
  192. }
  193. #endif
  194. } // namespace string
  195. } // namespace rack