@@ -279,6 +279,92 @@ macro_rules! custom_panic_default {
279279 } ;
280280}
281281
282+ /// This is a more space efficient implementation of custom panic.
283+ /// It is about 1kb in size and depends on the unstable feature `panic_info_message`,
284+ /// which is going to be stabilized in Rust 1.84.
285+ ///
286+ /// It works just like the default custom panic, except that dynamic generated error
287+ /// messages are not shown. For instance, trying to access an index out of bounds of a vector
288+ /// would give us `index Y is out of bounds for length X`. As the length is only know at runtime
289+ /// time, this message is elided.
290+ ///
291+ /// All messages known at compile time are correctly displayed, e.g. `called unwrap in a None`,
292+ /// file names, line and column numbers.
293+ #[ macro_export]
294+ macro_rules! custom_panic_space_efficient {
295+ ( ) => {
296+ #[ cfg( all( not( feature = "custom-panic" ) , target_os = "solana" ) ) ]
297+ #[ no_mangle]
298+ fn custom_panic( info: & core:: panic:: PanicInfo <' _>) {
299+ #[ inline( never) ]
300+ unsafe fn write_num( mut num: u32 , dst: * mut u8 ) -> usize {
301+ let mut buf: [ u8 ; 10 ] = [ 0 ; 10 ] ;
302+ let init_ptr = buf. as_mut_ptr( ) . add( 11 ) ;
303+ let mut write_ptr = init_ptr;
304+ while num > 0 {
305+ write_ptr = write_ptr. sub( 1 ) ;
306+ // SAFETY: The pointer will always be within *buf and *buf+10
307+ * write_ptr = ( num % 10 ) as u8 + 48 ;
308+ num /= 10 ;
309+ }
310+ let len = init_ptr. offset_from( write_ptr) as usize ;
311+ // SAFETY: The copy length will never be greater than 10.
312+ std:: ptr:: copy_nonoverlapping( write_ptr, dst, len) ;
313+ len + 1
314+ }
315+
316+ if let Some ( loc) = info. location( ) {
317+ let filename = loc. file( ) . as_bytes( ) ;
318+ // Array of 10 (line number) + 10 (column number)
319+ // + 6 (line string) + 8 bytes + 16 extra bytes
320+ let mut msg_line: [ u8 ; 50 ] = [ 32 ; 50 ] ;
321+
322+ let dst = msg_line. as_mut_ptr( ) ;
323+ unsafe {
324+ let panic_str = "PANICKED AT:" . as_bytes( ) ;
325+ // SAFETY: Both strings are well formed
326+ solana_program:: syscalls:: sol_log_( panic_str. as_ptr( ) , panic_str. len( ) as u64 ) ;
327+ solana_program:: syscalls:: sol_log_( filename. as_ptr( ) , filename. len( ) as u64 ) ;
328+
329+ let line = "Line: " . as_bytes( ) ; // 6 bytes
330+ let src = line. as_ptr( ) ;
331+ // SAFETY: Destination is larger than the line string
332+ std:: ptr:: copy_nonoverlapping( src, dst, line. len( ) ) ;
333+
334+ let dst = dst. add( line. len( ) ) ;
335+ // SAFETY: write_num will never write more than 10 bytes
336+ let written_bytes = write_num( loc. line( ) , dst) ;
337+ let dst = dst. add( written_bytes) ;
338+ * dst = 32 ;
339+
340+ let dst = dst. add( 1 ) ;
341+ let col = "Column: " . as_bytes( ) ; // 8 bytes
342+ let src = col. as_ptr( ) ;
343+ // SAFETY: Destination is larger than 25 bytes
344+ std:: ptr:: copy_nonoverlapping( src, dst, col. len( ) ) ;
345+
346+ let dst = dst. add( col. len( ) ) ;
347+ // SAFETY: write_num will never write more than 10 bytes
348+ let written_bytes = write_num( loc. column( ) , dst) ;
349+
350+ let line_len = dst. add( written_bytes) . offset_from( msg_line. as_ptr( ) ) as usize ;
351+ // SAFETY: The line length is properly offseted from number of
352+ // written bytes
353+ solana_program:: syscalls:: sol_log_( msg_line. as_ptr( ) , line_len as u64 ) ;
354+ }
355+ }
356+
357+ if let Some ( Some ( mm) ) = info. message( ) . map( |mes| mes. as_str( ) ) {
358+ let mes = mm. as_bytes( ) ;
359+ // SAFETY: We assumed the panic message is well formed.
360+ unsafe {
361+ solana_program:: syscalls:: sol_log_( mes. as_ptr( ) , mes. len( ) as u64 ) ;
362+ }
363+ }
364+ }
365+ } ;
366+ }
367+
282368/// The bump allocator used as the default rust heap when running programs.
283369pub struct BumpAllocator {
284370 pub start : usize ,
0 commit comments