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