Merge branch 'imbris/worldgen-speedup' into 'master'

Worldgen Speedup

See merge request veloren/veloren!507
This commit is contained in:
Joshua Barretto 2019-09-23 15:29:20 +00:00
commit 1936224901
8 changed files with 324 additions and 205 deletions

22
Cargo.lock generated
View File

@ -93,6 +93,25 @@ dependencies = [
"num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "arr_macro"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"arr_macro_impl 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro-hack 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "arr_macro_impl"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro-hack 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "arrayref"
version = "0.3.5"
@ -3733,6 +3752,7 @@ dependencies = [
name = "veloren-world"
version = "0.3.0"
dependencies = [
"arr_macro 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"hashbrown 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"minifb 0.12.0 (git+https://github.com/emoon/rust_minifb.git)",
@ -3943,6 +3963,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum anymap 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "33954243bd79057c2de7338850b85983a44588021f8a5fee574a8888c6de4344"
"checksum approx 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "08abcc3b4e9339e33a3d0a5ed15d84a687350c05689d825e0f6655eef9e76a94"
"checksum approx 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f0e60b75072ecd4168020818c0107f2857bb6c4e64252d8d3983f6263b40a5c3"
"checksum arr_macro 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d262b83f2f573121554ad6e764cd444303df85d86e5fcebc81903ddcf8dd3a97"
"checksum arr_macro_impl 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8decbe97ffec939e44228d91e5d0829ceb1616c6ed0984c09df164b1e7ebaafc"
"checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee"
"checksum arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b8d73f9beda665eaa98ab9e4f7442bd4e7de6652587de55b2525e52e29c1b0ba"
"checksum ascii 0.8.7 (registry+https://github.com/rust-lang/crates.io-index)" = "97be891acc47ca214468e09425d02cef3af2c94d0d82081cd02061f996802f14"

View File

@ -12,6 +12,7 @@ hashbrown = { version = "0.6.0", features = ["serde"] }
lazy_static = "1.4.0"
rand = "0.7.2"
rand_chacha = "0.2.1"
arr_macro = "0.1.2"
[dev-dependencies]
minifb = { git = "https://github.com/emoon/rust_minifb.git" }

View File

@ -3,7 +3,7 @@ mod natural;
use crate::{
column::{ColumnGen, ColumnSample},
generator::{Generator, TownGen},
util::{HashCache, RandomField, Sampler, SamplerMut},
util::{RandomField, Sampler, SmallCache},
World, CONFIG,
};
use common::{
@ -16,7 +16,7 @@ use vek::*;
pub struct BlockGen<'a> {
world: &'a World,
column_cache: HashCache<Vec2<i32>, Option<ColumnSample<'a>>>,
column_cache: SmallCache<Option<ColumnSample<'a>>>,
column_gen: ColumnGen<'a>,
}
@ -24,14 +24,14 @@ impl<'a> BlockGen<'a> {
pub fn new(world: &'a World, column_gen: ColumnGen<'a>) -> Self {
Self {
world,
column_cache: HashCache::with_capacity(64),
column_cache: SmallCache::default(),
column_gen,
}
}
fn sample_column(
column_gen: &ColumnGen<'a>,
cache: &mut HashCache<Vec2<i32>, Option<ColumnSample<'a>>>,
cache: &mut SmallCache<Option<ColumnSample<'a>>>,
wpos: Vec2<i32>,
) -> Option<ColumnSample<'a>> {
cache
@ -41,10 +41,11 @@ impl<'a> BlockGen<'a> {
fn get_cliff_height(
column_gen: &ColumnGen<'a>,
cache: &mut HashCache<Vec2<i32>, Option<ColumnSample<'a>>>,
cache: &mut SmallCache<Option<ColumnSample<'a>>>,
wpos: Vec2<f32>,
close_cliffs: &[(Vec2<i32>, u32); 9],
cliff_hill: f32,
tolerance: f32,
) -> f32 {
close_cliffs.iter().fold(
0.0f32,
@ -61,7 +62,7 @@ impl<'a> BlockGen<'a> {
max_height.max(
if cliff_pos.map(|e| e as f32).distance_squared(wpos)
< (radius * radius) as f32
< (radius as f32 + tolerance).powf(2.0)
{
cliff_sample.alt
+ height as f32 * (1.0 - cliff_sample.chaos)
@ -84,7 +85,7 @@ impl<'a> BlockGen<'a> {
} = self;
// Main sample
let sample = Self::sample_column(column_gen, column_cache, wpos)?;
let sample = column_gen.get(wpos)?;
// Tree samples
let mut structure_samples = [None, None, None, None, None, None, None, None, None];
@ -129,7 +130,12 @@ impl<'a> BlockGen<'a> {
})
}
pub fn get_with_z_cache(&mut self, wpos: Vec3<i32>, z_cache: Option<&ZCache>) -> Option<Block> {
pub fn get_with_z_cache(
&mut self,
wpos: Vec3<i32>,
z_cache: Option<&ZCache>,
only_structures: bool,
) -> Option<Block> {
let BlockGen {
world,
column_cache,
@ -165,195 +171,199 @@ impl<'a> BlockGen<'a> {
let wposf = wpos.map(|e| e as f64);
let (_definitely_underground, height, water_height) =
if (wposf.z as f32) < alt - 64.0 * chaos {
// Shortcut warping
(true, alt, CONFIG.sea_level /*water_level*/)
} else {
// Apply warping
let warp = (world.sim().gen_ctx.warp_nz.get(wposf.div(48.0)) as f32)
.mul((chaos - 0.1).max(0.0))
.mul(48.0)
+ (world.sim().gen_ctx.warp_nz.get(wposf.div(15.0)) as f32)
.mul((chaos - 0.1).max(0.0))
.mul(24.0);
let height = if (wposf.z as f32) < alt + warp - 10.0 {
// Shortcut cliffs
alt + warp
let (block, height) = if !only_structures {
let (_definitely_underground, height, water_height) =
if (wposf.z as f32) < alt - 64.0 * chaos {
// Shortcut warping
(true, alt, CONFIG.sea_level /*water_level*/)
} else {
let turb = Vec2::new(
world.sim().gen_ctx.fast_turb_x_nz.get(wposf.div(25.0)) as f32,
world.sim().gen_ctx.fast_turb_y_nz.get(wposf.div(25.0)) as f32,
) * 8.0;
// Apply warping
let warp = (world.sim().gen_ctx.warp_nz.get(wposf.div(48.0)) as f32)
.mul((chaos - 0.1).max(0.0))
.mul(48.0)
+ (world.sim().gen_ctx.warp_nz.get(wposf.div(15.0)) as f32)
.mul((chaos - 0.1).max(0.0))
.mul(24.0);
let wpos_turb = Vec2::from(wpos).map(|e: i32| e as f32) + turb;
let cliff_height = Self::get_cliff_height(
column_gen,
column_cache,
wpos_turb,
&close_cliffs,
cliff_hill,
);
let height = if (wposf.z as f32) < alt + warp - 10.0 {
// Shortcut cliffs
alt + warp
} else {
let turb = Vec2::new(
world.sim().gen_ctx.fast_turb_x_nz.get(wposf.div(25.0)) as f32,
world.sim().gen_ctx.fast_turb_y_nz.get(wposf.div(25.0)) as f32,
) * 8.0;
(alt + warp).max(cliff_height)
let wpos_turb = Vec2::from(wpos).map(|e: i32| e as f32) + turb;
let cliff_height = Self::get_cliff_height(
column_gen,
column_cache,
wpos_turb,
&close_cliffs,
cliff_hill,
0.0,
);
(alt + warp).max(cliff_height)
};
(
false,
height,
/*(water_level + warp).max(*/ CONFIG.sea_level, /*)*/
)
};
(
false,
height,
/*(water_level + warp).max(*/ CONFIG.sea_level, /*)*/
)
};
// Sample blocks
// Sample blocks
// let stone_col = Rgb::new(240, 230, 220);
let stone_col = Rgb::new(195, 187, 201);
// let stone_col = Rgb::new(240, 230, 220);
let stone_col = Rgb::new(195, 187, 201);
// let dirt_col = Rgb::new(79, 67, 60);
// let dirt_col = Rgb::new(79, 67, 60);
let _air = Block::empty();
// let stone = Block::new(2, stone_col);
// let surface_stone = Block::new(1, Rgb::new(200, 220, 255));
// let dirt = Block::new(1, dirt_col);
// let sand = Block::new(1, Rgb::new(180, 150, 50));
// let warm_stone = Block::new(1, Rgb::new(165, 165, 130));
let _air = Block::empty();
// let stone = Block::new(2, stone_col);
// let surface_stone = Block::new(1, Rgb::new(200, 220, 255));
// let dirt = Block::new(1, dirt_col);
// let sand = Block::new(1, Rgb::new(180, 150, 50));
// let warm_stone = Block::new(1, Rgb::new(165, 165, 130));
let water = Block::new(BlockKind::Water, Rgb::new(60, 90, 190));
let water = Block::new(BlockKind::Water, Rgb::new(60, 90, 190));
let grass_depth = 1.5 + 2.0 * chaos;
let block = if (wposf.z as f32) < height - grass_depth {
let col = Lerp::lerp(
saturate_srgb(sub_surface_color, 0.45).map(|e| (e * 255.0) as u8),
stone_col,
(height - grass_depth - wposf.z as f32) * 0.15,
);
let grass_depth = 1.5 + 2.0 * chaos;
let block = if (wposf.z as f32) < height - grass_depth {
let col = Lerp::lerp(
saturate_srgb(sub_surface_color, 0.45).map(|e| (e * 255.0) as u8),
stone_col,
(height - grass_depth - wposf.z as f32) * 0.15,
);
// Underground
if (wposf.z as f32) > alt - 32.0 * chaos {
Some(Block::new(BlockKind::Normal, col))
} else {
Some(Block::new(BlockKind::Dense, col))
}
} else if (wposf.z as f32) < height {
let col = Lerp::lerp(
sub_surface_color,
surface_color,
(wposf.z as f32 - (height - grass_depth))
.div(grass_depth)
.powf(0.5),
);
// Surface
Some(Block::new(
BlockKind::Normal,
saturate_srgb(col, 0.45).map(|e| (e * 255.0) as u8),
))
} else if (wposf.z as f32) < height + 0.9
&& temp < CONFIG.desert_temp
&& (wposf.z as f32 > water_height + 3.0)
&& marble > 0.68
&& marble_small > 0.65
&& (marble * 3173.7).fract() < 0.5
{
let flowers = [
BlockKind::BlueFlower,
BlockKind::PinkFlower,
BlockKind::PurpleFlower,
BlockKind::RedFlower,
BlockKind::WhiteFlower,
BlockKind::YellowFlower,
BlockKind::Sunflower,
BlockKind::Mushroom,
];
let grasses = [
BlockKind::LongGrass,
BlockKind::MediumGrass,
BlockKind::ShortGrass,
];
Some(Block::new(
if (height * 1271.0).fract() < 0.15 {
flowers[(height * 0.2) as usize % flowers.len()]
// Underground
if (wposf.z as f32) > alt - 32.0 * chaos {
Some(Block::new(BlockKind::Normal, col))
} else {
grasses[(height * 0.3) as usize % grasses.len()]
},
Rgb::broadcast(0),
))
} else if (wposf.z as f32) < height + 0.9
&& temp > CONFIG.desert_temp
&& (marble * 4423.5).fract() < 0.0005
{
let large_cacti = [BlockKind::LargeCactus, BlockKind::MedFlatCactus];
let small_cacti = [
BlockKind::BarrelCactus,
BlockKind::RoundCactus,
BlockKind::ShortCactus,
BlockKind::ShortFlatCactus,
];
Some(Block::new(
if (height * 1271.0).fract() < 0.5 {
large_cacti[(height * 0.2) as usize % large_cacti.len()]
} else {
small_cacti[(height * 0.3) as usize % small_cacti.len()]
},
Rgb::broadcast(0),
))
} else {
None
};
// Caves
let block = block.and_then(|block| {
// Underground
let cave = cave_xy.powf(2.0)
* (wposf.z as f32 - cave_alt)
.div(40.0)
.powf(4.0)
.neg()
.add(1.0)
> 0.9993;
if cave {
None
} else {
Some(block)
}
});
// Rocks
let block = block.or_else(|| {
if (height + 2.5 - wposf.z as f32).div(7.5).abs().powf(2.0) < rock {
let field0 = RandomField::new(world.sim().seed + 0);
let field1 = RandomField::new(world.sim().seed + 1);
let field2 = RandomField::new(world.sim().seed + 2);
Some(Block::new(BlockKind::Dense, col))
}
} else if (wposf.z as f32) < height {
let col = Lerp::lerp(
sub_surface_color,
surface_color,
(wposf.z as f32 - (height - grass_depth))
.div(grass_depth)
.powf(0.5),
);
// Surface
Some(Block::new(
BlockKind::Normal,
stone_col
- Rgb::new(
field0.get(wpos) as u8 % 16,
field1.get(wpos) as u8 % 16,
field2.get(wpos) as u8 % 16,
),
saturate_srgb(col, 0.45).map(|e| (e * 255.0) as u8),
))
} else if (wposf.z as f32) < height + 0.9
&& temp < CONFIG.desert_temp
&& (wposf.z as f32 > water_height + 3.0)
&& marble > 0.68
&& marble_small > 0.65
&& (marble * 3173.7).fract() < 0.5
{
let flowers = [
BlockKind::BlueFlower,
BlockKind::PinkFlower,
BlockKind::PurpleFlower,
BlockKind::RedFlower,
BlockKind::WhiteFlower,
BlockKind::YellowFlower,
BlockKind::Sunflower,
BlockKind::Mushroom,
];
let grasses = [
BlockKind::LongGrass,
BlockKind::MediumGrass,
BlockKind::ShortGrass,
];
Some(Block::new(
if (height * 1271.0).fract() < 0.15 {
flowers[(height * 0.2) as usize % flowers.len()]
} else {
grasses[(height * 0.3) as usize % grasses.len()]
},
Rgb::broadcast(0),
))
} else if (wposf.z as f32) < height + 0.9
&& temp > CONFIG.desert_temp
&& (marble * 4423.5).fract() < 0.0005
{
let large_cacti = [BlockKind::LargeCactus, BlockKind::MedFlatCactus];
let small_cacti = [
BlockKind::BarrelCactus,
BlockKind::RoundCactus,
BlockKind::ShortCactus,
BlockKind::ShortFlatCactus,
];
Some(Block::new(
if (height * 1271.0).fract() < 0.5 {
large_cacti[(height * 0.2) as usize % large_cacti.len()]
} else {
small_cacti[(height * 0.3) as usize % small_cacti.len()]
},
Rgb::broadcast(0),
))
} else {
None
}
});
.or_else(|| {
// Rocks
if (height + 2.5 - wposf.z as f32).div(7.5).abs().powf(2.0) < rock {
let field0 = RandomField::new(world.sim().seed + 0);
let field1 = RandomField::new(world.sim().seed + 1);
let field2 = RandomField::new(world.sim().seed + 2);
// Water
let block = block.or_else(|| {
if (wposf.z as f32) < water_height {
// Ocean
Some(water)
} else {
None
}
});
Some(Block::new(
BlockKind::Normal,
stone_col
- Rgb::new(
field0.get(wpos) as u8 % 16,
field1.get(wpos) as u8 % 16,
field2.get(wpos) as u8 % 16,
),
))
} else {
None
}
})
.and_then(|block| {
// Caves
// Underground
let cave = cave_xy.powf(2.0)
* (wposf.z as f32 - cave_alt)
.div(40.0)
.powf(4.0)
.neg()
.add(1.0)
> 0.9993;
if cave {
None
} else {
Some(block)
}
})
.or_else(|| {
// Water
if (wposf.z as f32) < water_height {
// Ocean
Some(water)
} else {
None
}
});
(block, height)
} else {
(None, sample.alt)
};
// Structures (like towns)
let block = chunk
@ -383,7 +393,7 @@ pub struct ZCache<'a> {
}
impl<'a> ZCache<'a> {
pub fn get_z_limits(&self) -> (f32, f32) {
pub fn get_z_limits(&self, block_gen: &mut BlockGen) -> (f32, f32, f32) {
let cave_depth = if self.sample.cave_xy.abs() > 0.9 {
(self.sample.alt - self.sample.cave_alt + 8.0).max(0.0)
} else {
@ -392,8 +402,18 @@ impl<'a> ZCache<'a> {
let min = self.sample.alt - (self.sample.chaos * 48.0 + cave_depth) - 4.0;
let cliff = if self.sample.near_cliffs { 48.0 } else { 0.0 };
let warp = self.sample.chaos * 48.0;
let cliff = BlockGen::get_cliff_height(
&mut block_gen.column_gen,
&mut block_gen.column_cache,
self.wpos.map(|e| e as f32),
&self.sample.close_cliffs,
self.sample.cliff_hill,
32.0,
);
let rocks = if self.sample.rock > 0.0 { 12.0 } else { 0.0 };
let warp = self.sample.chaos * 24.0;
let (structure_min, structure_max) = self
.structures
.iter()
@ -412,8 +432,10 @@ impl<'a> ZCache<'a> {
}
});
let ground_max = (self.sample.alt + 2.0 + warp + rocks).max(cliff);
let min = min + structure_min;
let max = (self.sample.alt + cliff + structure_max + warp + 8.0)
let max = (ground_max + structure_max)
.max(self.sample.water_level)
.max(CONFIG.sea_level + 2.0);
@ -430,17 +452,11 @@ impl<'a> ZCache<'a> {
})
.unwrap_or((min, max));
(min, max)
}
}
let structures_only_min_z = ground_max
.max(self.sample.water_level)
.max(CONFIG.sea_level + 2.0);
impl<'a> SamplerMut<'static> for BlockGen<'a> {
type Index = Vec3<i32>;
type Sample = Option<Block>;
fn get(&mut self, wpos: Vec3<i32>) -> Option<Block> {
let z_cache = self.get_z_cache(wpos.into());
self.get_with_z_cache(wpos, z_cache.as_ref())
(min, structures_only_min_z, max)
}
}

View File

@ -2,7 +2,7 @@ use super::{BlockGen, StructureInfo, StructureMeta};
use crate::{
all::ForestKind,
column::{ColumnGen, ColumnSample},
util::{HashCache, RandomPerm, Sampler, UnitChooser},
util::{RandomPerm, Sampler, SmallCache, UnitChooser},
CONFIG,
};
use common::{assets, terrain::Structure};
@ -17,7 +17,7 @@ static QUIRKY_RAND: RandomPerm = RandomPerm::new(0xA634460F);
pub fn structure_gen<'a>(
column_gen: &ColumnGen<'a>,
column_cache: &mut HashCache<Vec2<i32>, Option<ColumnSample<'a>>>,
column_cache: &mut SmallCache<Option<ColumnSample<'a>>>,
idx: usize,
st_pos: Vec2<i32>,
st_seed: u32,
@ -41,6 +41,7 @@ pub fn structure_gen<'a>(
st_pos.map(|e| e as f32),
&st_sample.close_cliffs,
st_sample.cliff_hill,
0.0,
);
let wheight = st_sample.alt.max(cliff_height);

View File

@ -116,7 +116,7 @@ impl World {
None => continue,
};
let (min_z, max_z) = z_cache.get_z_limits();
let (min_z, only_structures_min_z, max_z) = z_cache.get_z_limits(&mut sampler);
for z in base_z..min_z as i32 {
let _ = chunk.set(Vec3::new(x, y, z), stone);
@ -125,8 +125,11 @@ impl World {
for z in min_z as i32..max_z as i32 {
let lpos = Vec3::new(x, y, z);
let wpos = chunk_block_pos + lpos;
let only_structures = lpos.z >= only_structures_min_z as i32;
if let Some(block) = sampler.get_with_z_cache(wpos, Some(&z_cache)) {
if let Some(block) =
sampler.get_with_z_cache(wpos, Some(&z_cache), only_structures)
{
let _ = chunk.set(lpos, block);
}
}

View File

@ -1,5 +1,5 @@
use super::{RandomField, Sampler};
use std::f32;
use std::{f32, ops::Add};
use vek::*;
pub struct FastNoise {
@ -34,16 +34,19 @@ impl Sampler<'static> for FastNoise {
let v011 = self.noise_at(near_pos + Vec3::new(0, 1, 1));
let v111 = self.noise_at(near_pos + Vec3::new(1, 1, 1));
let factor = pos.map(|e| 0.5 - (e.fract() as f32 * f32::consts::PI).cos() * 0.5);
let factor = pos.map(|e| {
let f = e.fract().add(1.0).fract() as f32;
f.powf(2.0) * (3.0 - 2.0 * f)
});
let x00 = Lerp::lerp(v000, v100, factor.x);
let x10 = Lerp::lerp(v010, v110, factor.x);
let x01 = Lerp::lerp(v001, v101, factor.x);
let x11 = Lerp::lerp(v011, v111, factor.x);
let x00 = v000 + factor.x * (v100 - v000);
let x10 = v010 + factor.x * (v110 - v010);
let x01 = v001 + factor.x * (v101 - v001);
let x11 = v011 + factor.x * (v111 - v011);
let y0 = Lerp::lerp(x00, x10, factor.y);
let y1 = Lerp::lerp(x01, x11, factor.y);
let y0 = x00 + factor.y * (x10 - x00);
let y1 = x01 + factor.y * (x11 - x01);
Lerp::lerp(y0, y1, factor.z) * 2.0 - 1.0
(y0 + factor.z * (y1 - y0)) * 2.0 - 1.0
}
}

View File

@ -4,6 +4,7 @@ pub mod hash_cache;
pub mod random;
pub mod sampler;
pub mod seed_expan;
pub mod small_cache;
pub mod structure;
pub mod unit_chooser;
@ -14,6 +15,7 @@ pub use self::{
hash_cache::HashCache,
random::{RandomField, RandomPerm},
sampler::{Sampler, SamplerMut},
small_cache::SmallCache,
structure::StructureGen2d,
unit_chooser::UnitChooser,
};

View File

@ -0,0 +1,71 @@
use arr_macro::arr;
use vek::*;
fn calc_idx(v: Vec2<i32>) -> usize {
let mut x = v.x as u32;
let mut y = v.y as u32;
x = x.wrapping_mul(0x6eed0e9d);
y = y.wrapping_mul(0x2f72b421);
(x ^ y) as usize
}
const CACHE_LEN: usize = 32;
pub struct SmallCache<V: Default> {
index: [Option<Vec2<i32>>; CACHE_LEN + 9],
data: [V; CACHE_LEN + 9],
random: u32,
}
impl<V: Default> Default for SmallCache<V> {
fn default() -> Self {
Self {
index: [None; CACHE_LEN + 9],
data: arr![V::default(); 41], // TODO: Use CACHE_LEN
random: 1,
}
}
}
impl<V: Default> SmallCache<V> {
pub fn get<F: FnOnce(Vec2<i32>) -> V>(&mut self, key: Vec2<i32>, f: F) -> &V {
let idx = calc_idx(key) % CACHE_LEN;
// Search
if self.index[idx].as_ref().map(|k| k == &key).unwrap_or(false) {
return &self.data[idx];
} else if self.index[idx + 1]
.as_ref()
.map(|k| k == &key)
.unwrap_or(false)
{
return &self.data[idx + 1];
} else if self.index[idx + 4]
.as_ref()
.map(|k| k == &key)
.unwrap_or(false)
{
return &self.data[idx + 4];
} else if self.index[idx + 9]
.as_ref()
.map(|k| k == &key)
.unwrap_or(false)
{
return &self.data[idx + 9];
}
// Not present so insert
for i in 0..4 {
let idx = idx + i * i;
if self.index[idx].is_none() {
self.index[idx] = Some(key.clone());
self.data[idx] = f(key);
return &self.data[idx];
}
}
// No space randomly remove someone
let step = super::seed_expan::diffuse(self.random) as usize % 4;
let idx = step * step + idx;
self.random = self.random.wrapping_add(1);
self.index[idx] = Some(key.clone());
self.data[idx] = f(key);
&self.data[idx]
}
}