BsStringFormat.h 9.9 KB

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