oxigdal_algorithms/vector/
pool.rs1use oxigdal_core::vector::{Coordinate, LineString, Point, Polygon};
35use std::cell::RefCell;
36use std::mem::ManuallyDrop;
37use std::ops::{Deref, DerefMut};
38
39const INITIAL_POOL_CAPACITY: usize = 16;
41
42const MAX_POOL_SIZE: usize = 128;
44
45pub struct Pool<T> {
50 objects: Vec<T>,
51 capacity: usize,
52}
53
54impl<T> Pool<T> {
55 pub fn new(capacity: usize) -> Self {
57 Self {
58 objects: Vec::with_capacity(capacity),
59 capacity,
60 }
61 }
62
63 pub fn get<F>(&mut self, create: F) -> T
65 where
66 F: FnOnce() -> T,
67 {
68 self.objects.pop().unwrap_or_else(create)
69 }
70
71 pub fn put(&mut self, obj: T) {
75 if self.objects.len() < MAX_POOL_SIZE {
76 self.objects.push(obj);
77 }
78 }
80
81 pub fn clear(&mut self) {
83 self.objects.clear();
84 }
85
86 pub fn len(&self) -> usize {
88 self.objects.len()
89 }
90
91 pub fn is_empty(&self) -> bool {
93 self.objects.is_empty()
94 }
95}
96
97impl<T> Default for Pool<T> {
98 fn default() -> Self {
99 Self::new(INITIAL_POOL_CAPACITY)
100 }
101}
102
103pub struct PoolGuard<'a, T> {
111 object: ManuallyDrop<T>,
112 pool: &'a RefCell<Pool<T>>,
113 consumed: bool,
115}
116
117impl<'a, T> PoolGuard<'a, T> {
118 fn new(object: T, pool: &'a RefCell<Pool<T>>) -> Self {
120 Self {
121 object: ManuallyDrop::new(object),
122 pool,
123 consumed: false,
124 }
125 }
126
127 #[allow(unsafe_code)]
129 pub fn into_inner(mut self) -> T {
130 self.consumed = true;
131 unsafe { ManuallyDrop::take(&mut self.object) }
134 }
135}
136
137impl<'a, T> Deref for PoolGuard<'a, T> {
138 type Target = T;
139
140 fn deref(&self) -> &Self::Target {
141 &self.object
142 }
143}
144
145impl<'a, T> DerefMut for PoolGuard<'a, T> {
146 fn deref_mut(&mut self) -> &mut Self::Target {
147 &mut self.object
148 }
149}
150
151impl<'a, T> Drop for PoolGuard<'a, T> {
152 #[allow(unsafe_code)]
153 fn drop(&mut self) {
154 if !self.consumed {
155 let object = unsafe { ManuallyDrop::take(&mut self.object) };
158 if let Ok(mut pool) = self.pool.try_borrow_mut() {
159 pool.put(object);
160 }
161 }
163 }
164}
165
166thread_local! {
168 static POINT_POOL: RefCell<Pool<Point>> = RefCell::new(Pool::default());
169 static LINESTRING_POOL: RefCell<Pool<LineString>> = RefCell::new(Pool::default());
170 static POLYGON_POOL: RefCell<Pool<Polygon>> = RefCell::new(Pool::default());
171 static COORDINATE_VEC_POOL: RefCell<Pool<Vec<Coordinate>>> = RefCell::new(Pool::default());
172}
173
174#[allow(unsafe_code)]
178pub fn get_pooled_point(x: f64, y: f64) -> PoolGuard<'static, Point> {
179 POINT_POOL.with(|pool| {
180 let mut pool_ref = pool.borrow_mut();
181 let mut point = pool_ref.get(|| Point::new(0.0, 0.0));
182 point.coord.x = x;
184 point.coord.y = y;
185 point.coord.z = None;
186 drop(pool_ref);
187 PoolGuard::new(point, unsafe { &*(pool as *const RefCell<Pool<Point>>) })
190 })
191}
192
193#[allow(unsafe_code)]
201pub fn get_pooled_linestring() -> PoolGuard<'static, LineString> {
202 LINESTRING_POOL.with(|pool| {
203 let mut pool_ref = pool.borrow_mut();
204 let mut linestring = pool_ref.get(|| {
205 match LineString::new(vec![
209 Coordinate::new_2d(0.0, 0.0),
210 Coordinate::new_2d(0.0, 0.0),
211 ]) {
212 Ok(ls) => ls,
213 Err(_) => {
214 match LineString::new(vec![
217 Coordinate::new_2d(0.0, 0.0),
218 Coordinate::new_2d(1.0, 1.0),
219 ]) {
220 Ok(ls) => ls,
221 Err(_) => {
222 LineString {
225 coords: vec![
226 Coordinate::new_2d(0.0, 0.0),
227 Coordinate::new_2d(1.0, 1.0),
228 ],
229 }
230 }
231 }
232 }
233 }
234 });
235 if linestring.len() < 2 {
237 linestring.coords.clear();
238 linestring.coords.push(Coordinate::new_2d(0.0, 0.0));
239 linestring.coords.push(Coordinate::new_2d(0.0, 0.0));
240 }
241 drop(pool_ref);
242 PoolGuard::new(linestring, unsafe {
245 &*(pool as *const RefCell<Pool<LineString>>)
246 })
247 })
248}
249
250#[allow(unsafe_code)]
258pub fn get_pooled_polygon() -> PoolGuard<'static, Polygon> {
259 POLYGON_POOL.with(|pool| {
260 let mut pool_ref = pool.borrow_mut();
261 let mut polygon = pool_ref.get(|| {
262 let ring = match LineString::new(vec![
264 Coordinate::new_2d(0.0, 0.0),
265 Coordinate::new_2d(1.0, 0.0),
266 Coordinate::new_2d(0.0, 1.0),
267 Coordinate::new_2d(0.0, 0.0),
268 ]) {
269 Ok(ls) => ls,
270 Err(_) => {
271 LineString {
274 coords: vec![
275 Coordinate::new_2d(0.0, 0.0),
276 Coordinate::new_2d(1.0, 0.0),
277 Coordinate::new_2d(0.0, 1.0),
278 Coordinate::new_2d(0.0, 0.0),
279 ],
280 }
281 }
282 };
283 match Polygon::new(ring, vec![]) {
284 Ok(poly) => poly,
285 Err(_) => {
286 Polygon {
289 exterior: LineString {
290 coords: vec![
291 Coordinate::new_2d(0.0, 0.0),
292 Coordinate::new_2d(1.0, 0.0),
293 Coordinate::new_2d(0.0, 1.0),
294 Coordinate::new_2d(0.0, 0.0),
295 ],
296 },
297 interiors: vec![],
298 }
299 }
300 }
301 });
302 if polygon.exterior.len() < 4 {
304 polygon.exterior.coords.clear();
305 polygon.exterior.coords.push(Coordinate::new_2d(0.0, 0.0));
306 polygon.exterior.coords.push(Coordinate::new_2d(1.0, 0.0));
307 polygon.exterior.coords.push(Coordinate::new_2d(0.0, 1.0));
308 polygon.exterior.coords.push(Coordinate::new_2d(0.0, 0.0));
309 }
310 polygon.interiors.clear();
311 drop(pool_ref);
312 PoolGuard::new(polygon, unsafe {
315 &*(pool as *const RefCell<Pool<Polygon>>)
316 })
317 })
318}
319
320#[allow(unsafe_code)]
324pub fn get_pooled_coordinate_vec() -> PoolGuard<'static, Vec<Coordinate>> {
325 COORDINATE_VEC_POOL.with(|pool| {
326 let mut pool_ref = pool.borrow_mut();
327 let mut vec = pool_ref.get(Vec::new);
328 vec.clear();
329 drop(pool_ref);
330 PoolGuard::new(vec, unsafe {
331 &*(pool as *const RefCell<Pool<Vec<Coordinate>>>)
332 })
333 })
334}
335
336pub fn clear_all_pools() {
340 POINT_POOL.with(|pool| pool.borrow_mut().clear());
341 LINESTRING_POOL.with(|pool| pool.borrow_mut().clear());
342 POLYGON_POOL.with(|pool| pool.borrow_mut().clear());
343 COORDINATE_VEC_POOL.with(|pool| pool.borrow_mut().clear());
344}
345
346#[derive(Debug, Clone)]
348pub struct PoolStats {
349 pub points_pooled: usize,
350 pub linestrings_pooled: usize,
351 pub polygons_pooled: usize,
352 pub coordinate_vecs_pooled: usize,
353}
354
355pub fn get_pool_stats() -> PoolStats {
357 PoolStats {
358 points_pooled: POINT_POOL.with(|pool| pool.borrow().len()),
359 linestrings_pooled: LINESTRING_POOL.with(|pool| pool.borrow().len()),
360 polygons_pooled: POLYGON_POOL.with(|pool| pool.borrow().len()),
361 coordinate_vecs_pooled: COORDINATE_VEC_POOL.with(|pool| pool.borrow().len()),
362 }
363}
364
365#[cfg(test)]
366mod tests {
367 use super::*;
368
369 #[test]
370 fn test_pool_basic_operations() {
371 clear_all_pools();
372
373 let stats = get_pool_stats();
374 assert_eq!(stats.points_pooled, 0);
375
376 {
378 let _point = get_pooled_point(1.0, 2.0);
379 let stats = get_pool_stats();
381 assert_eq!(stats.points_pooled, 0);
382 }
383
384 let stats = get_pool_stats();
386 assert_eq!(stats.points_pooled, 1);
387 }
388
389 #[test]
390 fn test_pool_guard_deref() {
391 clear_all_pools();
392
393 let point = get_pooled_point(3.0, 4.0);
394 assert_eq!(point.x(), 3.0);
395 assert_eq!(point.y(), 4.0);
396 }
397
398 #[test]
399 fn test_pool_guard_deref_mut() {
400 clear_all_pools();
401
402 let mut point = get_pooled_point(1.0, 1.0);
403 point.coord.x = 5.0;
404 point.coord.y = 6.0;
405 assert_eq!(point.x(), 5.0);
406 assert_eq!(point.y(), 6.0);
407 }
408
409 #[test]
410 fn test_pool_reuse() {
411 clear_all_pools();
412
413 for i in 0..5 {
415 let _point = get_pooled_point(i as f64, i as f64);
416 }
417
418 let stats = get_pool_stats();
420 assert_eq!(stats.points_pooled, 1);
421
422 let _point = get_pooled_point(100.0, 100.0);
424 let stats = get_pool_stats();
425 assert_eq!(stats.points_pooled, 0);
426 }
427
428 #[test]
429 fn test_linestring_pool() {
430 clear_all_pools();
431
432 let mut linestring = get_pooled_linestring();
433 linestring.coords.clear();
434 linestring.coords.push(Coordinate::new_2d(0.0, 0.0));
435 linestring.coords.push(Coordinate::new_2d(1.0, 1.0));
436
437 assert_eq!(linestring.len(), 2);
438 drop(linestring);
439
440 let stats = get_pool_stats();
441 assert_eq!(stats.linestrings_pooled, 1);
442
443 let linestring = get_pooled_linestring();
445 assert_eq!(linestring.len(), 2);
446 }
447
448 #[test]
449 fn test_polygon_pool() {
450 clear_all_pools();
451
452 let polygon = get_pooled_polygon();
453 assert_eq!(polygon.exterior().len(), 4);
454 assert_eq!(polygon.interiors().len(), 0);
455 drop(polygon);
456
457 let stats = get_pool_stats();
458 assert_eq!(stats.polygons_pooled, 1);
459 }
460
461 #[test]
462 fn test_coordinate_vec_pool() {
463 clear_all_pools();
464
465 let mut coords = get_pooled_coordinate_vec();
466 coords.push(Coordinate::new_2d(1.0, 2.0));
467 coords.push(Coordinate::new_2d(3.0, 4.0));
468 assert_eq!(coords.len(), 2);
469 drop(coords);
470
471 let stats = get_pool_stats();
472 assert_eq!(stats.coordinate_vecs_pooled, 1);
473
474 let coords = get_pooled_coordinate_vec();
476 assert_eq!(coords.len(), 0);
477 }
478
479 #[test]
480 fn test_pool_max_size() {
481 clear_all_pools();
482
483 for i in 0..(MAX_POOL_SIZE + 10) {
485 let _point = get_pooled_point(i as f64, i as f64);
486 }
487
488 let stats = get_pool_stats();
490 assert!(stats.points_pooled <= MAX_POOL_SIZE);
491 }
492
493 #[test]
494 fn test_into_inner() {
495 clear_all_pools();
496
497 let guard = get_pooled_point(7.0, 8.0);
498 let point = guard.into_inner();
499
500 assert_eq!(point.x(), 7.0);
501 assert_eq!(point.y(), 8.0);
502
503 let stats = get_pool_stats();
505 assert_eq!(stats.points_pooled, 0);
506 }
507
508 #[test]
509 fn test_clear_all_pools() {
510 let _p = get_pooled_point(1.0, 1.0);
512 let _l = get_pooled_linestring();
513 let _poly = get_pooled_polygon();
514 let _coords = get_pooled_coordinate_vec();
515
516 drop(_p);
517 drop(_l);
518 drop(_poly);
519 drop(_coords);
520
521 let stats = get_pool_stats();
523 assert!(stats.points_pooled > 0);
524 assert!(stats.linestrings_pooled > 0);
525 assert!(stats.polygons_pooled > 0);
526 assert!(stats.coordinate_vecs_pooled > 0);
527
528 clear_all_pools();
530
531 let stats = get_pool_stats();
533 assert_eq!(stats.points_pooled, 0);
534 assert_eq!(stats.linestrings_pooled, 0);
535 assert_eq!(stats.polygons_pooled, 0);
536 assert_eq!(stats.coordinate_vecs_pooled, 0);
537 }
538
539 #[test]
540 fn test_thread_local_isolation() {
541 use std::thread;
542
543 clear_all_pools();
544
545 {
547 let _point = get_pooled_point(1.0, 1.0);
548 }
549
550 let main_stats = get_pool_stats();
551 assert_eq!(main_stats.points_pooled, 1);
552
553 let handle = thread::spawn(|| {
555 let stats = get_pool_stats();
556 assert_eq!(stats.points_pooled, 0);
557
558 {
559 let _point = get_pooled_point(2.0, 2.0);
560 }
561
562 let stats = get_pool_stats();
563 assert_eq!(stats.points_pooled, 1);
564 });
565
566 handle.join().expect("Thread panicked");
567
568 let main_stats = get_pool_stats();
570 assert_eq!(main_stats.points_pooled, 1);
571 }
572}