Skip to content

Commit 2010379

Browse files
committed
Switch VecStorage to MaybeUninit and add slice access
1 parent c1d8a96 commit 2010379

File tree

5 files changed

+69
-16
lines changed

5 files changed

+69
-16
lines changed

.travis.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ language: rust
22

33
rust:
44
- nightly
5-
- 1.34.0
5+
- 1.36.0
66
- stable
77

88
cache:
@@ -22,7 +22,7 @@ script:
2222
cargo build --all-features --verbose;
2323
cargo test --all-features --verbose --no-run;
2424
cargo bench --verbose --no-run --all-features;
25-
elif [ "$TRAVIS_RUST_VERSION" == "1.34.0" ]; then
25+
elif [ "$TRAVIS_RUST_VERSION" == "1.36.0" ]; then
2626
cargo check --tests --no-default-features;
2727
cargo check --tests --no-default-features --features "parallel";
2828
cargo check --tests --no-default-features --features "serde";

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ Unlike most other ECS libraries out there, it provides
3535
other and you can use barriers to force several stages in system execution
3636
* high performance for real-world applications
3737

38-
Minimum Rust version: 1.34
38+
Minimum Rust version: 1.36
3939

4040
## [Link to the book][book]
4141

docs/tutorials/src/05_storages.md

+10-1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ Certain storages provide access to component slices:
5353
|Storage Type | Slice type | Density | Indices |
5454
|:----------------------:|---------------------|---------|---------------|
5555
| [`DenseVecStorage`] | `&[T]` | Dense | Arbitrary |
56+
| [`VecStorage`] | `&[MaybeUninit<T>]` | Sparse | Entity `id()` |
5657
| [`DefaultVecStorage`] | `&[T]` | Sparse | Entity `id()` |
5758

5859
This is intended as an advanced technique. Component slices provide
@@ -102,6 +103,13 @@ Therefore it would be a waste of memory to use this storage for
102103
rare components, but it's best suited for commonly used components
103104
(like transform values).
104105

106+
`VecStorage<T>` provides `as_slice()` and `as_mut_slice()` accessors which
107+
return `&[MaybeUninit<T>]`. Consult the `Storage::mask()` to determine
108+
which indices are populated. Slice indices cannot be converted to `Entity`
109+
values because they lack a generation counter, but they do correspond to
110+
`Entity::id()`s, so indices can be used to collate between multiple
111+
`VecStorage`s.
112+
105113
## `DefaultVecStorage`
106114

107115
This storage works exactly like `VecStorage`, but instead of leaving gaps
@@ -113,4 +121,5 @@ writes than `VecStorage`.
113121
which return `&[T]`. `Storage::mask()` can be used to determine which
114122
indices are in active use, but all indices are fully initialized, so the
115123
`mask()` is not necessary for safety. `DefaultVecStorage` indices all
116-
correspond with each other and with `Entity::id()`s.
124+
correspond with each other, with `VecStorage` indices, and with
125+
`Entity::id()`s.

src/storage/storages.rs

+30-12
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! Different types of storages you can use for your components.
22
33
use std::collections::BTreeMap;
4+
use std::mem::MaybeUninit;
45

56
use derivative::Derivative;
67
use hashbrown::HashMap;
@@ -219,35 +220,53 @@ unsafe impl<T> DistinctStorage for NullStorage<T> {}
219220

220221
/// Vector storage. Uses a simple `Vec`. Supposed to have maximum
221222
/// performance for the components mostly present in entities.
223+
///
224+
/// `as_slice()` and `as_mut_slice()` indices correspond to
225+
/// entity IDs. These can be compared to other `VecStorage`s, to
226+
/// other `DefaultVecStorage`s, and to `Entity::id()`s for live
227+
/// entities.
222228
#[derive(Derivative)]
223229
#[derivative(Default(bound = ""))]
224-
pub struct VecStorage<T>(Vec<T>);
230+
pub struct VecStorage<T>(Vec<MaybeUninit<T>>);
231+
232+
impl<T> SliceAccess<T> for VecStorage<T> {
233+
type Element = MaybeUninit<T>;
234+
235+
#[inline]
236+
fn as_slice(&self) -> &[Self::Element] {
237+
self.0.as_slice()
238+
}
239+
240+
#[inline]
241+
fn as_mut_slice(&mut self) -> &mut [Self::Element] {
242+
self.0.as_mut_slice()
243+
}
244+
}
225245

226246
impl<T> UnprotectedStorage<T> for VecStorage<T> {
227247
unsafe fn clean<B>(&mut self, has: B)
228-
where
229-
B: BitSetLike,
248+
where
249+
B: BitSetLike,
230250
{
231251
use std::ptr;
232252
for (i, v) in self.0.iter_mut().enumerate() {
233253
if has.contains(i as u32) {
234-
ptr::drop_in_place(v);
254+
// drop in place
255+
ptr::drop_in_place(&mut *v.as_mut_ptr());
235256
}
236257
}
237258
self.0.set_len(0);
238259
}
239260

240261
unsafe fn get(&self, id: Index) -> &T {
241-
self.0.get_unchecked(id as usize)
262+
&*self.0.get_unchecked(id as usize).as_ptr()
242263
}
243264

244265
unsafe fn get_mut(&mut self, id: Index) -> &mut T {
245-
self.0.get_unchecked_mut(id as usize)
266+
&mut *self.0.get_unchecked_mut(id as usize).as_mut_ptr()
246267
}
247268

248269
unsafe fn insert(&mut self, id: Index, v: T) {
249-
use std::ptr;
250-
251270
let id = id as usize;
252271
if self.0.len() <= id {
253272
let delta = id + 1 - self.0.len();
@@ -256,12 +275,11 @@ impl<T> UnprotectedStorage<T> for VecStorage<T> {
256275
}
257276
// Write the value without reading or dropping
258277
// the (currently uninitialized) memory.
259-
ptr::write(self.0.get_unchecked_mut(id), v);
278+
*self.0.get_unchecked_mut(id as usize) = MaybeUninit::new(v);
260279
}
261280

262281
unsafe fn remove(&mut self, id: Index) -> T {
263282
use std::ptr;
264-
265283
ptr::read(self.get(id))
266284
}
267285
}
@@ -274,8 +292,8 @@ unsafe impl<T> DistinctStorage for VecStorage<T> {}
274292
/// Requires the component to implement `Default`.
275293
///
276294
/// `as_slice()` and `as_mut_slice()` indices correspond to entity IDs.
277-
/// These can be compared to other `DefaultVecStorage`s, and to
278-
/// `Entity::id()`s for live entities.
295+
/// These can be compared to other `DefaultVecStorage`s, to other
296+
/// `VecStorage`s, and to `Entity::id()`s for live entities.
279297
#[derive(Derivative)]
280298
#[derivative(Default(bound = ""))]
281299
pub struct DefaultVecStorage<T>(Vec<T>);

src/storage/tests.rs

+26
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::any::Any;
33
use super::*;
44
use crate::world::{Component, Entity, Generation, Index, WorldExt};
55
use shred::World;
6+
use std::mem::MaybeUninit;
67

78
fn create<T: Component>(world: &mut World) -> WriteStorage<T>
89
where
@@ -448,6 +449,27 @@ mod test {
448449
}
449450
}
450451

452+
fn test_maybeuninit_slice<T: Component + From<u32> + Debug + Eq>()
453+
where
454+
T::Storage: Default + SliceAccess<T, Element=MaybeUninit<T>>,
455+
{
456+
let mut w = World::new();
457+
let mut s: Storage<T, _> = create(&mut w);
458+
459+
for i in 0..1_000 {
460+
if let Err(err) = s.insert(Entity::new(i, Generation::new(1)), (i + 2718).into()) {
461+
panic!("Failed to insert component into entity! {:?}", err);
462+
}
463+
}
464+
465+
let slice = s.as_slice();
466+
assert_eq!(slice.len(), 1_000);
467+
for (i, v) in slice.iter().enumerate() {
468+
let v = unsafe { &*v.as_ptr() };
469+
assert_eq!(v, &(i as u32 + 2718).into());
470+
}
471+
}
472+
451473
#[test]
452474
fn vec_test_add() {
453475
test_add::<Cvec>();
@@ -480,6 +502,10 @@ mod test {
480502
fn vec_test_anti() {
481503
test_anti::<Cvec>();
482504
}
505+
#[test]
506+
fn vec_test_maybeuninit_slice() {
507+
test_maybeuninit_slice::<Cvec>();
508+
}
483509

484510
#[test]
485511
fn vec_arc() {

0 commit comments

Comments
 (0)