1+ #include "py/runtime.h"
2+ #include "py/builtin.h"
3+ #include "py/obj.h"
4+ #include "py/objstr.h"
5+ #include <string.h>
6+ #include "py/mperrno.h"
7+
8+ #define BASE32_MASK 0x1F
9+
10+ static const char B32CHARS [] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567" ;
11+ static int8_t base32_index [256 ];
12+
13+ static void init_base32_index (void ) {
14+ memset (base32_index , -1 , sizeof (base32_index ));
15+ for (int i = 0 ; i < 32 ; i ++ ) {
16+ char c = B32CHARS [i ];
17+ base32_index [(unsigned char )c ] = i ;
18+ }
19+ }
20+
21+ STATIC mp_obj_t base32_decode (mp_obj_t encoded_str_obj ) {
22+ size_t encoded_len ;
23+ const char * encoded_str = mp_obj_str_get_data (encoded_str_obj , & encoded_len );
24+
25+ // Strip padding
26+ size_t stripped_len = encoded_len ;
27+ while (stripped_len > 0 && encoded_str [stripped_len - 1 ] == '=' ) {
28+ stripped_len -- ;
29+ }
30+
31+ static bool initialized = false;
32+ if (!initialized ) {
33+ init_base32_index ();
34+ initialized = true;
35+ }
36+
37+ size_t max_decoded_size = (stripped_len * 5 + 7 ) / 8 ;
38+ uint8_t * decoded_buf = m_new0 (uint8_t , max_decoded_size );
39+
40+ if (decoded_buf == NULL ) {
41+ mp_raise_OSError (MP_ENOMEM );
42+ }
43+
44+ size_t decoded_len = 0 ;
45+ uint32_t buffer = 0 ;
46+ int bits_left = 0 ;
47+
48+ for (size_t i = 0 ; i < stripped_len ; i ++ ) {
49+ unsigned char c = encoded_str [i ];
50+ int8_t index = base32_index [c ];
51+ if (index == -1 ) {
52+ // Clean up allocated memory before raising exception
53+ m_del (uint8_t , decoded_buf , max_decoded_size );
54+ mp_raise_ValueError ("Invalid Base32 character" );
55+ }
56+
57+ buffer = (buffer << 5 ) | index ;
58+ bits_left += 5 ;
59+
60+ while (bits_left >= 8 ) {
61+ bits_left -= 8 ;
62+ decoded_buf [decoded_len ++ ] = (buffer >> bits_left ) & 0xFF ;
63+ buffer &= (1 << bits_left ) - 1 ;
64+ }
65+ }
66+
67+ mp_obj_t result = mp_obj_new_bytes (decoded_buf , decoded_len );
68+
69+ // Clean up allocated memory
70+ m_del (uint8_t , decoded_buf , max_decoded_size );
71+
72+ return result ;
73+ }
74+ STATIC MP_DEFINE_CONST_FUN_OBJ_1 (base32_decode_obj , base32_decode );
75+
76+ STATIC mp_obj_t base32_encode (mp_obj_t data_obj , mp_obj_t add_padding_obj ) {
77+ mp_buffer_info_t data_buf ;
78+ mp_get_buffer_raise (data_obj , & data_buf , MP_BUFFER_READ );
79+ uint8_t * data = data_buf .buf ;
80+ size_t data_len = data_buf .len ;
81+
82+ bool add_padding = mp_obj_is_true (add_padding_obj );
83+
84+ uint32_t buffer = 0 ;
85+ int bits_left = 0 ;
86+
87+ size_t base_encoded_size = (data_len * 8 + 4 ) / 5 ;
88+ size_t max_encoded_size ;
89+
90+ if (add_padding ) {
91+ // Round up to nearest multiple of 8 for padding
92+ max_encoded_size = ((base_encoded_size + 7 ) / 8 ) * 8 ;
93+ } else {
94+ max_encoded_size = base_encoded_size ;
95+ }
96+
97+ char * encoded_buf = m_new0 (char , max_encoded_size + 1 );
98+
99+ if (encoded_buf == NULL ) {
100+ mp_raise_OSError (MP_ENOMEM );
101+ }
102+
103+ size_t encoded_len = 0 ;
104+
105+ for (size_t i = 0 ; i < data_len ; i ++ ) {
106+ buffer = (buffer << 8 ) | data [i ];
107+ bits_left += 8 ;
108+
109+ while (bits_left >= 5 ) {
110+ bits_left -= 5 ;
111+ uint8_t index = (buffer >> bits_left ) & BASE32_MASK ;
112+ encoded_buf [encoded_len ++ ] = B32CHARS [index ];
113+ buffer &= (1 << bits_left ) - 1 ;
114+ }
115+ }
116+
117+ if (bits_left > 0 ) {
118+ buffer <<= (5 - bits_left );
119+ encoded_buf [encoded_len ++ ] = B32CHARS [buffer & BASE32_MASK ];
120+ }
121+
122+ if (add_padding ) {
123+ size_t padding_length = (8 - (encoded_len % 8 )) % 8 ;
124+ for (size_t i = 0 ; i < padding_length ; i ++ ) {
125+ encoded_buf [encoded_len ++ ] = '=' ;
126+ }
127+ }
128+
129+ mp_obj_t result = mp_obj_new_str (encoded_buf , encoded_len );
130+
131+ // Clean up allocated memory
132+ m_del (char , encoded_buf , max_encoded_size + 1 );
133+
134+ return result ;
135+ }
136+ STATIC MP_DEFINE_CONST_FUN_OBJ_2 (base32_encode_obj , base32_encode );
137+
138+ STATIC const mp_rom_map_elem_t base32_module_globals_table [] = {
139+ { MP_ROM_QSTR (MP_QSTR___name__ ), MP_ROM_QSTR (MP_QSTR_base32 ) },
140+ { MP_ROM_QSTR (MP_QSTR_decode ), MP_ROM_PTR (& base32_decode_obj ) },
141+ { MP_ROM_QSTR (MP_QSTR_encode ), MP_ROM_PTR (& base32_encode_obj ) },
142+ };
143+ STATIC MP_DEFINE_CONST_DICT (base32_module_globals , base32_module_globals_table );
144+
145+ const mp_obj_module_t base32_user_cmodule = {
146+ .base = { & mp_type_module },
147+ .globals = (mp_obj_dict_t * )& base32_module_globals ,
148+ };
149+
150+ MP_REGISTER_MODULE (MP_QSTR_base32 , base32_user_cmodule , MODULE_BASE32_ENABLED );
0 commit comments