@@ -43,14 +43,120 @@ mod decl {
4343
4444 #[ pyfunction( name = "b2a_hex" ) ]
4545 #[ pyfunction]
46- fn hexlify ( data : ArgBytesLike ) -> Vec < u8 > {
46+ fn hexlify (
47+ data : ArgBytesLike ,
48+ sep : OptionalArg < ArgAsciiBuffer > ,
49+ bytes_per_sep : OptionalArg < isize > ,
50+ vm : & VirtualMachine ,
51+ ) -> PyResult < Vec < u8 > > {
52+ let bytes_per_sep = bytes_per_sep. unwrap_or ( 1 ) ;
53+
4754 data. with_ref ( |bytes| {
48- let mut hex = Vec :: < u8 > :: with_capacity ( bytes. len ( ) * 2 ) ;
49- for b in bytes {
50- hex. push ( hex_nibble ( b >> 4 ) ) ;
51- hex. push ( hex_nibble ( b & 0xf ) ) ;
55+ // Get separator character if provided
56+ let sep_char = if let OptionalArg :: Present ( sep_buf) = sep {
57+ sep_buf. with_ref ( |sep_bytes| {
58+ if sep_bytes. len ( ) != 1 {
59+ return Err ( vm. new_value_error ( "sep must be length 1." . to_owned ( ) ) ) ;
60+ }
61+ let sep_char = sep_bytes[ 0 ] ;
62+ if !sep_char. is_ascii ( ) {
63+ return Err ( vm. new_value_error ( "sep must be ASCII." . to_owned ( ) ) ) ;
64+ }
65+ Ok ( Some ( sep_char) )
66+ } ) ?
67+ } else {
68+ None
69+ } ;
70+
71+ // If no separator or bytes_per_sep is 0, use simple hexlify
72+ if sep_char. is_none ( ) || bytes_per_sep == 0 || bytes. is_empty ( ) {
73+ let mut hex = Vec :: < u8 > :: with_capacity ( bytes. len ( ) * 2 ) ;
74+ for b in bytes {
75+ hex. push ( hex_nibble ( b >> 4 ) ) ;
76+ hex. push ( hex_nibble ( b & 0xf ) ) ;
77+ }
78+ return Ok ( hex) ;
79+ }
80+
81+ let sep_char = sep_char. unwrap ( ) ;
82+ let abs_bytes_per_sep = bytes_per_sep. unsigned_abs ( ) ;
83+
84+ // If separator interval is >= data length, no separators needed
85+ if abs_bytes_per_sep >= bytes. len ( ) {
86+ let mut hex = Vec :: < u8 > :: with_capacity ( bytes. len ( ) * 2 ) ;
87+ for b in bytes {
88+ hex. push ( hex_nibble ( b >> 4 ) ) ;
89+ hex. push ( hex_nibble ( b & 0xf ) ) ;
90+ }
91+ return Ok ( hex) ;
92+ }
93+
94+ // Calculate result length
95+ let num_separators = ( bytes. len ( ) - 1 ) / abs_bytes_per_sep;
96+ let result_len = bytes. len ( ) * 2 + num_separators;
97+ let mut hex = vec ! [ 0u8 ; result_len] ;
98+
99+ if bytes_per_sep < 0 {
100+ // Left-to-right processing (negative bytes_per_sep)
101+ let mut i = 0 ; // input index
102+ let mut j = 0 ; // output index
103+ let chunks = bytes. len ( ) / abs_bytes_per_sep;
104+
105+ // Process complete chunks
106+ for _ in 0 ..chunks {
107+ for _ in 0 ..abs_bytes_per_sep {
108+ let b = bytes[ i] ;
109+ hex[ j] = hex_nibble ( b >> 4 ) ;
110+ hex[ j + 1 ] = hex_nibble ( b & 0xf ) ;
111+ i += 1 ;
112+ j += 2 ;
113+ }
114+ if i < bytes. len ( ) {
115+ hex[ j] = sep_char;
116+ j += 1 ;
117+ }
118+ }
119+
120+ // Process remaining bytes
121+ while i < bytes. len ( ) {
122+ let b = bytes[ i] ;
123+ hex[ j] = hex_nibble ( b >> 4 ) ;
124+ hex[ j + 1 ] = hex_nibble ( b & 0xf ) ;
125+ i += 1 ;
126+ j += 2 ;
127+ }
128+ } else {
129+ // Right-to-left processing (positive bytes_per_sep)
130+ let mut i = bytes. len ( ) as isize - 1 ; // input index
131+ let mut j = result_len as isize - 1 ; // output index
132+ let chunks = bytes. len ( ) / abs_bytes_per_sep;
133+
134+ // Process complete chunks from right
135+ for _ in 0 ..chunks {
136+ for _ in 0 ..abs_bytes_per_sep {
137+ let b = bytes[ i as usize ] ;
138+ hex[ j as usize ] = hex_nibble ( b & 0xf ) ;
139+ hex[ ( j - 1 ) as usize ] = hex_nibble ( b >> 4 ) ;
140+ i -= 1 ;
141+ j -= 2 ;
142+ }
143+ if i >= 0 {
144+ hex[ j as usize ] = sep_char;
145+ j -= 1 ;
146+ }
147+ }
148+
149+ // Process remaining bytes
150+ while i >= 0 {
151+ let b = bytes[ i as usize ] ;
152+ hex[ j as usize ] = hex_nibble ( b & 0xf ) ;
153+ hex[ ( j - 1 ) as usize ] = hex_nibble ( b >> 4 ) ;
154+ i -= 1 ;
155+ j -= 2 ;
156+ }
52157 }
53- hex
158+
159+ Ok ( hex)
54160 } )
55161 }
56162
@@ -368,7 +474,7 @@ mod decl {
368474 #[ derive( FromArgs ) ]
369475 struct B2aQpArgs {
370476 #[ pyarg( any) ]
371- data : ArgAsciiBuffer ,
477+ data : ArgBytesLike ,
372478 #[ pyarg( named, default = false ) ]
373479 quotetabs : bool ,
374480 #[ pyarg( named, default = true ) ]
0 commit comments