@@ -45,6 +45,81 @@ use crate::test_utils::cairo_runner::{
4545
4646const COMPRESSION_MODULE_PATH : & str = "starkware.starknet.core.os.data_availability.compression" ;
4747
48+ /// Runs the OS unpack_felts function and returns the unpacked felts + resources used.
49+ fn cairo_unpack_felts (
50+ compressed : & [ Felt ] ,
51+ elm_bound : u128 ,
52+ n_elms : usize ,
53+ ) -> ( Vec < Felt > , ExecutionResources ) {
54+ let runner_config = EntryPointRunnerConfig {
55+ layout : LayoutName :: starknet,
56+ add_main_prefix_to_entrypoint : false ,
57+ ..Default :: default ( )
58+ } ;
59+ let mut implicit_args = vec ! [ ImplicitArg :: Builtin ( BuiltinName :: range_check) ] ;
60+ let ( mut runner, program, entrypoint) = initialize_cairo_runner (
61+ & runner_config,
62+ OS_PROGRAM_BYTES ,
63+ & format ! ( "{COMPRESSION_MODULE_PATH}.unpack_felts" ) ,
64+ & implicit_args,
65+ HashMap :: new ( ) ,
66+ )
67+ . unwrap ( ) ;
68+
69+ // Function accepts start pointer of the compressed data, and destination pointer of the
70+ // unpacked felts, as implicit arguments.
71+ let compressed_start = runner
72+ . vm
73+ . gen_arg ( & compressed. iter ( ) . map ( |x| MaybeRelocatable :: Int ( * x) ) . collect :: < Vec < _ > > ( ) )
74+ . unwrap ( )
75+ . get_relocatable ( )
76+ . unwrap ( ) ;
77+ let decompressed_start = runner. vm . add_memory_segment ( ) ;
78+ implicit_args. extend ( [
79+ ImplicitArg :: NonBuiltin ( EndpointArg :: Value ( ValueArg :: Single ( compressed_start. into ( ) ) ) ) ,
80+ ImplicitArg :: NonBuiltin ( EndpointArg :: Value ( ValueArg :: Single ( decompressed_start. into ( ) ) ) ) ,
81+ ] ) ;
82+
83+ let explicit_args = vec ! [
84+ EndpointArg :: Value ( ValueArg :: Single ( n_elms. into( ) ) ) ,
85+ EndpointArg :: Value ( ValueArg :: Single ( Felt :: from( elm_bound) . into( ) ) ) ,
86+ EndpointArg :: Value ( ValueArg :: Single ( Felt :: from( get_n_elms_per_felt( elm_bound) ) . into( ) ) ) ,
87+ ] ;
88+
89+ // Run the entrypoint.
90+ // The unpacked data is stored in the segment starting at `decompressed_start`, the returned
91+ // implicit value is the end of the unpacked data.
92+ let state_reader = None ;
93+ let expected_explicit_return_values = vec ! [ ] ;
94+ let ( implicit_return_values, _explicit_return_values, _hint_processor) =
95+ run_cairo_0_entrypoint (
96+ entrypoint,
97+ & explicit_args,
98+ & implicit_args,
99+ state_reader,
100+ & mut runner,
101+ & program,
102+ & runner_config,
103+ & expected_explicit_return_values,
104+ )
105+ . unwrap ( ) ;
106+
107+ // The implicit return values are [range_check_ptr, compressed_start, decompressed_end].
108+ assert_eq ! ( implicit_return_values. len( ) , 3 ) ;
109+ let EndpointArg :: Value ( ValueArg :: Single ( MaybeRelocatable :: RelocatableValue ( decompressed_end) ) ) =
110+ implicit_return_values[ 2 ]
111+ else {
112+ panic ! ( "Unexpected implicit return values, got: {implicit_return_values:?}" ) ;
113+ } ;
114+
115+ // Read the compressed data from the segment and return.
116+ let unpacked_data = runner
117+ . vm
118+ . get_integer_range ( decompressed_start, ( decompressed_end - decompressed_start) . unwrap ( ) )
119+ . unwrap ( ) ;
120+ ( unpacked_data. into_iter ( ) . map ( |f| * f) . collect ( ) , runner. get_execution_resources ( ) . unwrap ( ) )
121+ }
122+
48123/// Runs the OS compression function and returns the compressed data, plus the execution resources
49124/// used to compress the data.
50125fn cairo_compress ( data : & [ Felt ] ) -> ( Vec < Felt > , ExecutionResources ) {
@@ -244,6 +319,40 @@ fn test_usize_pack_and_unpack() {
244319 assert_eq ! ( nums, unpacked) ;
245320}
246321
322+ #[ rstest]
323+ #[ case:: trivial( vec![ ] , 0 , None ) ]
324+ #[ case:: powers_of_two_plus_one( ( 0 ..125 ) . map( |i| 2_u128 . pow( i) + 1 ) . collect( ) , 63 , Some ( 23 ) ) ]
325+ #[ case:: small_number_range( ( 0 ..2u128 . pow( 15 ) ) . collect( ) , 2usize . pow( 11 ) , Some ( 15 ) ) ]
326+ #[ case:: large_number_range(
327+ ( 0 ..2u128 . pow( 10 ) ) . map( |i| 2u128 . pow( 30 ) + i) . collect( ) , 2usize . pow( 7 ) , Some ( 16 )
328+ ) ]
329+ fn test_unpack_felts (
330+ #[ case] elms : Vec < u128 > ,
331+ #[ case] expected_compression_length : usize ,
332+ #[ case] expected_n_steps_per_elm : Option < usize > ,
333+ ) {
334+ let elm_bound = elms. iter ( ) . max ( ) . unwrap_or ( & 0 ) + 1 ;
335+ let n_elms = elms. len ( ) ;
336+ let compressed = pack_in_felts ( & elms, elm_bound) ;
337+ let felt_bound = Felt :: TWO . pow ( 251u8 ) ;
338+ assert ! ( compressed. iter( ) . all( |f| * f < felt_bound) ) ;
339+ assert_eq ! ( compressed. len( ) , expected_compression_length) ;
340+
341+ let elm_felts: Vec < _ > = elms. into_iter ( ) . map ( Felt :: from) . collect ( ) ;
342+ assert_eq ! (
343+ elm_felts,
344+ unpack_felts_to:: <u128 >( & compressed, n_elms, elm_bound)
345+ . into_iter( )
346+ . map( Felt :: from)
347+ . collect:: <Vec <Felt >>( )
348+ ) ;
349+ let ( cairo_unpacked, resources) = cairo_unpack_felts ( & compressed, elm_bound, n_elms) ;
350+ assert_eq ! ( elm_felts, cairo_unpacked) ;
351+ if n_elms > 0 {
352+ assert_eq ! ( resources. n_steps / n_elms, expected_n_steps_per_elm. unwrap( ) ) ;
353+ }
354+ }
355+
247356#[ test]
248357fn test_get_bucket_offsets ( ) {
249358 let lengths = vec ! [ 2 , 3 , 5 ] ;
0 commit comments