src/zserio/StringConvertUtil.h
Line | Count | Source |
1 | | #ifndef ZSERIO_STRING_CONVERT_UTIL_H_INC |
2 | | #define ZSERIO_STRING_CONVERT_UTIL_H_INC |
3 | | |
4 | | #include <array> |
5 | | #include <limits> |
6 | | #include <sstream> |
7 | | |
8 | | #include "zserio/RebindAlloc.h" |
9 | | #include "zserio/String.h" |
10 | | |
11 | | namespace zserio |
12 | | { |
13 | | |
14 | | namespace detail |
15 | | { |
16 | | |
17 | | /** |
18 | | * Converts integer value to string into the given buffer. |
19 | | * |
20 | | * The string is filled from backwards starting at the 24th byte. |
21 | | * |
22 | | * \return Beginning of the resulting string which is null-terminated. |
23 | | */ |
24 | | template <typename T, |
25 | | typename std::enable_if<std::is_unsigned<T>::value && !std::is_same<T, bool>::value, int>::type = 0> |
26 | | const char* convertIntToString(std::array<char, 24>& buffer, T value, bool isNegative) |
27 | 2.46k | { |
28 | 2.46k | static const std::array<char, 201> DIGITS = { |
29 | 2.46k | "0001020304050607080910111213141516171819" |
30 | 2.46k | "2021222324252627282930313233343536373839" |
31 | 2.46k | "4041424344454647484950515253545556575859" |
32 | 2.46k | "6061626364656667686970717273747576777879" |
33 | 2.46k | "8081828384858687888990919293949596979899"}; |
34 | | |
35 | 2.46k | auto bufferEnd = buffer.end(); |
36 | 2.46k | *(--bufferEnd) = 0; // always terminate with '\0' |
37 | | |
38 | 4.99k | while (value >= 100) |
39 | 2.52k | { |
40 | 2.52k | const unsigned int index = static_cast<unsigned int>((value % 100) * 2); |
41 | 2.52k | value /= 100; |
42 | 2.52k | *(--bufferEnd) = DIGITS[index + 1]; |
43 | 2.52k | *(--bufferEnd) = DIGITS[index]; |
44 | 2.52k | } |
45 | | |
46 | 2.46k | if (value < 10) |
47 | 1.66k | { |
48 | 1.66k | *(--bufferEnd) = static_cast<char>('0' + value); |
49 | 1.66k | } |
50 | 804 | else |
51 | 804 | { |
52 | 804 | const unsigned int index = static_cast<unsigned int>(value * 2); |
53 | 804 | *(--bufferEnd) = DIGITS[index + 1]; |
54 | 804 | *(--bufferEnd) = DIGITS[index]; |
55 | 804 | } |
56 | | |
57 | 2.46k | if (isNegative) |
58 | 231 | { |
59 | 231 | *(--bufferEnd) = '-'; |
60 | 231 | } |
61 | | |
62 | 2.46k | return &(*bufferEnd); |
63 | 2.46k | } |
64 | | |
65 | | } // namespace detail |
66 | | |
67 | | /** |
68 | | * Converts unsigned integral value to string and writes the result to the given buffer. |
69 | | * |
70 | | * Note that the buffer is filled from behind. |
71 | | * |
72 | | * \param buffer Buffer to fill with the string representation of the given value. |
73 | | * \param value Value to convert. |
74 | | * |
75 | | * \return Pointer to the beginning of the resulting string. |
76 | | */ |
77 | | template <typename T, typename std::enable_if<std::is_unsigned<T>::value, int>::type = 0> |
78 | | const char* convertIntToString(std::array<char, 24>& buffer, T value) |
79 | 1.96k | { |
80 | 1.96k | return detail::convertIntToString(buffer, value, false); |
81 | 1.96k | } |
82 | | |
83 | | /** |
84 | | * Converts signed integral value to string and writes the result to the given buffer. |
85 | | * |
86 | | * Note that the buffer is filled from behind. |
87 | | * |
88 | | * \param buffer Buffer to fill with the string representation of the given value. |
89 | | * \param value Value to convert. |
90 | | * |
91 | | * \return Pointer to the beginning of the resulting string. |
92 | | */ |
93 | | template <typename T, typename std::enable_if<std::is_signed<T>::value, int>::type = 0> |
94 | | const char* convertIntToString(std::array<char, 24>& buffer, T value) |
95 | 504 | { |
96 | 504 | using unsigned_type = typename std::make_unsigned<T>::type; |
97 | 504 | unsigned_type absValue = static_cast<unsigned_type>(value); |
98 | 504 | const bool isNegative = value < 0; |
99 | 504 | if (isNegative) |
100 | 231 | { |
101 | 231 | absValue = static_cast<unsigned_type>(0 - absValue); |
102 | 231 | } |
103 | | |
104 | 504 | return detail::convertIntToString(buffer, absValue, isNegative); |
105 | 504 | } |
106 | | |
107 | | /** |
108 | | * Converts float to string and writes the result to the given buffer. |
109 | | * |
110 | | * Note that only five three digits after point are used and that the buffers are filled from behind. |
111 | | * |
112 | | * \param integerPartBuffer Buffer to fill with the string representation of the integer part. |
113 | | * \param floatingPartBuffer Buffer to fill with the string representation of the floating part. |
114 | | * \param value Value to convert. |
115 | | * \param floatingPartString Reference where to fill pointer to the beginning of the floating part in string. |
116 | | * \param integerPartString Reference where to fill pointer to the beginning of the integer part in string. |
117 | | */ |
118 | | inline void convertFloatToString(std::array<char, 24>& integerPartBuffer, |
119 | | std::array<char, 24>& floatingPartBuffer, float value, const char*& integerPartString, |
120 | | const char*& floatingPartString) |
121 | 21 | { |
122 | 21 | if (value >= static_cast<float>(std::numeric_limits<int64_t>::max())) |
123 | 2 | { |
124 | 2 | integerPartString = "+Inf"; |
125 | 2 | floatingPartString = nullptr; |
126 | 2 | } |
127 | 19 | else if (value <= static_cast<float>(std::numeric_limits<int64_t>::min())) |
128 | 1 | { |
129 | 1 | integerPartString = "-Inf"; |
130 | 1 | floatingPartString = nullptr; |
131 | 1 | } |
132 | 18 | else |
133 | 18 | { |
134 | 18 | const int64_t integerPart = static_cast<int64_t>(value); |
135 | 18 | const int64_t floatingPart = |
136 | 18 | static_cast<int64_t>((value - static_cast<float>(integerPart)) * 1e3F); // 3 digits |
137 | 18 | const int64_t floatingPartAbs = (floatingPart < 0) ? 0 - floatingPart1 : floatingPart17 ; |
138 | 18 | integerPartString = convertIntToString(integerPartBuffer, integerPart); |
139 | 18 | floatingPartString = convertIntToString(floatingPartBuffer, floatingPartAbs); |
140 | 18 | } |
141 | 21 | } |
142 | | |
143 | | /** |
144 | | * Converts bool value to boolalpha C-string ("true" or "false"). |
145 | | * |
146 | | * \param value Value to convert. |
147 | | * |
148 | | * \return C-string representation of the given bool value. |
149 | | */ |
150 | | inline const char* convertBoolToString(bool value) |
151 | 23 | { |
152 | 23 | return value ? "true"13 : "false"10 ; |
153 | 23 | } |
154 | | |
155 | | /** |
156 | | * Converts an integral value to string using the given allocator. Defined for convenience. |
157 | | * |
158 | | * \param value Value to convert. |
159 | | * \param allocator Allocator to use for the string allocation. |
160 | | * |
161 | | * \return String representation of the given integral value. |
162 | | */ |
163 | | template <typename ALLOC, typename T> |
164 | | string<ALLOC> toString(T value, const ALLOC& allocator = ALLOC()) |
165 | 497 | { |
166 | 497 | std::array<char, 24> buffer = {}; |
167 | 497 | return string<ALLOC>(convertIntToString(buffer, value), allocator); |
168 | 497 | } |
169 | | |
170 | | /** |
171 | | * Converts a boolean value to string using the given allocator. Defined for convenience. |
172 | | * |
173 | | * Note that in contrast to std::to_string, this behaves as STL streams with boolalpha flag and produces |
174 | | * "true" and "false" strings. |
175 | | * |
176 | | * \param value Value to convert. |
177 | | * \param allocator Allocator to use for the string allocation. |
178 | | */ |
179 | | template <typename ALLOC> |
180 | | string<ALLOC> toString(bool value, const ALLOC& allocator = ALLOC()) |
181 | 21 | { |
182 | 21 | return string<ALLOC>(convertBoolToString(value), allocator); |
183 | 21 | } |
184 | | |
185 | | /** |
186 | | * Converts an integral (or a boolean) value to string. Convenience wrapper to call without allocator. |
187 | | * |
188 | | * \param value Value to convert. |
189 | | * |
190 | | * \return String representation of the given value. |
191 | | */ |
192 | | template <typename T> |
193 | | string<std::allocator<char>> toString(T value) |
194 | 287 | { |
195 | 287 | return toString<std::allocator<char>>(value); |
196 | 287 | } |
197 | | |
198 | | } // namespace zserio |
199 | | |
200 | | #endif // ifndef ZSERIO_STRING_CONVERT_UTIL_H_INC |