@@ -20,8 +20,9 @@ extern "C" {
2020#include " scalar.h"
2121#include " casts.h"
2222#include " dtype.h"
23+ #include " utilities.h"
2324
24- #define NUM_CASTS 34 // 16 to_casts + 16 from_casts + 1 quad_to_quad + 1 void_to_quad
25+ #define NUM_CASTS 35 // 16 to_casts + 16 from_casts + 1 quad_to_quad + 1 void_to_quad + 1 unicode_to_quad
2526#define QUAD_STR_WIDTH 50 // 42 is enough for scientific notation float128, just keeping some buffer
2627
2728static NPY_CASTING
@@ -172,6 +173,151 @@ void_to_quad_strided_loop(PyArrayMethod_Context *context, char *const data[],
172173 return -1 ;
173174}
174175
176+ // Unicode/String to QuadDType casting
177+ static NPY_CASTING
178+ unicode_to_quad_resolve_descriptors (PyObject *NPY_UNUSED (self), PyArray_DTypeMeta *dtypes[2],
179+ PyArray_Descr *given_descrs[2], PyArray_Descr *loop_descrs[2],
180+ npy_intp *view_offset)
181+ {
182+ Py_INCREF (given_descrs[0 ]);
183+ loop_descrs[0 ] = given_descrs[0 ];
184+
185+ if (given_descrs[1 ] == NULL ) {
186+ loop_descrs[1 ] = (PyArray_Descr *)new_quaddtype_instance (BACKEND_SLEEF);
187+ if (loop_descrs[1 ] == nullptr ) {
188+ return (NPY_CASTING)-1 ;
189+ }
190+ }
191+ else {
192+ Py_INCREF (given_descrs[1 ]);
193+ loop_descrs[1 ] = given_descrs[1 ];
194+ }
195+
196+ return NPY_UNSAFE_CASTING;
197+ }
198+
199+ static int
200+ unicode_to_quad_strided_loop_unaligned (PyArrayMethod_Context *context, char *const data[],
201+ npy_intp const dimensions[], npy_intp const strides[],
202+ void *NPY_UNUSED (auxdata))
203+ {
204+ npy_intp N = dimensions[0 ];
205+ char *in_ptr = data[0 ];
206+ char *out_ptr = data[1 ];
207+ npy_intp in_stride = strides[0 ];
208+ npy_intp out_stride = strides[1 ];
209+
210+ PyArray_Descr *const *descrs = context->descriptors ;
211+ QuadPrecDTypeObject *descr_out = (QuadPrecDTypeObject *)descrs[1 ];
212+ QuadBackendType backend = descr_out->backend ;
213+
214+ // Unicode strings are stored as UCS4 (4 bytes per character)
215+ npy_intp unicode_size_chars = descrs[0 ]->elsize / 4 ;
216+
217+ while (N--) {
218+ // Temporary buffer to convert UCS4 to null-terminated char string
219+ char temp_str[QUAD_STR_WIDTH + 1 ];
220+ npy_intp copy_len = unicode_size_chars < QUAD_STR_WIDTH ? unicode_size_chars : QUAD_STR_WIDTH;
221+ // Convert UCS4 characters to ASCII/char
222+ Py_UCS4 *ucs4_str = (Py_UCS4 *)in_ptr;
223+ npy_intp i;
224+ for (i = 0 ; i < copy_len; i++) {
225+ Py_UCS4 c = ucs4_str[i];
226+
227+ // reject non-ASCII characters
228+ if (c > 127 ) {
229+ PyErr_Format (PyExc_ValueError, " Cannot cast non-ASCII character '%c' to QuadPrecision" , c);
230+ return -1 ;
231+ }
232+
233+ temp_str[i] = (char )c;
234+ }
235+ temp_str[i] = ' \0 ' ;
236+
237+ quad_value out_val;
238+ char *endptr;
239+ int err = cstring_to_quad (temp_str, backend, &out_val, &endptr, true );
240+ if (err < 0 ) {
241+ PyErr_Format (PyExc_ValueError,
242+ " could not convert string to QuadPrecision: np.str_('%s')" , temp_str);
243+ return -1 ;
244+ }
245+
246+ if (backend == BACKEND_SLEEF) {
247+ memcpy (out_ptr, &out_val.sleef_value , sizeof (Sleef_quad));
248+ }
249+ else {
250+ memcpy (out_ptr, &out_val.longdouble_value , sizeof (long double ));
251+ }
252+
253+ in_ptr += in_stride;
254+ out_ptr += out_stride;
255+ }
256+
257+ return 0 ;
258+ }
259+
260+ static int
261+ unicode_to_quad_strided_loop_aligned (PyArrayMethod_Context *context, char *const data[],
262+ npy_intp const dimensions[], npy_intp const strides[],
263+ void *NPY_UNUSED (auxdata))
264+ {
265+ npy_intp N = dimensions[0 ];
266+ char *in_ptr = data[0 ];
267+ char *out_ptr = data[1 ];
268+ npy_intp in_stride = strides[0 ];
269+ npy_intp out_stride = strides[1 ];
270+
271+ PyArray_Descr *const *descrs = context->descriptors ;
272+ QuadPrecDTypeObject *descr_out = (QuadPrecDTypeObject *)descrs[1 ];
273+ QuadBackendType backend = descr_out->backend ;
274+
275+ // Unicode strings are stored as UCS4 (4 bytes per character)
276+ npy_intp unicode_size_chars = descrs[0 ]->elsize / 4 ;
277+
278+ while (N--) {
279+ // Temporary buffer to convert UCS4 to null-terminated char string
280+ char temp_str[QUAD_STR_WIDTH + 1 ];
281+ npy_intp copy_len = unicode_size_chars < QUAD_STR_WIDTH ? unicode_size_chars : QUAD_STR_WIDTH;
282+ // Convert UCS4 characters to ASCII/char
283+ Py_UCS4 *ucs4_str = (Py_UCS4 *)in_ptr;
284+ npy_intp i;
285+ for (i = 0 ; i < copy_len; i++) {
286+ Py_UCS4 c = ucs4_str[i];
287+
288+ // reject non-ASCII characters
289+ if (c > 127 ) {
290+ PyErr_Format (PyExc_ValueError, " Cannot cast non-ASCII character '%c' to QuadPrecision" , c);
291+ return -1 ;
292+ }
293+
294+ temp_str[i] = (char )c;
295+ }
296+ temp_str[i] = ' \0 ' ;
297+
298+ quad_value out_val;
299+ char *endptr;
300+ int err = cstring_to_quad (temp_str, backend, &out_val, &endptr, true );
301+ if (err < 0 ) {
302+ PyErr_Format (PyExc_ValueError,
303+ " could not convert string to QuadPrecision: np.str_('%s')" , temp_str);
304+ return -1 ;
305+ }
306+
307+ if (backend == BACKEND_SLEEF) {
308+ *(Sleef_quad *)out_ptr = out_val.sleef_value ;
309+ }
310+ else {
311+ *(long double *)out_ptr = out_val.longdouble_value ;
312+ }
313+
314+ in_ptr += in_stride;
315+ out_ptr += out_stride;
316+ }
317+
318+ return 0 ;
319+ }
320+
175321
176322// Tag dispatching to ensure npy_bool/npy_ubyte and npy_half/npy_ushort do not alias in templates
177323// see e.g. https://stackoverflow.com/q/32522279
@@ -880,6 +1026,25 @@ init_casts_internal(void)
8801026 add_cast_from<double >(&PyArray_DoubleDType);
8811027 add_cast_from<long double >(&PyArray_LongDoubleDType);
8821028
1029+ // Unicode/String to QuadPrecision cast
1030+ PyArray_DTypeMeta **unicode_dtypes = new PyArray_DTypeMeta *[2 ]{&PyArray_UnicodeDType, &QuadPrecDType};
1031+ PyType_Slot *unicode_slots = new PyType_Slot[4 ]{
1032+ {NPY_METH_resolve_descriptors, (void *)&unicode_to_quad_resolve_descriptors},
1033+ {NPY_METH_strided_loop, (void *)&unicode_to_quad_strided_loop_aligned},
1034+ {NPY_METH_unaligned_strided_loop, (void *)&unicode_to_quad_strided_loop_unaligned},
1035+ {0 , nullptr }};
1036+
1037+ PyArrayMethod_Spec *unicode_spec = new PyArrayMethod_Spec{
1038+ .name = " cast_Unicode_to_QuadPrec" ,
1039+ .nin = 1 ,
1040+ .nout = 1 ,
1041+ .casting = NPY_UNSAFE_CASTING,
1042+ .flags = NPY_METH_SUPPORTS_UNALIGNED,
1043+ .dtypes = unicode_dtypes,
1044+ .slots = unicode_slots,
1045+ };
1046+ add_spec (unicode_spec);
1047+
8831048 specs[spec_count] = nullptr ;
8841049 return specs;
8851050}
0 commit comments