Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend no_std support #280

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,24 @@ rustdoc-args = ["--cfg", "docsrs"]
maintenance = { status = "actively-developed" }

[features]
default = ["std"]
default = ["std", "atomic"]
std = []
atomic = ["spin"]
atomic-polyfill = ["once_cell/atomic-polyfill", "dep:atomic-polyfill"]
# Enables derive(Bundle)
macros = ["hecs-macros", "lazy_static"]
macros = ["hecs-macros", "once_cell"]
# Enables the serialize::column module
column-serialize = ["serde"]
# Enables the serialize::row module
row-serialize = ["serde"]

[dependencies]
atomic-polyfill = { version = "0.1.7", optional = true }
hecs-macros = { path = "macros", version = "0.8.2", optional = true }
hashbrown = { version = "0.12.0", default-features = false, features = ["ahash", "inline-more"] }
lazy_static = { version = "1.4.0", optional = true, features = ["spin_no_std"] }
once_cell = { version = "1.12.0", default-features = false, optional = true, features = ["alloc"] }
serde = { version = "1.0.117", default-features = false, optional = true }
spin = { version = "0.9.2", default-features = false, features = ["mutex", "spin_mutex"] }
spin = { version = "0.9.2", default-features = false, optional = true, features = ["mutex", "spin_mutex"] }

[dev-dependencies]
bencher = "0.1.5"
Expand Down
4 changes: 4 additions & 0 deletions macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ license = "Apache-2.0"
[lib]
proc-macro = true

[features]
default = ["std"]
std = []

[dependencies]
syn = { version = "1.0", default-features = false, features = ["proc-macro", "parsing", "printing", "derive", "clone-impls", "visit-mut"] }
quote = "1.0.3"
Expand Down
51 changes: 24 additions & 27 deletions macros/src/bundle.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use std::borrow::Cow;

use crate::alloc::borrow::Cow;
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use syn::{DeriveInput, Error, Result};
Expand Down Expand Up @@ -45,19 +44,19 @@ fn gen_dynamic_bundle_impl(
::core::option::Option::Some(::core::any::TypeId::of::<Self>())
}

fn with_ids<__hecs__T>(&self, f: impl ::std::ops::FnOnce(&[::std::any::TypeId]) -> __hecs__T) -> __hecs__T {
fn with_ids<__hecs__T>(&self, f: impl ::core::ops::FnOnce(&[::core::any::TypeId]) -> __hecs__T) -> __hecs__T {
<Self as ::hecs::Bundle>::with_static_ids(f)
}

fn type_info(&self) -> ::std::vec::Vec<::hecs::TypeInfo> {
fn type_info(&self) -> ::hecs::alloc::vec::Vec<::hecs::TypeInfo> {
<Self as ::hecs::Bundle>::with_static_type_info(|info| info.to_vec())
}

#[allow(clippy::forget_copy, clippy::forget_non_drop)]
unsafe fn put(mut self, mut f: impl ::std::ops::FnMut(*mut u8, ::hecs::TypeInfo)) {
unsafe fn put(mut self, mut f: impl ::core::ops::FnMut(*mut u8, ::hecs::TypeInfo)) {
#(
f((&mut self.#field_members as *mut #tys).cast::<u8>(), ::hecs::TypeInfo::of::<#tys>());
::std::mem::forget(self.#field_members);
::core::mem::forget(self.#field_members);
)*
}
}
Expand All @@ -75,27 +74,25 @@ fn gen_bundle_impl(
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
let with_static_ids_inner = quote! {
{
let mut tys = [#((::std::mem::align_of::<#tys>(), ::std::any::TypeId::of::<#tys>())),*];
let mut tys = [#((::core::mem::align_of::<#tys>(), ::core::any::TypeId::of::<#tys>())),*];
tys.sort_unstable_by(|x, y| {
::std::cmp::Ord::cmp(&x.0, &y.0)
::core::cmp::Ord::cmp(&x.0, &y.0)
.reverse()
.then(::std::cmp::Ord::cmp(&x.1, &y.1))
.then(::core::cmp::Ord::cmp(&x.1, &y.1))
});
let mut ids = [::std::any::TypeId::of::<()>(); #num_tys];
for (id, info) in ::std::iter::Iterator::zip(ids.iter_mut(), tys.iter()) {
let mut ids = [::core::any::TypeId::of::<()>(); #num_tys];
for (id, info) in ::core::iter::Iterator::zip(ids.iter_mut(), tys.iter()) {
*id = info.1;
}
ids
}
};
let with_static_ids_body = if generics.params.is_empty() {
quote! {
::hecs::lazy_static::lazy_static! {
static ref ELEMENTS: [::std::any::TypeId; #num_tys] = {
#with_static_ids_inner
};
}
f(&*ELEMENTS)
static ELEMENTS: ::hecs::once_cell::race::OnceBox<[::core::any::TypeId; #num_tys]> = ::hecs::once_cell::race::OnceBox::new();
f(ELEMENTS.get_or_init(||
::hecs::alloc::boxed::Box::new(#with_static_ids_inner)
))
}
} else {
quote! {
Expand All @@ -105,27 +102,27 @@ fn gen_bundle_impl(
quote! {
unsafe impl #impl_generics ::hecs::Bundle for #ident #ty_generics #where_clause {
#[allow(non_camel_case_types)]
fn with_static_ids<__hecs__T>(f: impl ::std::ops::FnOnce(&[::std::any::TypeId]) -> __hecs__T) -> __hecs__T {
fn with_static_ids<__hecs__T>(f: impl ::core::ops::FnOnce(&[::core::any::TypeId]) -> __hecs__T) -> __hecs__T {
#with_static_ids_body
}

#[allow(non_camel_case_types)]
fn with_static_type_info<__hecs__T>(f: impl ::std::ops::FnOnce(&[::hecs::TypeInfo]) -> __hecs__T) -> __hecs__T {
fn with_static_type_info<__hecs__T>(f: impl ::core::ops::FnOnce(&[::hecs::TypeInfo]) -> __hecs__T) -> __hecs__T {
let mut info: [::hecs::TypeInfo; #num_tys] = [#(::hecs::TypeInfo::of::<#tys>()),*];
info.sort_unstable();
f(&info)
}

unsafe fn get(
mut f: impl ::std::ops::FnMut(::hecs::TypeInfo) -> ::std::option::Option<::std::ptr::NonNull<u8>>,
) -> ::std::result::Result<Self, ::hecs::MissingComponent> {
mut f: impl ::core::ops::FnMut(::hecs::TypeInfo) -> ::core::option::Option<::core::ptr::NonNull<u8>>,
) -> ::core::result::Result<Self, ::hecs::MissingComponent> {
#(
let #field_idents = f(::hecs::TypeInfo::of::<#tys>())
.ok_or_else(::hecs::MissingComponent::new::<#tys>)?
.cast::<#tys>()
.as_ptr();
)*
::std::result::Result::Ok(Self { #( #field_members: #field_idents.read(), )* })
::core::result::Result::Ok(Self { #( #field_members: #field_idents.read(), )* })
}
}
}
Expand All @@ -137,14 +134,14 @@ fn gen_unit_struct_bundle_impl(ident: syn::Ident, generics: &syn::Generics) -> T
quote! {
unsafe impl #impl_generics ::hecs::Bundle for #ident #ty_generics #where_clause {
#[allow(non_camel_case_types)]
fn with_static_ids<__hecs__T>(f: impl ::std::ops::FnOnce(&[::std::any::TypeId]) -> __hecs__T) -> __hecs__T { f(&[]) }
fn with_static_ids<__hecs__T>(f: impl ::core::ops::FnOnce(&[::core::any::TypeId]) -> __hecs__T) -> __hecs__T { f(&[]) }
#[allow(non_camel_case_types)]
fn with_static_type_info<__hecs__T>(f: impl ::std::ops::FnOnce(&[::hecs::TypeInfo]) -> __hecs__T) -> __hecs__T { f(&[]) }
fn with_static_type_info<__hecs__T>(f: impl ::core::ops::FnOnce(&[::hecs::TypeInfo]) -> __hecs__T) -> __hecs__T { f(&[]) }

unsafe fn get(
mut f: impl ::std::ops::FnMut(::hecs::TypeInfo) -> ::std::option::Option<::std::ptr::NonNull<u8>>,
) -> ::std::result::Result<Self, ::hecs::MissingComponent> {
::std::result::Result::Ok(Self {/* for some reason this works for all unit struct variations */})
mut f: impl ::core::ops::FnMut(::hecs::TypeInfo) -> ::core::option::Option<::core::ptr::NonNull<u8>>,
) -> ::core::result::Result<Self, ::hecs::MissingComponent> {
::core::result::Result::Ok(Self {/* for some reason this works for all unit struct variations */})
}
}
}
Expand Down
6 changes: 3 additions & 3 deletions macros/src/bundle_clone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,14 @@ fn gen_dynamic_bundle_impl(
quote! {
unsafe impl #impl_generics ::hecs::DynamicBundleClone for #ident #ty_generics #where_clause {
#[allow(clippy::forget_copy)]
unsafe fn put_with_clone(mut self, mut f: impl ::std::ops::FnMut(*mut u8, ::hecs::TypeInfo, ::hecs::DynamicClone)) {
unsafe fn put_with_clone(mut self, mut f: impl ::core::ops::FnMut(*mut u8, ::hecs::TypeInfo, ::hecs::DynamicClone)) {
#(
f(
(&mut self.#field_members as *mut #tys).cast::<u8>(),
::hecs::TypeInfo::of::<#tys>(),
::hecs::DynamicClone::new::<#tys>()
);
::std::mem::forget(self.#field_members);
::core::mem::forget(self.#field_members);
)*
}
}
Expand All @@ -51,7 +51,7 @@ fn make_component_trait_bound() -> syn::TraitBound {
paren_token: None,
modifier: syn::TraitBoundModifier::None,
lifetimes: None,
path: syn::parse_quote!(::std::clone::Clone),
path: syn::parse_quote!(::core::clone::Clone),
}
}

Expand Down
2 changes: 1 addition & 1 deletion macros/src/common.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::borrow::Cow;
use crate::alloc::{borrow::Cow, vec::Vec, format};

use proc_macro2::Span;

Expand Down
7 changes: 7 additions & 0 deletions macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

#![no_std]

#[cfg(feature = "std")]
extern crate std;

extern crate alloc;

extern crate proc_macro;

mod bundle;
Expand Down
10 changes: 6 additions & 4 deletions macros/src/query.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use alloc::format;
use alloc::vec::Vec;
use proc_macro2::Span;
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
Expand Down Expand Up @@ -125,12 +127,12 @@ pub fn derive(input: DeriveInput) -> Result<TokenStream2> {
}

#[allow(unused_variables, unused_mut)]
fn access(archetype: &::hecs::Archetype) -> ::std::option::Option<::hecs::Access> {
fn access(archetype: &::hecs::Archetype) -> ::core::option::Option<::hecs::Access> {
let mut access = ::hecs::Access::Iterate;
#(
access = ::core::cmp::max(access, #fetches::access(archetype)?);
)*
::std::option::Option::Some(access)
::core::option::Option::Some(access)
}

#[allow(unused_variables)]
Expand All @@ -139,8 +141,8 @@ pub fn derive(input: DeriveInput) -> Result<TokenStream2> {
}

#[allow(unused_variables)]
fn prepare(archetype: &::hecs::Archetype) -> ::std::option::Option<Self::State> {
::std::option::Option::Some(#state_ident {
fn prepare(archetype: &::hecs::Archetype) -> ::core::option::Option<Self::State> {
::core::option::Option::Some(#state_ident {
#(
#fields: #fetches::prepare(archetype)?,
)*
Expand Down
2 changes: 1 addition & 1 deletion src/borrow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

use core::sync::atomic::{AtomicUsize, Ordering};
use crate::atomic::{AtomicUsize, Ordering};

/// A bit mask used to signal the `AtomicBorrow` has an active mutable borrow.
const UNIQUE_BIT: usize = !(usize::max_value() >> 1);
Expand Down
3 changes: 2 additions & 1 deletion src/entities.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ use core::convert::TryFrom;
use core::iter::ExactSizeIterator;
use core::num::{NonZeroU32, NonZeroU64};
use core::ops::Range;
use core::sync::atomic::{AtomicIsize, Ordering};

use crate::atomic::{AtomicIsize, Ordering};
use core::{fmt, mem};
#[cfg(feature = "std")]
use std::error::Error;
Expand Down
1 change: 0 additions & 1 deletion src/entity_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,6 @@ unsafe impl DynamicBundle for BuiltEntity<'_> {
f(&self.builder.ids)
}

#[doc(hidden)]
fn type_info(&self) -> Vec<TypeInfo> {
self.builder.info.iter().map(|x| x.0).collect()
}
Expand Down
11 changes: 9 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
#[cfg(feature = "std")]
extern crate std;

extern crate alloc;
pub extern crate alloc;

/// Imagine macro parameters, but more like those Russian dolls.
///
Expand Down Expand Up @@ -97,10 +97,17 @@ pub use archetype::TypeInfo;
pub use bundle::DynamicClone;
#[cfg(feature = "macros")]
#[doc(hidden)]
pub use lazy_static;
pub use once_cell;
#[doc(hidden)]
pub use query::Fetch;

#[cfg(feature = "atomic-polyfill")]
#[doc(hidden)]
pub use atomic_polyfill as atomic;
#[cfg(not(feature = "atomic-polyfill"))]
#[doc(hidden)]
pub use core::sync::atomic;

#[cfg(feature = "macros")]
pub use hecs_macros::{Bundle, DynamicBundleClone, Query};

Expand Down
16 changes: 16 additions & 0 deletions src/world.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ use core::any::TypeId;
use core::borrow::Borrow;
use core::convert::TryFrom;
use core::hash::{BuildHasherDefault, Hasher};

#[cfg(feature = "atomic")]
use spin::Mutex;

use core::{fmt, ptr};
Expand Down Expand Up @@ -61,6 +63,7 @@ pub struct World {

impl World {
/// Create an empty world
#[cfg(feature = "atomic")]
pub fn new() -> Self {
// AtomicU64 is unsupported on 32-bit MIPS and PPC architectures
// For compatibility, use Mutex<u64>
Expand All @@ -71,6 +74,17 @@ impl World {
*id = next;
next
};
Self::new_impl(id)
}

/// Create an empty world
#[cfg(not(feature = "atomic"))]
pub fn new_with_id(id: u64) -> Self {
Self::new_impl(id)
}

/// Create an empty world
fn new_impl(id: u64) -> Self {
Self {
entities: Entities::default(),
archetypes: ArchetypeSet::new(),
Expand Down Expand Up @@ -878,6 +892,7 @@ impl World {
unsafe impl Send for World {}
unsafe impl Sync for World {}

#[cfg(feature = "atomic")]
impl Default for World {
fn default() -> Self {
Self::new()
Expand Down Expand Up @@ -1046,6 +1061,7 @@ impl<A: DynamicBundle> Extend<A> for World {
}
}

#[cfg(feature = "atomic")]
impl<A: DynamicBundle> core::iter::FromIterator<A> for World {
fn from_iter<I: IntoIterator<Item = A>>(iter: I) -> Self {
let mut world = World::new();
Expand Down