1- /// `concat!` but working with constants
2- #[ macro_export]
3- #[ doc( hidden) ]
4- macro_rules! const_concat {
5- ( $e: expr) => { {
6- $e
7- } } ;
8- ( $l: expr, $( $r: expr) ,+ $( , ) ?) => { {
9- const PIECES : & [ & str ] = & [ $l, $( $r) ,+] ;
10- const RAW_BYTES : & [ u8 ] = & $crate:: impl_:: concat:: combine_to_array:: <{
11- $crate:: impl_:: concat:: combined_len( PIECES )
12- } >( PIECES ) ;
13- // Safety: `RAW_BYTES` is combined from valid &str pieces
14- unsafe { :: std:: str :: from_utf8_unchecked( RAW_BYTES ) }
15- } }
16- }
17-
18- pub use const_concat;
19-
20- /// Calculates the total byte length of all string pieces in the array.
21- ///
22- /// This is a useful utility in order to determine the size needed for the constant
23- /// `combine` function.
24- pub const fn combined_len ( pieces : & [ & str ] ) -> usize {
25- let mut len = 0 ;
26- let mut pieces_idx = 0 ;
27- while pieces_idx < pieces. len ( ) {
28- len += pieces[ pieces_idx] . len ( ) ;
29- pieces_idx += 1 ;
30- }
31- len
32- }
33-
34- /// Combines all string pieces into a single byte array.
35- ///
36- /// `out` should be a buffer at the correct size of `combined_len(pieces)`, else this will panic.
37- #[ cfg( mut_ref_in_const_fn) ] // requires MSRV 1.83
38- #[ allow( clippy:: incompatible_msrv) ] // `split_at_mut` also requires MSRV 1.83
39- pub const fn combine ( pieces : & [ & str ] , mut out : & mut [ u8 ] ) {
40- let mut pieces_idx = 0 ;
41- while pieces_idx < pieces. len ( ) {
42- let piece = pieces[ pieces_idx] . as_bytes ( ) ;
43- slice_copy_from_slice ( out, piece) ;
44- // using split_at_mut because range indexing not yet supported in const fn
45- out = out. split_at_mut ( piece. len ( ) ) . 1 ;
46- pieces_idx += 1 ;
47- }
48- // should be no trailing buffer
49- assert ! ( out. is_empty( ) , "output buffer too large" ) ;
50- }
51-
52- /// Wrapper around combine which has a const generic parameter, this is going to be more codegen
53- /// at compile time (?)
54- ///
55- /// Unfortunately the `&mut [u8]` buffer needs MSRV 1.83
56- pub const fn combine_to_array < const LEN : usize > ( pieces : & [ & str ] ) -> [ u8 ; LEN ] {
57- let mut out: [ u8 ; LEN ] = [ 0u8 ; LEN ] ;
58- #[ cfg( mut_ref_in_const_fn) ]
59- combine ( pieces, & mut out) ;
60- #[ cfg( not( mut_ref_in_const_fn) ) ] // inlined here for higher code
61- {
62- let mut out_idx = 0 ;
63- let mut pieces_idx = 0 ;
64- while pieces_idx < pieces. len ( ) {
65- let piece = pieces[ pieces_idx] . as_bytes ( ) ;
66- let mut piece_idx = 0 ;
67- while piece_idx < piece. len ( ) {
68- out[ out_idx] = piece[ piece_idx] ;
69- out_idx += 1 ;
70- piece_idx += 1 ;
71- }
72- pieces_idx += 1 ;
73- }
74- assert ! ( out_idx == out. len( ) , "output buffer too large" ) ;
75- }
76- out
77- }
78-
791/// Calculates the total byte length of all byte pieces in the array.
802///
813/// This is a useful utility in order to determine the size needed for the constant
82- /// `combine_bytes ` function.
83- pub const fn combined_len_bytes ( pieces : & [ & [ u8 ] ] ) -> usize {
4+ /// `combine ` function.
5+ pub const fn combined_len ( pieces : & [ & [ u8 ] ] ) -> usize {
846 let mut len = 0 ;
857 let mut pieces_idx = 0 ;
868 while pieces_idx < pieces. len ( ) {
@@ -95,7 +17,7 @@ pub const fn combined_len_bytes(pieces: &[&[u8]]) -> usize {
9517/// `out` should be a buffer at the correct size of `combined_len(pieces)`, else this will panic.
9618#[ cfg( mut_ref_in_const_fn) ] // requires MSRV 1.83
9719#[ allow( clippy:: incompatible_msrv) ] // `split_at_mut` also requires MSRV 1.83
98- pub const fn combine_bytes ( pieces : & [ & [ u8 ] ] , mut out : & mut [ u8 ] ) {
20+ const fn combine ( pieces : & [ & [ u8 ] ] , mut out : & mut [ u8 ] ) {
9921 let mut pieces_idx = 0 ;
10022 while pieces_idx < pieces. len ( ) {
10123 let piece = pieces[ pieces_idx] ;
@@ -108,14 +30,14 @@ pub const fn combine_bytes(pieces: &[&[u8]], mut out: &mut [u8]) {
10830 assert ! ( out. is_empty( ) , "output buffer too large" ) ;
10931}
11032
111- /// Wrapper around `combine_bytes ` which has a const generic parameter, this is going to be more codegen
33+ /// Wrapper around `combine ` which has a const generic parameter, this is going to be more codegen
11234/// at compile time (?)
11335///
11436/// Unfortunately the `&mut [u8]` buffer needs MSRV 1.83
115- pub const fn combine_bytes_to_array < const LEN : usize > ( pieces : & [ & [ u8 ] ] ) -> [ u8 ; LEN ] {
37+ pub const fn combine_to_array < const LEN : usize > ( pieces : & [ & [ u8 ] ] ) -> [ u8 ; LEN ] {
11638 let mut out: [ u8 ; LEN ] = [ 0u8 ; LEN ] ;
11739 #[ cfg( mut_ref_in_const_fn) ]
118- combine_bytes ( pieces, & mut out) ;
40+ combine ( pieces, & mut out) ;
11941 #[ cfg( not( mut_ref_in_const_fn) ) ] // inlined here for higher code
12042 {
12143 let mut out_idx = 0 ;
@@ -151,71 +73,32 @@ mod tests {
15173
15274 #[ test]
15375 fn test_combined_len ( ) {
154- let pieces = [ "foo" , "bar" , "baz" ] ;
76+ let pieces: [ & [ u8 ] ; 3 ] = [ b "foo", b "bar", b "baz"] ;
15577 assert_eq ! ( combined_len( & pieces) , 9 ) ;
156- let empty: [ & str ; 0 ] = [ ] ;
78+ let empty: [ & [ u8 ] ; 0 ] = [ ] ;
15779 assert_eq ! ( combined_len( & empty) , 0 ) ;
15880 }
15981
16082 #[ test]
16183 fn test_combine_to_array ( ) {
162- let pieces = [ "foo" , "bar" ] ;
163- let combined = combine_to_array :: < 6 > ( & pieces) ;
164- assert_eq ! ( & combined, b"foobar" ) ;
165- }
166-
167- #[ test]
168- fn test_const_concat_macro ( ) {
169- const RESULT : & str = const_concat ! ( "foo" , "bar" , "baz" ) ;
170- assert_eq ! ( RESULT , "foobarbaz" ) ;
171- const SINGLE : & str = const_concat ! ( "abc" ) ;
172- assert_eq ! ( SINGLE , "abc" ) ;
173- }
174-
175- #[ test]
176- fn test_combined_len_bytes ( ) {
177- let pieces: [ & [ u8 ] ; 3 ] = [ b"foo" , b"bar" , b"baz" ] ;
178- assert_eq ! ( combined_len_bytes( & pieces) , 9 ) ;
179- let empty: [ & [ u8 ] ; 0 ] = [ ] ;
180- assert_eq ! ( combined_len_bytes( & empty) , 0 ) ;
181- }
182-
183- #[ test]
184- fn test_combine_bytes_to_array ( ) {
18584 let pieces: [ & [ u8 ] ; 2 ] = [ b"foo" , b"bar" ] ;
186- let combined = combine_bytes_to_array :: < 6 > ( & pieces) ;
85+ let combined = combine_to_array :: < 6 > ( & pieces) ;
18786 assert_eq ! ( & combined, b"foobar" ) ;
18887 }
18988
19089 #[ test]
19190 #[ should_panic( expected = "index out of bounds" ) ]
19291 fn test_combine_to_array_buffer_too_small ( ) {
193- let pieces = [ "foo" , "bar" ] ;
92+ let pieces: [ & [ u8 ] ; 2 ] = [ b "foo", b "bar"] ;
19493 // Intentionally wrong length
19594 let _ = combine_to_array :: < 5 > ( & pieces) ;
19695 }
19796
19897 #[ test]
19998 #[ should_panic( expected = "output buffer too large" ) ]
20099 fn test_combine_to_array_buffer_too_big ( ) {
201- let pieces = [ "foo" , "bar" ] ;
202- // Intentionally wrong length
203- let _ = combine_to_array :: < 10 > ( & pieces) ;
204- }
205-
206- #[ test]
207- #[ should_panic( expected = "index out of bounds" ) ]
208- fn test_combine_bytes_to_array_buffer_too_small ( ) {
209- let pieces: [ & [ u8 ] ; 2 ] = [ b"foo" , b"bar" ] ;
210- // Intentionally wrong length
211- let _ = combine_bytes_to_array :: < 5 > ( & pieces) ;
212- }
213-
214- #[ test]
215- #[ should_panic( expected = "output buffer too large" ) ]
216- fn test_combine_bytes_to_array_buffer_too_big ( ) {
217100 let pieces: [ & [ u8 ] ; 2 ] = [ b"foo" , b"bar" ] ;
218101 // Intentionally wrong length
219- let _ = combine_bytes_to_array :: < 10 > ( & pieces) ;
102+ let _ = combine_to_array :: < 10 > ( & pieces) ;
220103 }
221104}
0 commit comments