@@ -15,47 +15,182 @@ namespace boost {
1515namespace decimal {
1616namespace detail {
1717
18- inline void convert_string_to_c_locale (char * buffer) noexcept
18+ // GCC-9 issues an erroneous warning for char == -30 being outside of type limits
19+ #if defined(__GNUC__) && __GNUC__ >= 9
20+ # pragma GCC diagnostic push
21+ # pragma GCC diagnostic ignored "-Wtype-limits"
22+ #endif
23+
24+ inline void convert_string_to_c_locale (char * buffer, const std::locale& loc) noexcept
1925{
20- const auto locale_decimal_point = *std::localeconv ()->decimal_point ;
26+ const std::numpunct<char >& np = std::use_facet<std::numpunct<char >>(loc);
27+
28+ const auto locale_decimal_point {np.decimal_point ()};
29+ auto locale_thousands_sep {np.thousands_sep ()};
30+ if (locale_thousands_sep == -30 )
31+ {
32+ locale_thousands_sep = ' ' ;
33+ }
34+ const bool has_grouping {!np.grouping ().empty () && np.grouping ()[0 ] > 0 };
35+
36+ // Remove thousands separator if it exists and grouping is enabled
37+ if (has_grouping && locale_thousands_sep != ' \0 ' )
38+ {
39+ // Find the decimal point first to know where the integer part ends
40+ const auto decimal_pos {std::strchr (buffer, static_cast <int >(locale_decimal_point))};
41+ const auto int_end {decimal_pos ? decimal_pos : (buffer + std::strlen (buffer))};
42+
43+ // Find the start of the number to include skipping sign
44+ auto start {buffer};
45+ if (*start == ' -' || *start == ' +' )
46+ {
47+ ++start;
48+ }
49+
50+ // Only remove thousands separators from the integer part
51+ auto read {start};
52+ auto write {start};
53+
54+ while (read < int_end)
55+ {
56+ const auto ch = *read;
57+ if (ch != locale_thousands_sep)
58+ {
59+ *write++ = *read;
60+ }
61+ ++read;
62+ }
63+
64+ // Copy the rest of the string (decimal point and fractional part)
65+ while (*read != ' \0 ' )
66+ {
67+ *write++ = *read++;
68+ }
69+ *write = ' \0 ' ;
70+ }
71+
2172 if (locale_decimal_point != ' .' )
2273 {
23- auto p = std::strchr (buffer, static_cast <int >(locale_decimal_point));
74+ const auto p { std::strchr (buffer, static_cast <int >(locale_decimal_point))} ;
2475 if (p != nullptr )
2576 {
2677 *p = ' .' ;
2778 }
2879 }
2980}
3081
31- inline void convert_string_to_local_locale (char * buffer) noexcept
82+ inline void convert_string_to_c_locale (char * buffer) noexcept
3283{
33- const auto locale_decimal_point = *std::localeconv ()->decimal_point ;
34- if (locale_decimal_point != ' .' )
84+ convert_string_to_c_locale (buffer, std::locale ());
85+ }
86+
87+ inline int convert_pointer_pair_to_local_locale (char * first, char * last, const std::locale& loc) noexcept
88+ {
89+ const std::numpunct<char >& np = std::use_facet<std::numpunct<char >>(loc);
90+
91+ const auto locale_decimal_point {np.decimal_point ()};
92+ auto locale_thousands_sep {np.thousands_sep ()};
93+ if (locale_thousands_sep == -30 )
3594 {
36- auto p = std::strchr (buffer, static_cast <int >(' .' ));
37- if (p != nullptr )
95+ locale_thousands_sep = ' ' ;
96+ }
97+ const bool has_grouping {!np.grouping ().empty () && np.grouping ()[0 ] > 0 };
98+ const int grouping_size {has_grouping ? np.grouping ()[0 ] : 0 };
99+
100+ // Find the start of the number (skip sign if present)
101+ char * start = first;
102+ if (start < last && (*start == ' -' || *start == ' +' ))
103+ {
104+ ++start;
105+ }
106+
107+ // Find the actual end of the string
108+ auto string_end {start};
109+ while (string_end < last && *string_end != ' \0 ' )
110+ {
111+ ++string_end;
112+ }
113+
114+ // Find decimal point position
115+ char * decimal_pos {nullptr };
116+ for (char * p = start; p < string_end; ++p)
117+ {
118+ if (*p == ' .' )
38119 {
39- *p = locale_decimal_point;
120+ decimal_pos = p;
121+ *decimal_pos = locale_decimal_point;
122+ break ;
40123 }
41124 }
42- }
43125
44- inline void convert_pointer_pair_to_local_locale (char * first, const char * last) noexcept
45- {
46- const auto locale_decimal_point = *std::localeconv ()->decimal_point ;
47- if (locale_decimal_point != ' .' )
126+ // Determine the end of the integer part
127+ const auto int_end {decimal_pos != nullptr ? decimal_pos : string_end};
128+ const auto int_digits {static_cast <int >(int_end - start)};
129+
130+ // Calculate how many separators we need
131+ int num_separators {};
132+ if (has_grouping && locale_thousands_sep != ' \0 ' && int_digits > 0 )
133+ {
134+ if (int_digits > grouping_size)
135+ {
136+ num_separators = (int_digits - 1 ) / grouping_size;
137+ }
138+ }
139+
140+ // If we need to add separators, shift content and insert them
141+ if (num_separators > 0 )
48142 {
49- while (first != last)
143+ const auto original_length {static_cast <int >(string_end - first)};
144+ const auto new_length {original_length + num_separators};
145+
146+ // Check if we have enough space in the buffer
147+ if (first + new_length >= last)
50148 {
51- if (*first == ' .' )
149+ // Not enough space, return error indicator
150+ return -1 ;
151+ }
152+
153+ // Shift everything after the integer part to make room
154+ // Work backwards to avoid overwriting
155+ auto old_pos {string_end};
156+ auto new_pos {first + new_length};
157+
158+ // Copy from end (including null terminator) back to the end of integer part
159+ while (old_pos >= int_end)
160+ {
161+ *new_pos-- = *old_pos--;
162+ }
163+
164+ // Now insert the integer digits with separators
165+ // Count digits from right to left (from decimal point backwards)
166+ old_pos = int_end - 1 ;
167+ int digits_from_right {1 };
168+
169+ while (old_pos >= start)
170+ {
171+ *new_pos-- = *old_pos--;
172+
173+ // Insert separator after every grouping_size digits from the right
174+ // but not after the leftmost digit
175+ if (old_pos >= start && digits_from_right % grouping_size == 0 )
52176 {
53- *first = locale_decimal_point ;
177+ *new_pos-- = locale_thousands_sep ;
54178 }
55-
56- ++first;
179+ ++digits_from_right;
57180 }
58181 }
182+
183+ return num_separators;
184+ }
185+
186+ #if defined(__GNUC__) && __GNUC__ == 9
187+ # pragma GCC diagnostic pop
188+ #endif
189+
190+ inline int convert_pointer_pair_to_local_locale (char * first, char * last)
191+ {
192+ const auto loc {std::locale ()};
193+ return convert_pointer_pair_to_local_locale (first, last, loc);
59194}
60195
61196} // namespace detail
0 commit comments