2
0

BsStringFormat.h 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. namespace BansheeEngine
  4. {
  5. /** @cond INTERNAL */
  6. /** @addtogroup String
  7. * @{
  8. */
  9. /** Helper class used for string formatting operations. */
  10. class StringFormat
  11. {
  12. private:
  13. /**
  14. * Data structure used during string formatting. It holds information about parameter identifiers to replace with
  15. * actual parameters.
  16. */
  17. struct FormatParamRange
  18. {
  19. FormatParamRange() { }
  20. FormatParamRange(UINT32 start, UINT32 identifierSize, UINT32 paramIdx)
  21. :start(start), identifierSize(identifierSize), paramIdx(paramIdx)
  22. { }
  23. UINT32 start;
  24. UINT32 identifierSize;
  25. UINT32 paramIdx;
  26. };
  27. /**
  28. * @brief Structure that holds value of a parameter during string
  29. * formatting.
  30. */
  31. template<class T>
  32. struct ParamData
  33. {
  34. T* buffer;
  35. UINT32 size;
  36. };
  37. public:
  38. /**
  39. * Formats the provided string by replacing the identifiers with the provided parameters. The identifiers are
  40. * represented like "{0}, {1}" in the source string, where the number represents the position of the parameter
  41. * that will be used for replacing the identifier.
  42. *
  43. * @note You may use "\" to escape identifier brackets.
  44. * @note Maximum identifier number is 19 (for a total of 20 unique identifiers. e.g. {20} won't be recognized as an identifier).
  45. * @note Total number of parameters that can be referenced is 200.
  46. */
  47. template<class T, class... Args>
  48. static BasicString<T> format(const T* source, Args&& ...args)
  49. {
  50. UINT32 strLength = getLength(source);
  51. ParamData<T> parameters[MAX_PARAMS];
  52. memset(parameters, 0, sizeof(parameters));
  53. getParams(parameters, 0U, std::forward<Args>(args)...);
  54. T bracketChars[MAX_IDENTIFIER_SIZE + 1];
  55. UINT32 bracketWriteIdx = 0;
  56. FormatParamRange paramRanges[MAX_PARAM_REFERENCES];
  57. memset(paramRanges, 0, sizeof(paramRanges));
  58. UINT32 paramRangeWriteIdx = 0;
  59. // Determine parameter positions
  60. INT32 lastBracket = -1;
  61. bool escaped = false;
  62. UINT32 charWriteIdx = 0;
  63. for (UINT32 i = 0; i < strLength; i++)
  64. {
  65. if (source[i] == '\\' && !escaped && paramRangeWriteIdx < MAX_PARAM_REFERENCES)
  66. {
  67. escaped = true;
  68. paramRanges[paramRangeWriteIdx++] = FormatParamRange(charWriteIdx, 1, (UINT32)-1);
  69. continue;
  70. }
  71. if (lastBracket == -1)
  72. {
  73. // If current char is non-escaped opening bracket start parameter definition
  74. if (source[i] == '{' && !escaped)
  75. lastBracket = i;
  76. else
  77. charWriteIdx++;
  78. }
  79. else
  80. {
  81. if (isdigit(source[i]) && bracketWriteIdx < MAX_IDENTIFIER_SIZE)
  82. bracketChars[bracketWriteIdx++] = source[i];
  83. else
  84. {
  85. // If current char is non-escaped closing bracket end parameter definition
  86. UINT32 numParamChars = bracketWriteIdx;
  87. bool processedBracket = false;
  88. if (source[i] == '}' && numParamChars > 0 && !escaped)
  89. {
  90. bracketChars[bracketWriteIdx] = '\0';
  91. UINT32 paramIdx = strToInt(bracketChars);
  92. if (paramIdx < MAX_PARAMS && paramRangeWriteIdx < MAX_PARAM_REFERENCES) // Check if exceeded maximum parameter limit
  93. {
  94. paramRanges[paramRangeWriteIdx++] = FormatParamRange(charWriteIdx, numParamChars + 2, paramIdx);
  95. charWriteIdx += parameters[paramIdx].size;
  96. processedBracket = true;
  97. }
  98. }
  99. if (!processedBracket)
  100. {
  101. // Last bracket wasn't really a parameter
  102. for (UINT32 j = lastBracket; j <= i; j++)
  103. charWriteIdx++;
  104. }
  105. lastBracket = -1;
  106. bracketWriteIdx = 0;
  107. }
  108. }
  109. escaped = false;
  110. }
  111. // Copy the clean string into output buffer
  112. UINT32 finalStringSize = charWriteIdx;
  113. T* outputBuffer = (T*)bs_alloc(finalStringSize * sizeof(T));
  114. UINT32 copySourceIdx = 0;
  115. UINT32 copyDestIdx = 0;
  116. for (UINT32 i = 0; i < paramRangeWriteIdx; i++)
  117. {
  118. const FormatParamRange& rangeInfo = paramRanges[i];
  119. UINT32 copySize = rangeInfo.start - copyDestIdx;
  120. memcpy(outputBuffer + copyDestIdx, source + copySourceIdx, copySize * sizeof(T));
  121. copySourceIdx += copySize + rangeInfo.identifierSize;
  122. copyDestIdx += copySize;
  123. if (rangeInfo.paramIdx == (UINT32)-1)
  124. continue;
  125. UINT32 paramSize = parameters[rangeInfo.paramIdx].size;
  126. memcpy(outputBuffer + copyDestIdx, parameters[rangeInfo.paramIdx].buffer, paramSize * sizeof(T));
  127. copyDestIdx += paramSize;
  128. }
  129. memcpy(outputBuffer + copyDestIdx, source + copySourceIdx, (finalStringSize - copyDestIdx) * sizeof(T));
  130. BasicString<T> outputStr(outputBuffer, finalStringSize);
  131. bs_free(outputBuffer);
  132. for (UINT32 i = 0; i < MAX_PARAMS; i++)
  133. {
  134. if (parameters[i].buffer != nullptr)
  135. bs_free(parameters[i].buffer);
  136. }
  137. return outputStr;
  138. }
  139. private:
  140. /**
  141. * Set of methods that can be specialized so we have a generalized way for retrieving length of strings of
  142. * different types.
  143. */
  144. static UINT32 getLength(const char* source) { return (UINT32)strlen(source); }
  145. /**
  146. * Set of methods that can be specialized so we have a generalized way for retrieving length of strings of
  147. * different types.
  148. */
  149. static UINT32 getLength(const wchar_t* source) { return (UINT32)wcslen(source); }
  150. /** Parses the string and returns an integer value extracted from string characters. */
  151. static UINT32 strToInt(const char* buffer)
  152. {
  153. return (UINT32)strtoul(buffer, nullptr, 10);
  154. }
  155. /** Parses the string and returns an integer value extracted from string characters. */
  156. static UINT32 strToInt(const wchar_t* buffer)
  157. {
  158. return (UINT32)wcstoul(buffer, nullptr, 10);
  159. }
  160. /** Helper method for converting any data type to a narrow string. */
  161. template<class T> static std::string toString(const T& param) { return std::to_string(param); }
  162. /** Helper method that "converts" a narrow string to a narrow string (simply a pass through). */
  163. template<> static std::string toString(const std::string& param) { return param; }
  164. /** Helper method that converts a Banshee narrow string to a standard narrow string. */
  165. template<> static std::string toString(const String& param)
  166. {
  167. return std::string(param.c_str());
  168. }
  169. /** Helper method that converts a narrow character array to a narrow string. */
  170. template<class T> static std::string toString(T* param) { static_assert("Invalid pointer type."); }
  171. /** Helper method that converts a narrow character array to a narrow string. */
  172. template<> static std::string toString<const char>(const char* param) { return std::string(param); }
  173. /** Helper method that converts a narrow character array to a narrow string. */
  174. template<> static std::string toString<char>(char* param) { return std::string(param); }
  175. /** Helper method for converting any data type to a wide string. */
  176. template<class T> static std::wstring toWString(const T& param) { return std::to_wstring(param); }
  177. /** Helper method that "converts" a wide string to a wide string (simply a pass through). */
  178. template<> static std::wstring toWString<std::wstring>(const std::wstring& param) { return param; }
  179. /** Helper method that converts a Banshee wide string to a standard wide string. */
  180. template<> static std::wstring toWString<WString>(const WString& param)
  181. {
  182. return std::wstring(param.c_str());
  183. }
  184. /** Helper method that converts a wide character array to a wide string. */
  185. template<class T> static std::wstring toWString(T* param) { static_assert("Invalid pointer type."); }
  186. /** Helper method that converts a wide character array to a wide string. */
  187. template<> static std::wstring toWString<const wchar_t>(const wchar_t* param) { return std::wstring(param); }
  188. /** Helper method that converts a wide character array to a wide string. */
  189. template<> static std::wstring toWString<wchar_t>(wchar_t* param) { return std::wstring(param); }
  190. /**
  191. * Converts all the provided parameters into string representations and populates the provided @p parameters array.
  192. */
  193. template<class P, class... Args>
  194. static void getParams(ParamData<char>* parameters, UINT32 idx, P&& param, Args&& ...args)
  195. {
  196. if (idx >= MAX_PARAMS)
  197. return;
  198. std::basic_string<char> sourceParam = toString(param);
  199. parameters[idx].buffer = (char*)bs_alloc((UINT32)sourceParam.size() * sizeof(char));
  200. parameters[idx].size = (UINT32)sourceParam.size();
  201. sourceParam.copy(parameters[idx].buffer, parameters[idx].size, 0);
  202. getParams(parameters, idx + 1, std::forward<Args>(args)...);
  203. }
  204. /**
  205. * Converts all the provided parameters into string representations and populates the provided @p parameters array.
  206. */
  207. template<class P, class... Args>
  208. static void getParams(ParamData<wchar_t>* parameters, UINT32 idx, P&& param, Args&& ...args)
  209. {
  210. if (idx >= MAX_PARAMS)
  211. return;
  212. std::basic_string<wchar_t> sourceParam = toWString(param);
  213. parameters[idx].buffer = (wchar_t*)bs_alloc((UINT32)sourceParam.size() * sizeof(wchar_t));
  214. parameters[idx].size = (UINT32)sourceParam.size();
  215. sourceParam.copy(parameters[idx].buffer, parameters[idx].size, 0);
  216. getParams(parameters, idx + 1, std::forward<Args>(args)...);
  217. }
  218. /** Helper method for parameter size calculation. Used as a stopping point in template recursion. */
  219. static void getParams(ParamData<char>* parameters, UINT32 idx)
  220. {
  221. // Do nothing
  222. }
  223. /** Helper method for parameter size calculation. Used as a stopping point in template recursion. */
  224. static void getParams(ParamData<wchar_t>* parameters, UINT32 idx)
  225. {
  226. // Do nothing
  227. }
  228. static const UINT32 MAX_PARAMS = 20;
  229. static const UINT32 MAX_IDENTIFIER_SIZE = 2;
  230. static const UINT32 MAX_PARAM_REFERENCES = 200;
  231. };
  232. /** @} */
  233. /** @endcond */
  234. }