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.

225 lines
4.8KB

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