@@ -652,11 +652,49 @@ where
652652 }
653653}
654654
655+ // CPython 3.12+ PyLongObject layout: `PyObject_HEAD; uintptr_t lv_tag; digit ob_digit[]`.
656+ // `lv_tag` packs the sign in the low 3 bits (0=positive, 1=zero, 2=negative) and the
657+ // digit count in the upper bits. Default builds use 30-bit digits (uint32_t).
658+ #[ cfg( all( CPython , Py_3_12 ) ) ]
659+ #[ inline]
660+ unsafe fn pylong_to_dag_int_fast ( obj : * mut ffi:: PyObject ) -> Option < ( u64 , bool ) > {
661+ const NON_SIZE_BITS : u32 = 3 ;
662+ const SIGN_MASK : usize = 3 ;
663+ const SIGN_NEGATIVE : usize = 2 ;
664+ const PYLONG_DIGIT_BITS : u32 = 30 ;
665+
666+ let lv_tag_ptr = ( obj as * const u8 ) . add ( std:: mem:: size_of :: < ffi:: PyObject > ( ) ) as * const usize ;
667+ let lv_tag = * lv_tag_ptr;
668+ let ndigits = lv_tag >> NON_SIZE_BITS ;
669+ let neg = ( lv_tag & SIGN_MASK ) == SIGN_NEGATIVE ;
670+
671+ let ob_digit = lv_tag_ptr. add ( 1 ) as * const u32 ;
672+ let abs_val: u64 = match ndigits {
673+ 0 => return Some ( ( 0 , false ) ) ,
674+ 1 => * ob_digit as u64 ,
675+ 2 => ( * ob_digit as u64 ) | ( ( * ob_digit. add ( 1 ) as u64 ) << PYLONG_DIGIT_BITS ) ,
676+ _ => return None ,
677+ } ;
678+ Some ( ( abs_val, neg) )
679+ }
680+
655681#[ inline]
656682fn encode_int < W : enc:: Write > ( obj : & Bound < ' _ , PyAny > , w : & mut W ) -> Result < ( ) >
657683where
658684 W :: Error : Send + Sync ,
659685{
686+ #[ cfg( all( CPython , Py_3_12 ) ) ]
687+ {
688+ if let Some ( ( abs_val, neg) ) = unsafe { pylong_to_dag_int_fast ( obj. as_ptr ( ) ) } {
689+ if neg {
690+ types:: Negative ( abs_val - 1 ) . encode ( w) ?;
691+ } else {
692+ abs_val. encode ( w) ?;
693+ }
694+ return Ok ( ( ) ) ;
695+ }
696+ }
697+
660698 let i: i128 = obj. extract ( ) ?;
661699 if i. is_negative ( ) {
662700 if -( i + 1 ) > u64:: MAX as i128 {
0 commit comments