@@ -620,68 +620,130 @@ QuadPrecision_as_integer_ratio(QuadPrecisionObject *self, PyObject *Py_UNUSED(ig
620620 return PyTuple_Pack (2 , numerator , denominator );
621621}
622622
623- static PyObject *
624- QuadPrecision_reduce ( QuadPrecisionObject * self , PyObject * Py_UNUSED ( ignored ) )
623+ static int
624+ quad_host_is_big_endian ( void )
625625{
626- Dragon4_Options opt = {.scientific = 1 ,
627- .digit_mode = DigitMode_Unique ,
628- .cutoff_mode = CutoffMode_TotalLength ,
629- .precision = SLEEF_QUAD_DECIMAL_DIG ,
630- .sign = 1 ,
631- .trim_mode = TrimMode_LeaveOneZero ,
632- .digits_left = 1 ,
633- .exp_digits = 4 };
626+ uint16_t probe = 1 ;
627+ return ((const unsigned char * )& probe )[0 ] == 0 ;
628+ }
634629
635- PyObject * str_value ;
636- if (self -> backend == BACKEND_SLEEF ) {
637- str_value = Dragon4_Scientific_QuadDType (& self -> value .sleef_value , opt .digit_mode ,
638- opt .precision , opt .min_digits , opt .sign ,
639- opt .trim_mode , opt .digits_left , opt .exp_digits );
630+
631+ static void
632+ quad_copy_canonical (unsigned char * dst , const unsigned char * src , size_t n )
633+ {
634+ if (quad_host_is_big_endian ()) {
635+ for (size_t i = 0 ; i < n ; i ++ ) {
636+ dst [i ] = src [n - 1 - i ];
637+ }
640638 }
641639 else {
642- char buffer [128 ];
643- int written = snprintf (buffer , sizeof (buffer ), "%.*Le" ,
644- LDBL_DECIMAL_DIG - 1 , self -> value .longdouble_value );
645- if (written < 0 || written >= (int )sizeof (buffer )) {
646- PyErr_SetString (PyExc_RuntimeError ,
647- "Failed to format long double for pickle" );
648- return NULL ;
649- }
650- str_value = PyUnicode_FromString (buffer );
640+ memcpy (dst , src , n );
651641 }
652- if (str_value == NULL ) {
642+ }
643+
644+
645+ static PyObject *
646+ QuadPrecision_reduce (QuadPrecisionObject * self , PyObject * Py_UNUSED (ignored ))
647+ {
648+ size_t nbytes = (self -> backend == BACKEND_SLEEF )
649+ ? sizeof (self -> value .sleef_value )
650+ : sizeof (self -> value .longdouble_value );
651+ const unsigned char * src = (self -> backend == BACKEND_SLEEF )
652+ ? (const unsigned char * )& self -> value .sleef_value
653+ : (const unsigned char * )& self -> value .longdouble_value ;
654+
655+ unsigned char raw [sizeof (quad_value )];
656+ quad_copy_canonical (raw , src , nbytes );
657+
658+ PyObject * data = PyBytes_FromStringAndSize ((const char * )raw , (Py_ssize_t )nbytes );
659+ if (data == NULL ) {
653660 return NULL ;
654661 }
655662
656663 PyObject * backend_obj = PyUnicode_FromString (
657664 self -> backend == BACKEND_SLEEF ? "sleef" : "longdouble" );
658665 if (backend_obj == NULL ) {
659- Py_DECREF (str_value );
666+ Py_DECREF (data );
660667 return NULL ;
661668 }
662669
663- PyObject * args = PyTuple_Pack (2 , str_value , backend_obj );
664- Py_DECREF (str_value );
670+ PyObject * module = PyImport_ImportModule ("numpy_quaddtype._quaddtype_main" );
671+ if (module == NULL ) {
672+ Py_DECREF (data );
673+ Py_DECREF (backend_obj );
674+ return NULL ;
675+ }
676+ PyObject * reconstruct = PyObject_GetAttrString (module , "from_raw_bytes" );
677+ Py_DECREF (module );
678+ if (reconstruct == NULL ) {
679+ Py_DECREF (data );
680+ Py_DECREF (backend_obj );
681+ return NULL ;
682+ }
683+
684+ PyObject * args = PyTuple_Pack (2 , data , backend_obj );
685+ Py_DECREF (data );
665686 Py_DECREF (backend_obj );
666687 if (args == NULL ) {
688+ Py_DECREF (reconstruct );
667689 return NULL ;
668690 }
669691
670- PyObject * type_obj = (PyObject * )Py_TYPE (self );
671- Py_INCREF (type_obj );
672- PyObject * result = PyTuple_Pack (2 , type_obj , args );
673- Py_DECREF (type_obj );
692+ PyObject * result = PyTuple_Pack (2 , reconstruct , args );
693+ Py_DECREF (reconstruct );
674694 Py_DECREF (args );
675695 return result ;
676696}
677697
698+ PyObject *
699+ QuadPrecision_from_raw_bytes (PyObject * Py_UNUSED (module ), PyObject * args )
700+ {
701+ Py_buffer view ;
702+ const char * backend_str = "sleef" ;
703+ if (!PyArg_ParseTuple (args , "y*|s" , & view , & backend_str )) {
704+ return NULL ;
705+ }
706+
707+ QuadBackendType backend = BACKEND_SLEEF ;
708+ if (strcmp (backend_str , "longdouble" ) == 0 ) {
709+ backend = BACKEND_LONGDOUBLE ;
710+ }
711+ else if (strcmp (backend_str , "sleef" ) != 0 ) {
712+ PyBuffer_Release (& view );
713+ PyErr_SetString (PyExc_ValueError , "Invalid backend. Use 'sleef' or 'longdouble'." );
714+ return NULL ;
715+ }
716+
717+ size_t expected = (backend == BACKEND_SLEEF ) ? sizeof (Sleef_quad ) : sizeof (long double );
718+ if (view .len != (Py_ssize_t )expected ) {
719+ PyErr_Format (PyExc_ValueError ,
720+ "QuadPrecision.from_raw_bytes expected %zu bytes for the '%s' "
721+ "backend, got %zd" ,
722+ expected , backend_str , view .len );
723+ PyBuffer_Release (& view );
724+ return NULL ;
725+ }
726+
727+ QuadPrecisionObject * self = QuadPrecision_raw_new (backend );
728+ if (self == NULL ) {
729+ PyBuffer_Release (& view );
730+ return NULL ;
731+ }
732+ unsigned char * dst = (backend == BACKEND_SLEEF )
733+ ? (unsigned char * )& self -> value .sleef_value
734+ : (unsigned char * )& self -> value .longdouble_value ;
735+ quad_copy_canonical (dst , (const unsigned char * )view .buf , expected );
736+ PyBuffer_Release (& view );
737+ return (PyObject * )self ;
738+ }
739+
678740static PyMethodDef QuadPrecision_methods [] = {
679741 {"is_integer" , (PyCFunction )QuadPrecision_is_integer , METH_NOARGS ,
680742 "Return True if the value is an integer." },
681743 {"as_integer_ratio" , (PyCFunction )QuadPrecision_as_integer_ratio , METH_NOARGS ,
682744 "Return a pair of integers whose ratio is exactly equal to the original value." },
683745 {"__reduce__" , (PyCFunction )QuadPrecision_reduce , METH_NOARGS ,
684- "Support pickling: return (QuadPrecision , (str_value , backend))." },
746+ "Support pickling: return (from_raw_bytes , (raw_bytes , backend))." },
685747 {NULL , NULL , 0 , NULL } /* Sentinel */
686748};
687749
0 commit comments