@@ -68,18 +68,30 @@ fn optimized_flood_fill_area(
6868
6969 // Pre-allocate queue with reasonable capacity to avoid reallocations
7070 let mut queue = VecDeque :: with_capacity ( 1024 ) ;
71+ let mut iterations = 0u64 ;
72+ const MAX_ITERATIONS : u64 = 1_000_000 ; // Safety limit to prevent infinite loops
7173
7274 for z in ( min_z..=max_z) . step_by ( step_z as usize ) {
7375 for x in ( min_x..=max_x) . step_by ( step_x as usize ) {
74- // Fast timeout check - only every few iterations
75- if filled_area . len ( ) % 100 == 0 {
76+ // Check timeout more frequently for small areas
77+ if iterations % 50 == 0 {
7678 if let Some ( timeout) = timeout {
7779 if start_time. elapsed ( ) > * timeout {
7880 return filled_area;
7981 }
8082 }
8183 }
8284
85+ // Safety check: prevent infinite loops
86+ iterations += 1 ;
87+ if iterations > MAX_ITERATIONS {
88+ eprintln ! (
89+ "Warning: Flood fill exceeded max iterations ({}), aborting" ,
90+ MAX_ITERATIONS
91+ ) ;
92+ return filled_area;
93+ }
94+
8395 // Skip if already visited or not inside polygon
8496 if global_visited. contains ( & ( x, z) )
8597 || !polygon. contains ( & Point :: new ( x as f64 , z as f64 ) )
@@ -93,6 +105,25 @@ fn optimized_flood_fill_area(
93105 global_visited. insert ( ( x, z) ) ;
94106
95107 while let Some ( ( curr_x, curr_z) ) = queue. pop_front ( ) {
108+ // Additional iteration check inside inner loop
109+ iterations += 1 ;
110+ if iterations > MAX_ITERATIONS {
111+ eprintln ! (
112+ "Warning: Flood fill exceeded max iterations ({}), aborting" ,
113+ MAX_ITERATIONS
114+ ) ;
115+ return filled_area;
116+ }
117+
118+ // Timeout check in inner loop for problematic polygons
119+ if iterations % 1000 == 0 {
120+ if let Some ( timeout) = timeout {
121+ if start_time. elapsed ( ) > * timeout {
122+ return filled_area;
123+ }
124+ }
125+ }
126+
96127 // Add current point to filled area
97128 filled_area. push ( ( curr_x, curr_z) ) ;
98129
@@ -155,19 +186,31 @@ fn original_flood_fill_area(
155186 // Pre-allocate queue and reserve space for filled_area
156187 let mut queue: VecDeque < ( i32 , i32 ) > = VecDeque :: with_capacity ( 2048 ) ;
157188 filled_area. reserve ( 1000 ) ; // Reserve space to reduce reallocations
189+ let mut iterations = 0u64 ;
190+ const MAX_ITERATIONS : u64 = 1_000_000 ; // Safety limit to prevent infinite loops
158191
159192 // Scan for multiple seed points to handle U-shapes and concave polygons
160193 for z in ( min_z..=max_z) . step_by ( step_z as usize ) {
161194 for x in ( min_x..=max_x) . step_by ( step_x as usize ) {
162- // Reduced timeout checking frequency for better performance
163- // Use manual % check since is_multiple_of() is unstable on stable Rust
164- # [ allow ( clippy :: manual_is_multiple_of ) ]
165- if let Some ( timeout ) = timeout {
166- if & start_time . elapsed ( ) > timeout {
167- return filled_area ;
195+ // Check timeout more frequently for problematic polygons
196+ if iterations % 50 == 0 {
197+ if let Some ( timeout ) = timeout {
198+ if & start_time . elapsed ( ) > timeout {
199+ return filled_area ;
200+ }
168201 }
169202 }
170203
204+ // Safety check: prevent infinite loops
205+ iterations += 1 ;
206+ if iterations > MAX_ITERATIONS {
207+ eprintln ! (
208+ "Warning: Flood fill exceeded max iterations ({}), aborting" ,
209+ MAX_ITERATIONS
210+ ) ;
211+ return filled_area;
212+ }
213+
171214 // Skip if already processed or not inside polygon
172215 if global_visited. contains ( & ( x, z) )
173216 || !polygon. contains ( & Point :: new ( x as f64 , z as f64 ) )
@@ -181,6 +224,25 @@ fn original_flood_fill_area(
181224 global_visited. insert ( ( x, z) ) ;
182225
183226 while let Some ( ( curr_x, curr_z) ) = queue. pop_front ( ) {
227+ // Additional iteration check inside inner loop
228+ iterations += 1 ;
229+ if iterations > MAX_ITERATIONS {
230+ eprintln ! (
231+ "Warning: Flood fill exceeded max iterations ({}), aborting" ,
232+ MAX_ITERATIONS
233+ ) ;
234+ return filled_area;
235+ }
236+
237+ // Timeout check in inner loop
238+ if iterations % 1000 == 0 {
239+ if let Some ( timeout) = timeout {
240+ if & start_time. elapsed ( ) > timeout {
241+ return filled_area;
242+ }
243+ }
244+ }
245+
184246 // Only check polygon containment once per point when adding to filled_area
185247 if polygon. contains ( & Point :: new ( curr_x as f64 , curr_z as f64 ) ) {
186248 filled_area. push ( ( curr_x, curr_z) ) ;
0 commit comments