1 /***************************************************************************** 2 * Copyright (c) 2014-2020 OpenRCT2 developers 3 * 4 * For a complete list of all authors, please refer to contributors.md 5 * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 6 * 7 * OpenRCT2 is licensed under the GNU General Public License version 3. 8 *****************************************************************************/ 9 10 #pragma once 11 12 #include "../common.h" 13 #include "FormatCodes.h" 14 #include "Language.h" 15 16 #include <cstring> 17 #include <sstream> 18 #include <stack> 19 #include <string> 20 #include <string_view> 21 #include <utility> 22 #include <variant> 23 #include <vector> 24 25 namespace OpenRCT2 26 { 27 template<typename T, size_t StackSize = 256, typename TTraits = std::char_traits<T>> class FormatBufferBase 28 { 29 T _storage[StackSize]; 30 T* _buffer; 31 size_t _size; 32 // NOTE: Capacity is on purpose uint32_t to have a fixed position for the flag on each architecture. 33 uint32_t _capacity; 34 TTraits _traits; 35 36 static constexpr uint32_t FlagLocalStorage = (1u << 31); 37 38 public: FormatBufferBase()39 explicit FormatBufferBase() 40 : _storage{} 41 , _buffer(_storage) 42 , _size{} 43 , _capacity(FlagLocalStorage | static_cast<uint32_t>(StackSize)) 44 { 45 } 46 ~FormatBufferBase()47 ~FormatBufferBase() 48 { 49 if (_capacity & FlagLocalStorage) 50 return; 51 delete[] _buffer; 52 } 53 size()54 size_t size() const 55 { 56 return _size; 57 } 58 capacity()59 size_t capacity() const 60 { 61 return _capacity & ~FlagLocalStorage; 62 } 63 clear()64 void clear() 65 { 66 _size = 0; 67 _buffer[0] = T{}; 68 } 69 data()70 const T* data() const 71 { 72 return _buffer; 73 } 74 data()75 T* data() 76 { 77 return _buffer; 78 } 79 80 template<size_t N> auto& operator<<(T const (&v)[N]) 81 { 82 append(v, N); 83 return *this; 84 } 85 86 auto& operator<<(const T v) 87 { 88 append(&v, 1); 89 return *this; 90 } 91 92 auto& operator<<(const T* v) 93 { 94 if (!v) 95 return *this; 96 97 append(v, _traits.length(v)); 98 return *this; 99 } 100 101 auto& operator<<(const std::basic_string_view<T> v) 102 { 103 append(v.data(), v.size()); 104 return *this; 105 } 106 107 auto& operator<<(const std::basic_string<T>& v) 108 { 109 append(v.data(), v.size()); 110 return *this; 111 } 112 append(const T * buf,size_t len)113 void append(const T* buf, size_t len) 114 { 115 ensure_capacity(len); 116 117 std::copy(buf, buf + len, _buffer + _size); 118 119 _size += len; 120 _buffer[_size] = T{}; 121 } 122 123 private: ensure_capacity(size_t additionalSize)124 void ensure_capacity(size_t additionalSize) 125 { 126 const size_t curSize = size(); 127 const size_t curCapacity = capacity(); 128 const bool isLocalStorage = _capacity & FlagLocalStorage; 129 130 if (curSize + additionalSize < curCapacity) 131 return; 132 133 const size_t newCapacity = (curCapacity + additionalSize + 1) << 1; 134 135 T* newBuf = new T[newCapacity]; 136 std::copy(_buffer, _buffer + curSize, newBuf); 137 138 if (!isLocalStorage) 139 delete[] _buffer; 140 141 _capacity = static_cast<uint32_t>(newCapacity); 142 _buffer = newBuf; 143 } 144 }; 145 146 using FormatBuffer = FormatBufferBase<char>; 147 148 using FormatArg_t = std::variant<uint16_t, int32_t, int64_t, const char*, std::string>; 149 150 class FmtString 151 { 152 private: 153 std::string_view _str; 154 std::string _strOwned; 155 156 public: 157 struct token 158 { 159 FormatToken kind{}; 160 std::string_view text; 161 uint32_t parameter{}; 162 163 token() = default; 164 token(FormatToken k, std::string_view s, uint32_t p = 0); 165 bool IsLiteral() const; 166 bool IsCodepoint() const; 167 codepoint_t GetCodepoint() const; 168 }; 169 170 struct iterator 171 { 172 private: 173 std::string_view str; 174 size_t index; 175 token current; 176 177 void update(); 178 179 public: 180 iterator(std::string_view s, size_t i); 181 bool operator==(iterator& rhs); 182 bool operator!=(iterator& rhs); 183 token CreateToken(size_t len); 184 const token* operator->() const; 185 const token& operator*(); 186 iterator& operator++(); 187 iterator operator++(int); 188 bool eol() const; 189 }; 190 191 FmtString() = default; 192 FmtString(std::string&& s); 193 FmtString(std::string_view s); 194 FmtString(const char* s); 195 iterator begin() const; 196 iterator end() const; 197 198 std::string WithoutFormatTokens() const; 199 }; 200 201 template<typename T> void FormatArgument(FormatBuffer& ss, FormatToken token, T arg); 202 203 bool IsRealNameStringId(rct_string_id id); 204 void FormatRealName(FormatBuffer& ss, rct_string_id id); 205 FmtString GetFmtStringById(rct_string_id id); 206 FormatBuffer& GetThreadFormatStream(); 207 size_t CopyStringStreamToBuffer(char* buffer, size_t bufferLen, FormatBuffer& ss); 208 FormatString(FormatBuffer & ss,std::stack<FmtString::iterator> & stack)209 inline void FormatString(FormatBuffer& ss, std::stack<FmtString::iterator>& stack) 210 { 211 while (!stack.empty()) 212 { 213 auto& it = stack.top(); 214 while (!it.eol()) 215 { 216 const auto& token = *it; 217 if (!FormatTokenTakesArgument(token.kind)) 218 { 219 ss << token.text; 220 } 221 it++; 222 } 223 stack.pop(); 224 } 225 } 226 227 template<typename TArg0, typename... TArgs> FormatString(FormatBuffer & ss,std::stack<FmtString::iterator> & stack,TArg0 arg0,TArgs &&...argN)228 static void FormatString(FormatBuffer& ss, std::stack<FmtString::iterator>& stack, TArg0 arg0, TArgs&&... argN) 229 { 230 while (!stack.empty()) 231 { 232 auto& it = stack.top(); 233 while (!it.eol()) 234 { 235 auto token = *it++; 236 if (token.kind == FormatToken::StringId) 237 { 238 if constexpr (std::is_integral<TArg0>()) 239 { 240 auto stringId = static_cast<rct_string_id>(arg0); 241 if (IsRealNameStringId(stringId)) 242 { 243 FormatRealName(ss, stringId); 244 return FormatString(ss, stack, argN...); 245 } 246 247 auto subfmt = GetFmtStringById(stringId); 248 auto subit = subfmt.begin(); 249 stack.push(subit); 250 return FormatString(ss, stack, argN...); 251 } 252 } 253 else if (FormatTokenTakesArgument(token.kind)) 254 { 255 FormatArgument(ss, token.kind, arg0); 256 return FormatString(ss, stack, argN...); 257 } 258 259 ss << token.text; 260 } 261 stack.pop(); 262 } 263 } 264 FormatString(FormatBuffer & ss,const FmtString & fmt,TArgs &&...argN)265 template<typename... TArgs> static void FormatString(FormatBuffer& ss, const FmtString& fmt, TArgs&&... argN) 266 { 267 std::stack<FmtString::iterator> stack; 268 stack.push(fmt.begin()); 269 FormatString(ss, stack, argN...); 270 } 271 FormatString(const FmtString & fmt,TArgs &&...argN)272 template<typename... TArgs> std::string FormatString(const FmtString& fmt, TArgs&&... argN) 273 { 274 auto& ss = GetThreadFormatStream(); 275 FormatString(ss, fmt, argN...); 276 return ss.data(); 277 } 278 279 template<typename... TArgs> FormatStringToBuffer(char * buffer,size_t bufferLen,const FmtString & fmt,TArgs &&...argN)280 size_t FormatStringToBuffer(char* buffer, size_t bufferLen, const FmtString& fmt, TArgs&&... argN) 281 { 282 auto& ss = GetThreadFormatStream(); 283 FormatString(ss, fmt, argN...); 284 return CopyStringStreamToBuffer(buffer, bufferLen, ss); 285 } 286 FormatStringId(FormatBuffer & ss,rct_string_id id,TArgs &&...argN)287 template<typename... TArgs> static void FormatStringId(FormatBuffer& ss, rct_string_id id, TArgs&&... argN) 288 { 289 auto fmt = GetFmtStringById(id); 290 FormatString(ss, fmt, argN...); 291 } 292 FormatStringId(rct_string_id id,TArgs &&...argN)293 template<typename... TArgs> std::string FormatStringId(rct_string_id id, TArgs&&... argN) 294 { 295 auto fmt = GetFmtStringById(id); 296 return FormatString(fmt, argN...); 297 } 298 FormatStringId(char * buffer,size_t bufferLen,rct_string_id id,TArgs &&...argN)299 template<typename... TArgs> size_t FormatStringId(char* buffer, size_t bufferLen, rct_string_id id, TArgs&&... argN) 300 { 301 auto& ss = GetThreadFormatStream(); 302 FormatStringId(ss, id, argN...); 303 return CopyStringStreamToBuffer(buffer, bufferLen, ss); 304 } 305 306 std::string FormatStringAny(const FmtString& fmt, const std::vector<FormatArg_t>& args); 307 size_t FormatStringAny(char* buffer, size_t bufferLen, const FmtString& fmt, const std::vector<FormatArg_t>& args); 308 size_t FormatStringLegacy(char* buffer, size_t bufferLen, rct_string_id id, const void* args); 309 } // namespace OpenRCT2 310