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