#![allow(clippy::pedantic, reason = "test tool")]
use geo_types::Coord;
use map_tile_toolkit::{Slicer, TileId};
use stats_alloc::{INSTRUMENTED_SYSTEM, Region, StatsAlloc};
mod support;
#[global_allocator]
static GLOBAL: &StatsAlloc<std::alloc::System> = &INSTRUMENTED_SYSTEM;
fn allocations(f: impl FnOnce()) -> usize {
let region = Region::new(GLOBAL);
f();
region.change().allocations
}
#[test]
fn slicing_allocates_little() {
lines_and_points_allocate_nothing();
polygon_memory_is_one_row();
polygon_index_is_a_fraction_of_the_input();
}
fn lines_and_points_allocate_nothing() {
let zigzag: Vec<Coord<i32>> = (0..200_000)
.map(|i| Coord {
x: (i * 37) % 1000,
y: (i * 91) % 1000,
})
.collect();
let diagonal = vec![
Coord { x: 3, y: 5 },
Coord {
x: 999_990,
y: 999_000,
},
];
let points: Vec<Coord<i32>> = zigzag.iter().step_by(7).copied().collect();
let slicer = Slicer::new(25, 2).expect("config");
let lines = [zigzag.as_slice(), diagonal.as_slice()];
let mut slices = 0u64;
let mut vertices = 0u64;
let mut slice_all = || {
for line in lines {
for s in slicer.line(line).expect("line") {
slices += 1;
vertices += s.run().iter().count() as u64;
}
for s in slicer.tile_line(TileId::new(3, 4), line).expect("tile") {
vertices += s.run().iter().count() as u64;
}
}
for &point in &points {
for _ in slicer.point(point).expect("point") {
slices += 1;
}
}
};
slice_all();
let used = allocations(&mut slice_all);
assert!(slices > 100_000, "{slices}");
assert!(vertices > 200_000, "{vertices}");
assert_eq!(used, 0, "slicing allocated {used} times");
}
fn bytes(f: impl FnOnce()) -> usize {
let region = Region::new(GLOBAL);
f();
region.change().bytes_allocated
}
fn drain(slicer: &mut Slicer<Coord<i32>>, input: &[&[Coord<i32>]]) -> u64 {
let mut slices = slicer.polygon(input).expect("polygon");
let mut n = 0;
while let Some(slice) = slices.next() {
if let map_tile_toolkit::PolygonSlice::Piece(piece) = slice.expect("slice") {
for ring in piece.rings() {
for v in ring {
std::hint::black_box(v);
}
}
}
n += 1;
}
n
}
fn polygon_memory_is_one_row() {
let ocean = support::huge_square();
let input = support::rings(&ocean);
let mut slicer = Slicer::new(4096, 64).expect("config");
let mut slices = 0;
let first = bytes(|| slices = drain(&mut slicer, &input));
assert!(slices > 16_000, "{slices}");
assert!(first < 4 * 1024, "first slicing allocated {first} bytes");
assert_eq!(
allocations(|| {
drain(&mut slicer, &input);
}),
0
);
let big = support::big_polygon();
let input = support::rings(&big);
let mut slicer = Slicer::new(25, 2).expect("config");
drain(&mut slicer, &input);
assert_eq!(
allocations(|| {
drain(&mut slicer, &input);
}),
0
);
}
fn polygon_index_is_a_fraction_of_the_input() {
const N: usize = 200_000;
let ring: Vec<Coord<i32>> = (0..N)
.map(|k| {
let angle = std::f64::consts::TAU * k as f64 / N as f64;
let radius = 3_000_000.0 + 20_000.0 * ((k * 37) % 50) as f64 / 50.0;
Coord {
x: (radius * angle.cos()) as i32 + 4_000_000,
y: (radius * angle.sin()) as i32 + 4_000_000,
}
})
.collect();
let input = [ring.as_slice()];
let mut slicer = Slicer::new(4096, 64).expect("config");
let mut slices = 0;
let first = bytes(|| slices = drain(&mut slicer, &input));
assert!(slices > 10_000, "{slices}");
assert!(
first < 8 * N,
"first slicing allocated {first} bytes for {N} vertices"
);
assert_eq!(
allocations(|| {
drain(&mut slicer, &input);
}),
0
);
}