@@ -286,12 +286,95 @@ impl Memory {
286
286
}
287
287
Ok ( addr. into ( ) )
288
288
}
289
+ #[ cfg( not( feature = "extensive_hints" ) ) ]
290
+ fn flatten_relocation_rules ( & mut self ) -> Result < ( ) , MemoryError > {
291
+ let keys: Vec < usize > = self . relocation_rules . keys ( ) . copied ( ) . collect ( ) ;
292
+ let max_hops = self . relocation_rules . len ( ) . saturating_add ( 1 ) ;
293
+ for key in keys {
294
+ let mut dst = * self
295
+ . relocation_rules
296
+ . get ( & key)
297
+ . expect ( "key taken from keys vec must exist" ) ;
298
+
299
+ let mut hops = 0usize ;
300
+ while dst. segment_index < 0 {
301
+ let next_key = ( -( dst. segment_index + 1 ) ) as usize ;
302
+ let next = * self . relocation_rules . get ( & next_key) . ok_or_else ( || {
303
+ MemoryError :: UnallocatedSegment ( Box :: new ( ( next_key, self . temp_data . len ( ) ) ) )
304
+ } ) ?;
305
+ dst = ( next + dst. offset ) . map_err ( MemoryError :: Math ) ?;
306
+ hops += 1 ;
307
+ if hops > max_hops {
308
+ return Err ( MemoryError :: Relocation ) ; // cycle guard
309
+ }
310
+ }
311
+ self . relocation_rules . insert ( key, dst) ;
312
+ }
313
+ Ok ( ( ) )
314
+ }
315
+
316
+ #[ cfg( feature = "extensive_hints" ) ]
317
+ fn flatten_relocation_rules ( & mut self ) -> Result < ( ) , MemoryError > {
318
+ let keys: Vec < usize > = self . relocation_rules . keys ( ) . copied ( ) . collect ( ) ;
319
+ let max_hops = self . relocation_rules . len ( ) . saturating_add ( 1 ) ;
320
+ for key in keys {
321
+ let mut dst = self
322
+ . relocation_rules
323
+ . get ( & key)
324
+ . expect ( "key taken from keys vec must exist" )
325
+ . clone ( ) ;
326
+
327
+ let mut hops = 0usize ;
328
+ loop {
329
+ match dst {
330
+ MaybeRelocatable :: RelocatableValue ( r) if r. segment_index < 0 => {
331
+ let next_key = ( -( r. segment_index + 1 ) ) as usize ;
332
+ let next = self
333
+ . relocation_rules
334
+ . get ( & next_key)
335
+ . ok_or_else ( || {
336
+ MemoryError :: UnallocatedSegment ( Box :: new ( (
337
+ next_key,
338
+ self . temp_data . len ( ) ,
339
+ ) ) )
340
+ } ) ?
341
+ . clone ( ) ;
342
+
343
+ match next {
344
+ MaybeRelocatable :: RelocatableValue ( nr) => {
345
+ dst = MaybeRelocatable :: RelocatableValue (
346
+ ( nr + r. offset ) . map_err ( MemoryError :: Math ) ?,
347
+ ) ;
348
+ }
349
+ MaybeRelocatable :: Int ( i) => {
350
+ if r. offset != 0 {
351
+ return Err ( MemoryError :: NonZeroOffset ( r. offset ) ) ;
352
+ }
353
+ dst = MaybeRelocatable :: Int ( i) ;
354
+ }
355
+ }
356
+ }
357
+ _ => break ,
358
+ }
359
+ hops += 1 ;
360
+ if hops > max_hops {
361
+ return Err ( MemoryError :: Relocation ) ;
362
+ }
363
+ }
364
+ self . relocation_rules . insert ( key, dst) ;
365
+ }
366
+ Ok ( ( ) )
367
+ }
289
368
290
369
/// Relocates the memory according to the relocation rules and clears `self.relocaction_rules`.
291
370
pub fn relocate_memory ( & mut self ) -> Result < ( ) , MemoryError > {
292
371
if self . relocation_rules . is_empty ( ) || self . temp_data . is_empty ( ) {
293
372
return Ok ( ( ) ) ;
294
373
}
374
+
375
+ // flatten chains (temp->temp->...->real).
376
+ self . flatten_relocation_rules ( ) ?;
377
+
295
378
// Relocate temporary addresses in memory
296
379
for segment in self . data . iter_mut ( ) . chain ( self . temp_data . iter_mut ( ) ) {
297
380
for cell in segment. iter_mut ( ) {
@@ -345,6 +428,7 @@ impl Memory {
345
428
self . relocation_rules . clear ( ) ;
346
429
Ok ( ( ) )
347
430
}
431
+
348
432
/// Add a new relocation rule.
349
433
///
350
434
/// When using feature "extensive_hints" the destination is allowed to be an Integer (via
@@ -1971,4 +2055,148 @@ mod memory_tests {
1971
2055
] )
1972
2056
)
1973
2057
}
2058
+
2059
+ #[ test]
2060
+ #[ cfg( not( feature = "extensive_hints" ) ) ]
2061
+ fn flatten_relocation_rules_chain_happy ( ) {
2062
+ let mut mem = Memory :: new ( ) ;
2063
+ // temp segments just to keep indices sensible (not strictly required here)
2064
+ mem. temp_data = vec ! [ vec![ ] , vec![ ] ] ;
2065
+
2066
+ // key 0 -> (-2, 3) ; key 1 -> (10, 4)
2067
+ // after flatten: key 0 -> (10, 7)
2068
+ mem. relocation_rules . insert ( 0 , Relocatable :: from ( ( -2 , 3 ) ) ) ;
2069
+ mem. relocation_rules . insert ( 1 , Relocatable :: from ( ( 10 , 4 ) ) ) ;
2070
+ // sanity: an unrelated rule stays as-is
2071
+ mem. relocation_rules . insert ( 2 , Relocatable :: from ( ( 7 , 1 ) ) ) ;
2072
+
2073
+ assert_eq ! ( mem. flatten_relocation_rules( ) , Ok ( ( ) ) ) ;
2074
+ assert_eq ! (
2075
+ * mem. relocation_rules. get( & 0 ) . unwrap( ) ,
2076
+ Relocatable :: from( ( 10 , 7 ) )
2077
+ ) ;
2078
+ assert_eq ! (
2079
+ * mem. relocation_rules. get( & 1 ) . unwrap( ) ,
2080
+ Relocatable :: from( ( 10 , 4 ) )
2081
+ ) ;
2082
+ assert_eq ! (
2083
+ * mem. relocation_rules. get( & 2 ) . unwrap( ) ,
2084
+ Relocatable :: from( ( 7 , 1 ) )
2085
+ ) ;
2086
+ }
2087
+
2088
+ #[ test]
2089
+ #[ cfg( not( feature = "extensive_hints" ) ) ]
2090
+ fn flatten_relocation_rules_cycle_err ( ) {
2091
+ let mut mem = Memory :: new ( ) ;
2092
+ mem. temp_data = vec ! [ vec![ ] ] ;
2093
+
2094
+ // Self-loop: key 0 -> (-1, 0) (i.e., points back to itself)
2095
+ mem. relocation_rules . insert ( 0 , Relocatable :: from ( ( -1 , 0 ) ) ) ;
2096
+
2097
+ assert_eq ! ( mem. flatten_relocation_rules( ) , Err ( MemoryError :: Relocation ) ) ;
2098
+ }
2099
+
2100
+ #[ test]
2101
+ #[ cfg( feature = "extensive_hints" ) ]
2102
+ fn flatten_relocation_rules_chain_happy_extensive_reloc_and_int ( ) {
2103
+ let mut mem = Memory :: new ( ) ;
2104
+ mem. temp_data = vec ! [ vec![ ] , vec![ ] , vec![ ] , vec![ ] , vec![ ] ] ;
2105
+
2106
+ // ---- Chain A (ends at relocatable) ----
2107
+ // key 0 -> (-2, 3) (=> key 1)
2108
+ // key 1 -> (10, 4) (real)
2109
+ // Expect: key 0 -> (10, 7)
2110
+ mem. relocation_rules . insert (
2111
+ 0 ,
2112
+ MaybeRelocatable :: RelocatableValue ( Relocatable :: from ( ( -2 , 3 ) ) ) ,
2113
+ ) ;
2114
+ mem. relocation_rules . insert (
2115
+ 1 ,
2116
+ MaybeRelocatable :: RelocatableValue ( Relocatable :: from ( ( 10 , 4 ) ) ) ,
2117
+ ) ;
2118
+
2119
+ // ---- Chain B (ends at int) ----
2120
+ // key 2 -> (-4, 0) (=> key 3)
2121
+ // key 3 -> (-5, 0) (=> key 4)
2122
+ // key 4 -> 99 (final Int)
2123
+ // Expect: key 2 -> 99 (offset is zero along the way)
2124
+ mem. relocation_rules . insert (
2125
+ 2 ,
2126
+ MaybeRelocatable :: RelocatableValue ( Relocatable :: from ( ( -4 , 0 ) ) ) ,
2127
+ ) ;
2128
+ mem. relocation_rules . insert (
2129
+ 3 ,
2130
+ MaybeRelocatable :: RelocatableValue ( Relocatable :: from ( ( -5 , 0 ) ) ) ,
2131
+ ) ;
2132
+ mem. relocation_rules
2133
+ . insert ( 4 , MaybeRelocatable :: Int ( Felt252 :: from ( 99 ) ) ) ;
2134
+
2135
+ assert_eq ! ( mem. flatten_relocation_rules( ) , Ok ( ( ) ) ) ;
2136
+
2137
+ assert_eq ! (
2138
+ mem. relocation_rules. get( & 0 ) ,
2139
+ Some ( & MaybeRelocatable :: RelocatableValue ( Relocatable :: from( (
2140
+ 10 , 7
2141
+ ) ) ) )
2142
+ ) ;
2143
+ assert_eq ! (
2144
+ mem. relocation_rules. get( & 1 ) ,
2145
+ Some ( & MaybeRelocatable :: RelocatableValue ( Relocatable :: from( (
2146
+ 10 , 4
2147
+ ) ) ) )
2148
+ ) ;
2149
+ assert_eq ! (
2150
+ mem. relocation_rules. get( & 2 ) ,
2151
+ Some ( & MaybeRelocatable :: Int ( Felt252 :: from( 99 ) ) )
2152
+ ) ;
2153
+ }
2154
+
2155
+ #[ test]
2156
+ #[ cfg( feature = "extensive_hints" ) ]
2157
+ fn flatten_relocation_rules_int_with_non_zero_offset_err ( ) {
2158
+ let mut mem = Memory :: new ( ) ;
2159
+ // temp_data length only matters for error messages; keep it >= biggest temp key + 1
2160
+ mem. temp_data = vec ! [ vec![ ] , vec![ ] , vec![ ] ] ;
2161
+
2162
+ // key 0 -> (-2, 5) (points to key 1, offset 5)
2163
+ // key 1 -> (-3, 0) (points to key 2, offset 2)
2164
+ // key 2 -> 7 (final Int)
2165
+ mem. relocation_rules . insert (
2166
+ 0 ,
2167
+ MaybeRelocatable :: RelocatableValue ( Relocatable :: from ( ( -2 , 5 ) ) ) ,
2168
+ ) ;
2169
+ mem. relocation_rules . insert (
2170
+ 1 ,
2171
+ MaybeRelocatable :: RelocatableValue ( Relocatable :: from ( ( -3 , 0 ) ) ) ,
2172
+ ) ;
2173
+ mem. relocation_rules
2174
+ . insert ( 2 , MaybeRelocatable :: Int ( Felt252 :: from ( 7 ) ) ) ;
2175
+
2176
+ assert_eq ! (
2177
+ mem. flatten_relocation_rules( ) ,
2178
+ Err ( MemoryError :: NonZeroOffset ( 5 ) )
2179
+ ) ;
2180
+ }
2181
+
2182
+ #[ test]
2183
+ #[ cfg( feature = "extensive_hints" ) ]
2184
+ fn flatten_relocation_rules_cycle_err_extensive ( ) {
2185
+ let mut mem = Memory :: new ( ) ;
2186
+ mem. temp_data = vec ! [ vec![ ] , vec![ ] ] ;
2187
+
2188
+ // 2-cycle:
2189
+ // key 0 -> (-1, 0) (points to key 0)
2190
+ // key 1 -> (-2, 0) (points to key 1)
2191
+ mem. relocation_rules . insert (
2192
+ 0 ,
2193
+ MaybeRelocatable :: RelocatableValue ( Relocatable :: from ( ( -1 , 0 ) ) ) ,
2194
+ ) ;
2195
+ mem. relocation_rules . insert (
2196
+ 1 ,
2197
+ MaybeRelocatable :: RelocatableValue ( Relocatable :: from ( ( -2 , 0 ) ) ) ,
2198
+ ) ;
2199
+
2200
+ assert_eq ! ( mem. flatten_relocation_rules( ) , Err ( MemoryError :: Relocation ) ) ;
2201
+ }
1974
2202
}
0 commit comments