@@ -598,13 +598,16 @@ static int decode_slow(const uint8_t *src, size_t srclen, uint8_t* out, size_t*
598598
599599static PyObject * pybase64_encode_impl_core (PyObject * self , Py_buffer const * buffer , char const * alphabet , Py_ssize_t wrapcol , unsigned int flags )
600600{
601+ size_t groups ;
602+ size_t groups_remainder ;
601603 size_t out_len ;
602604 PyObject * out_object ;
603605#if PY_VERSION_HEX >= 0x030f0000
604606 PyBytesWriter * writer ;
605607#endif
606608 char * dst_start ;
607609 char * dst ;
610+ struct base64_state b64_state ;
608611
609612 if (wrapcol < 0 ) {
610613 PyErr_SetString (PyExc_ValueError , "wrapcol must be >= 0" );
@@ -619,7 +622,20 @@ static PyObject* pybase64_encode_impl_core(PyObject* self, Py_buffer const* buff
619622 return PyErr_NoMemory (); /* GCOVR_EXCL_LINE */
620623 }
621624
622- out_len = (size_t )(((buffer -> len + 2 ) / 3 ) * 4 );
625+ groups = (size_t )(buffer -> len / 3 );
626+ groups_remainder = (size_t )buffer -> len - groups * 3U ;
627+ out_len = groups * 4 ;
628+ switch (groups_remainder )
629+ {
630+ case 1 :
631+ out_len += (flags & PYBASE64_FLAGS_NO_PADDING ) ? 2U : 4U ;
632+ break ;
633+ case 2 :
634+ out_len += (flags & PYBASE64_FLAGS_NO_PADDING ) ? 3U : 4U ;
635+ break ;
636+ default :
637+ break ;
638+ }
623639 if (wrapcol > 0 && out_len > 0 ) {
624640 size_t newlines = (out_len - 1U ) / (size_t )wrapcol ;
625641 if (newlines > ((size_t )PY_SSIZE_T_MAX - out_len )) { /* GCOVR_EXCL_BR_WITHOUT_HIT: 1/2 */
@@ -674,6 +690,10 @@ static PyObject* pybase64_encode_impl_core(PyObject* self, Py_buffer const* buff
674690 /* not interacting with Python objects from here, release the GIL */
675691 Py_BEGIN_ALLOW_THREADS
676692
693+ int const b64_flags = ((flags & PYBASE64_FLAGS_NO_PADDING ) ? BASE64_NO_PADDING : 0 );
694+
695+ base64_stream_encode_init (& b64_state , b64_flags );
696+
677697 if (flags & PYBASE64_FLAGS_APPEND_NEW_LINE ) {
678698 out_len -- ; /* only consider len without new line terminator */
679699 }
@@ -683,13 +703,14 @@ static PyObject* pybase64_encode_impl_core(PyObject* self, Py_buffer const* buff
683703 const Py_ssize_t src_slice = (Py_ssize_t )((dst_slice / 4U ) * 3U );
684704 Py_ssize_t len = buffer -> len ;
685705 const char * src = (const char * )buffer -> buf ;
686- size_t remainder ;
687706
688707 if (alphabet ) {
708+ size_t remainder ;
709+
689710 while (out_len > dst_slice ) {
690711 size_t dst_len = (size_t )wrapcol ;
691712
692- base64_encode ( src , src_slice , dst , & dst_len , 0 );
713+ base64_stream_encode ( & b64_state , src , src_slice , dst , & dst_len );
693714 translate_inplace (dst , dst_len , alphabet );
694715 dst [dst_len ] = '\n' ;
695716
@@ -698,25 +719,28 @@ static PyObject* pybase64_encode_impl_core(PyObject* self, Py_buffer const* buff
698719 out_len -= dst_slice ;
699720 dst += dst_slice ;
700721 }
722+ base64_stream_encode (& b64_state , src , len , dst , & out_len );
701723 remainder = out_len ;
702- base64_encode (src , len , dst , & remainder , 0 );
724+ base64_stream_encode_final (& b64_state , dst + out_len , & out_len );
725+ remainder += out_len ;
703726 translate_inplace (dst , remainder , alphabet );
704727 dst += remainder ;
705728 }
706729 else {
707730 while (out_len > dst_slice ) {
708731 size_t dst_len = (size_t )wrapcol ;
709- base64_encode ( src , src_slice , dst , & dst_len , 0 );
732+ base64_stream_encode ( & b64_state , src , src_slice , dst , & dst_len );
710733 dst [dst_len ] = '\n' ;
711734
712735 len -= src_slice ;
713736 src += src_slice ;
714737 out_len -= dst_slice ;
715738 dst += dst_slice ;
716739 }
717- remainder = out_len ;
718- base64_encode (src , len , dst , & remainder , 0 );
719- dst += remainder ;
740+ base64_stream_encode (& b64_state , src , len , dst , & out_len );
741+ dst += out_len ;
742+ base64_stream_encode_final (& b64_state , dst , & out_len );
743+ dst += out_len ;
720744 }
721745 }
722746 else if (alphabet ) {
@@ -730,32 +754,26 @@ static PyObject* pybase64_encode_impl_core(PyObject* self, Py_buffer const* buff
730754 while (out_len > dst_slice ) {
731755 size_t dst_len = dst_slice ;
732756
733- base64_encode ( src , src_slice , dst , & dst_len , 0 );
757+ base64_stream_encode ( & b64_state , src , src_slice , dst , & dst_len );
734758 translate_inplace (dst , dst_slice , alphabet );
735759
736760 len -= src_slice ;
737761 src += src_slice ;
738762 out_len -= dst_slice ;
739763 dst += dst_slice ;
740764 }
765+ base64_stream_encode (& b64_state , src , len , dst , & out_len );
741766 remainder = out_len ;
742- base64_encode (src , len , dst , & out_len , 0 );
767+ base64_stream_encode_final (& b64_state , dst + out_len , & out_len );
768+ remainder += out_len ;
743769 translate_inplace (dst , remainder , alphabet );
744770 dst += remainder ;
745771 }
746772 else {
747- base64_encode (buffer -> buf , buffer -> len , dst , & out_len , 0 );
773+ base64_stream_encode (& b64_state , buffer -> buf , buffer -> len , dst , & out_len );
774+ dst += out_len ;
775+ base64_stream_encode_final (& b64_state , dst , & out_len );
748776 dst += out_len ;
749- }
750- if (flags & PYBASE64_FLAGS_NO_PADDING ) {
751- /* we have at least 4 bytes, at most 2 '=' */
752- assert ((dst - dst_start ) >= 4 );
753- if (dst [-1 ] == '=' ) {
754- dst -= 1 ;
755- }
756- if (dst [-1 ] == '=' ) {
757- dst -= 1 ;
758- }
759777 }
760778 if (flags & PYBASE64_FLAGS_APPEND_NEW_LINE ) {
761779 * dst ++ = '\n' ;
@@ -764,29 +782,6 @@ static PyObject* pybase64_encode_impl_core(PyObject* self, Py_buffer const* buff
764782 /* restore the GIL */
765783 Py_END_ALLOW_THREADS
766784
767- if (flags & PYBASE64_FLAGS_NO_PADDING )
768- {
769- if (flags & PYBASE64_FLAGS_ENCODE_AS_STRING ) {
770- #if defined(PYPY_VERSION ) || defined(GRAALVM_PYTHON )
771- /* SystemError: PyUnicode_Resize called on already created string... */
772- /* we'll be less efficient */
773- PyObject * temp_object = PyUnicode_FromKindAndData (PyUnicode_1BYTE_KIND , dst_start , dst - dst_start );
774- Py_DECREF (out_object );
775- out_object = temp_object ;
776- #else
777- if (PyUnicode_Resize (& out_object , dst - dst_start ) != 0 ) { /* GCOVR_EXCL_BR_WITHOUT_HIT: 1/2 */
778- Py_DECREF (out_object ); /* GCOVR_EXCL_LINE */
779- return NULL ; /* GCOVR_EXCL_LINE */
780- }
781- #endif
782- }
783- else {
784- #if PY_VERSION_HEX < 0x030f0000
785- _PyBytes_Resize (& out_object , dst - dst_start );
786- #endif
787- }
788- }
789-
790785#if PY_VERSION_HEX >= 0x030f0000
791786 if (!(flags & PYBASE64_FLAGS_ENCODE_AS_STRING )) {
792787 out_object = PyBytesWriter_FinishWithPointer (writer , dst );
0 commit comments