Skip to content

Commit cfb8e38

Browse files
committed
refactoring cstring_to_quad as an internal helper
1 parent c918666 commit cfb8e38

File tree

3 files changed

+107
-110
lines changed

3 files changed

+107
-110
lines changed

quaddtype/numpy_quaddtype/src/dtype.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -354,11 +354,11 @@ quadprec_scanfunc(FILE *fp, void *dptr, char *ignore, PyArray_Descr *descr_gener
354354
}
355355
buffer[i] = '\0';
356356

357-
/* Convert string to quad precision */
357+
/* Convert string to quad precision (supports inf/nan) */
358358
char *endptr;
359359
quad_value val;
360-
int err = cstring_to_quad(buffer, descr->backend, &val, &endptr, true);
361-
if (err < 0) {
360+
int err = NumPyOS_ascii_strtoq(buffer, descr->backend, &val, &endptr);
361+
if (err < 0 || *endptr != '\0') {
362362
return 0; /* Return 0 on parse error (no items read) */
363363
}
364364
if (descr->backend == BACKEND_SLEEF) {
@@ -376,7 +376,7 @@ quadprec_fromstr(char *s, void *dptr, char **endptr, PyArray_Descr *descr_generi
376376
{
377377
QuadPrecDTypeObject *descr = (QuadPrecDTypeObject *)descr_generic;
378378
quad_value val;
379-
int err = cstring_to_quad(s, descr->backend, &val, endptr, false);
379+
int err = NumPyOS_ascii_strtoq(s, descr->backend, &val, endptr);
380380
if (err < 0) {
381381
return -1;
382382
}

quaddtype/numpy_quaddtype/src/utilities.c

Lines changed: 103 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,105 @@ ascii_strncasecmp(const char *s1, const char *s2, size_t n)
6262
return 0;
6363
}
6464

65+
/*
66+
* Internal helper: Parse numeric string to quad-precision.
67+
* Assumes no leading whitespace and no inf/nan (caller handles those).
68+
* The 'start' parameter points to where the original string started (for endptr on error).
69+
*/
70+
static int
71+
cstring_to_quad_internal(const char *str, const char *start, QuadBackendType backend,
72+
quad_value *out_value, char **endptr)
73+
{
74+
if (backend == BACKEND_SLEEF) {
75+
// SLEEF 4.0's Sleef_strtoq doesn't properly set endptr to indicate
76+
// where parsing stopped. We need to manually validate and track the parse position.
77+
78+
const char *p = str;
79+
80+
// Handle optional sign
81+
if (*p == '+' || *p == '-') {
82+
p++;
83+
}
84+
85+
// Must have at least one digit or decimal point followed by digit
86+
int has_digits = 0;
87+
88+
// Parse integer part
89+
while (ascii_isdigit(*p)) {
90+
has_digits = 1;
91+
p++;
92+
}
93+
94+
// Parse decimal point and fractional part
95+
if (*p == '.') {
96+
p++;
97+
while (ascii_isdigit(*p)) {
98+
has_digits = 1;
99+
p++;
100+
}
101+
}
102+
103+
// Must have at least one digit somewhere
104+
if (!has_digits) {
105+
if (endptr) *endptr = (char *)start;
106+
return -1;
107+
}
108+
109+
// Parse optional exponent
110+
if (*p == 'e' || *p == 'E') {
111+
const char *exp_start = p;
112+
p++;
113+
114+
// Optional sign in exponent
115+
if (*p == '+' || *p == '-') {
116+
p++;
117+
}
118+
119+
// Must have at least one digit in exponent
120+
if (!ascii_isdigit(*p)) {
121+
// Invalid exponent, backtrack
122+
p = exp_start;
123+
} else {
124+
while (ascii_isdigit(*p)) {
125+
p++;
126+
}
127+
}
128+
}
129+
130+
// Now p points to where valid parsing ends
131+
// Create a null-terminated substring for SLEEF
132+
size_t len = p - str;
133+
char *temp = (char *)malloc(len + 1);
134+
if (!temp) {
135+
if (endptr) *endptr = (char *)start;
136+
return -1;
137+
}
138+
memcpy(temp, str, len);
139+
temp[len] = '\0';
140+
141+
// Call Sleef_strtoq with the bounded string
142+
char *sleef_endptr;
143+
out_value->sleef_value = Sleef_strtoq(temp, &sleef_endptr);
144+
free(temp);
145+
146+
// Set endptr to our calculated position
147+
if (endptr) {
148+
*endptr = (char *)p;
149+
}
150+
151+
} else {
152+
out_value->longdouble_value = strtold(str, endptr);
153+
}
154+
155+
if (endptr && *endptr == str) {
156+
// Nothing was parsed - set endptr to original start
157+
*endptr = (char *)start;
158+
return -1;
159+
}
160+
161+
return 0; // success
162+
}
163+
65164
/*
66165
* NumPyOS_ascii_strtoq:
67166
*
@@ -71,7 +170,7 @@ ascii_strncasecmp(const char *s1, const char *s2, size_t n)
71170
* This function:
72171
* - Skips leading whitespace
73172
* - Recognizes inf/nan case-insensitively with optional signs and payloads
74-
* - Delegates to cstring_to_quad for numeric parsing
173+
* - Parses numeric values
75174
*
76175
* Returns:
77176
* 0 on success
@@ -153,110 +252,9 @@ NumPyOS_ascii_strtoq(const char *s, QuadBackendType backend, quad_value *out_val
153252
return 0;
154253
}
155254

156-
// For numeric values, delegate to cstring_to_quad
157-
// Pass the original string position (after whitespace, includes sign if present)
158-
return cstring_to_quad(s, backend, out_value, endptr, false);
159-
}
160-
161-
int cstring_to_quad(const char *str, QuadBackendType backend, quad_value *out_value,
162-
char **endptr, bool require_full_parse)
163-
{
164-
if(backend == BACKEND_SLEEF) {
165-
// SLEEF 4.0's Sleef_strtoq doesn't properly set endptr to indicate
166-
// where parsing stopped. It always sets endptr to the end of the string.
167-
// We need to manually validate and track the parse position.
168-
169-
const char *p = str;
170-
171-
// Skip leading whitespace
172-
while (ascii_isspace(*p)) {
173-
p++;
174-
}
175-
176-
// Handle optional sign
177-
if (*p == '+' || *p == '-') {
178-
p++;
179-
}
180-
181-
// Must have at least one digit or decimal point followed by digit
182-
int has_digits = 0;
183-
184-
// Parse integer part
185-
while (ascii_isdigit(*p)) {
186-
has_digits = 1;
187-
p++;
188-
}
189-
190-
// Parse decimal point and fractional part
191-
if (*p == '.') {
192-
p++;
193-
while (ascii_isdigit(*p)) {
194-
has_digits = 1;
195-
p++;
196-
}
197-
}
198-
199-
// Must have at least one digit somewhere
200-
if (!has_digits) {
201-
if (endptr) *endptr = (char *)str;
202-
return -1;
203-
}
204-
205-
// Parse optional exponent
206-
if (*p == 'e' || *p == 'E') {
207-
const char *exp_start = p;
208-
p++;
209-
210-
// Optional sign in exponent
211-
if (*p == '+' || *p == '-') {
212-
p++;
213-
}
214-
215-
// Must have at least one digit in exponent
216-
if (!ascii_isdigit(*p)) {
217-
// Invalid exponent, backtrack
218-
p = exp_start;
219-
} else {
220-
while (ascii_isdigit(*p)) {
221-
p++;
222-
}
223-
}
224-
}
225-
226-
// Now p points to where valid parsing ends
227-
// SLEEF 4.0's Sleef_strtoq has a bug where it doesn't properly stop at whitespace
228-
// or other delimiters. We need to create a null-terminated substring.
229-
size_t len = p - str;
230-
char *temp = (char *)malloc(len + 1);
231-
if (!temp) {
232-
if (endptr) *endptr = (char *)str;
233-
return -1;
234-
}
235-
memcpy(temp, str, len);
236-
temp[len] = '\0';
237-
238-
// Call Sleef_strtoq with the bounded string
239-
char *sleef_endptr;
240-
out_value->sleef_value = Sleef_strtoq(temp, &sleef_endptr);
241-
free(temp);
242-
243-
// Set endptr to our calculated position
244-
if (endptr) {
245-
*endptr = (char *)p;
246-
}
247-
248-
} else {
249-
out_value->longdouble_value = strtold(str, endptr);
250-
}
251-
252-
if(endptr && *endptr == str)
253-
return -1; // parse error - nothing was parsed
254-
255-
// If full parse is required
256-
if(require_full_parse && endptr && **endptr != '\0')
257-
return -1; // parse error - characters remain to be converted
258-
259-
return 0; // success
255+
// For numeric values, parse starting from 's' (includes sign if present)
256+
// The sign is part of the number, not handled separately like inf/nan
257+
return cstring_to_quad_internal(s, s, backend, out_value, endptr);
260258
}
261259

262260
// Helper function: Convert quad_value to Sleef_quad for Dragon4

quaddtype/numpy_quaddtype/src/utilities.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ extern "C" {
1010
#include <sleefquad.h>
1111
#include <stdbool.h>
1212

13-
int cstring_to_quad(const char *str, QuadBackendType backend, quad_value *out_value, char **endptr, bool require_full_parse);
1413
int ascii_isspace(int c);
1514
int ascii_isalpha(char c);
1615
int ascii_isdigit(char c);

0 commit comments

Comments
 (0)