@@ -76,6 +76,35 @@ static const char *bc_count_digits(const char *str, const char *end)
7676 return str ;
7777}
7878
79+ static inline const char * bc_skip_zero_reverse (const char * scanner , const char * stop )
80+ {
81+ /* Check in bulk */
82+ #ifdef __SSE2__
83+ const __m128i c_zero_repeat = _mm_set1_epi8 ('0' );
84+ while (scanner - sizeof (__m128i ) >= stop ) {
85+ scanner -= sizeof (__m128i );
86+ __m128i bytes = _mm_loadu_si128 ((const __m128i * ) scanner );
87+ /* Checks if all numeric strings are equal to '0'. */
88+ bytes = _mm_cmpeq_epi8 (bytes , c_zero_repeat );
89+
90+ int mask = _mm_movemask_epi8 (bytes );
91+ /* The probability of having 16 trailing 0s in a row is very low, so we use EXPECTED. */
92+ if (EXPECTED (mask != 0xffff )) {
93+ /* Move the pointer back and check each character in loop. */
94+ scanner += sizeof (__m128i );
95+ break ;
96+ }
97+ }
98+ #endif
99+
100+ /* Exclude trailing zeros. */
101+ while (scanner - 1 >= stop && scanner [-1 ] == '0' ) {
102+ scanner -- ;
103+ }
104+
105+ return scanner ;
106+ }
107+
79108/* Assumes `num` points to NULL, i.e. does yet not hold a number. */
80109bool bc_str2num (bc_num * num , const char * str , const char * end , size_t scale , bool auto_scale )
81110{
@@ -104,32 +133,28 @@ bool bc_str2num(bc_num *num, const char *str, const char *end, size_t scale, boo
104133 const char * decimal_point = (* ptr == '.' ) ? ptr : NULL ;
105134
106135 /* If a non-digit and non-decimal-point indicator is in the string, i.e. an invalid character */
107- if (!decimal_point && * ptr != '\0' ) {
136+ if (UNEXPECTED ( !decimal_point && * ptr != '\0' ) ) {
108137 goto fail ;
109138 }
110139
111140 /* search and validate fractional end if exists */
112141 if (decimal_point ) {
113142 /* search */
114143 fractional_ptr = fractional_end = decimal_point + 1 ;
115- if (* fractional_ptr == '\0' ) {
144+ /* For strings that end with a decimal point, such as "012." */
145+ if (UNEXPECTED (* fractional_ptr == '\0' )) {
116146 goto after_fractional ;
117147 }
118148
119149 /* validate */
120150 fractional_end = bc_count_digits (fractional_ptr , end );
121- if (* fractional_end != '\0' ) {
151+ if (UNEXPECTED ( * fractional_end != '\0' ) ) {
122152 /* invalid num */
123153 goto fail ;
124154 }
125155
126156 /* Exclude trailing zeros. */
127- while (fractional_end - 1 > decimal_point && fractional_end [-1 ] == '0' ) {
128- fractional_end -- ;
129- }
130-
131- /* Move the pointer to the beginning of the fraction. */
132- fractional_ptr = decimal_point + 1 ;
157+ fractional_end = bc_skip_zero_reverse (fractional_end , fractional_ptr );
133158
134159 /* Calculate the length of the fraction excluding trailing zero. */
135160 str_scale = fractional_end - fractional_ptr ;
0 commit comments