Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
glandium committed Apr 19, 2018
0 parents commit 9b56f8b
Show file tree
Hide file tree
Showing 7 changed files with 695 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

/target
**/*.rs.bk
Cargo.lock
20 changes: 20 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[package]
name = "boxext"
version = "0.1.0"
authors = ["Mike Hommey <[email protected]>"]
license = "Apache-2.0/MIT"
description = "Extensions to the `Box` type"
repository = "https://github.com/glandium/boxext"
keywords = ["box", "allocator"]

[features]
default = ["std"]
std = []
unstable-rust = []

[dependencies]
static_assertions = "0.2"
allocator_api = { version = "0.2", optional = true }

[dev-dependencies]
boxext_derive = { path = "boxext_derive", version = "0.1" }
15 changes: 15 additions & 0 deletions boxext_derive/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "boxext_derive"
version = "0.1.0"
authors = ["Mike Hommey <[email protected]>"]
license = "Apache-2.0/MIT"
description = "Custom Derive for the `boxext::Zero` type"
repository = "https://github.com/glandium/boxext"
keywords = ["box", "allocator", "derive"]

[dependencies]
syn = ">= 0.12, <0.14"
quote = ">=0.4, <0.6"

[lib]
proc-macro = true
67 changes: 67 additions & 0 deletions boxext_derive/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Copyright 2018 Mike Hommey
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

extern crate proc_macro;

extern crate syn;

#[macro_use]
extern crate quote;

use proc_macro::TokenStream;
use syn::{Data, DeriveInput, Fields};

#[proc_macro_derive(Zero)]
pub fn derive_zero(input: TokenStream) -> TokenStream {
let input: DeriveInput = syn::parse(input).unwrap();

let name = input.ident;

let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();

let mut types = vec![];

match input.data {
Data::Struct(ref data) => {
match data.fields {
Fields::Named(ref fields) => {
for f in &fields.named {
types.push(&f.ty);
}
}
Fields::Unnamed(ref fields) => {
for f in &fields.unnamed {
types.push(&f.ty);
}
}
Fields::Unit => {}
}
}
_ => panic!("Can only derive(Zero) for structs"),
}

let predicates = if let Some(ref w) = where_clause {
w.predicates.iter().collect()
} else {
vec![]
};

let expanded = if predicates.is_empty() && types.is_empty() {
quote! {
unsafe impl #impl_generics ::boxext::Zero for #name #ty_generics {}
}
} else {
quote! {
unsafe impl #impl_generics ::boxext::Zero for #name #ty_generics
where #(#predicates,)* #(#types: ::boxext::Zero,)*
{}
}
};

expanded.into()
}
199 changes: 199 additions & 0 deletions src/allocator_box.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
// Copyright 2018 Mike Hommey
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use core::ptr;
use allocator_api::{Alloc, Box, RawVec};
use {BoxExt, Zero};

/// Extensions to the `allocator_api::Box` type
pub trait BoxInExt<A: Alloc> {
/// Type contained inside the `Box`.
type Inner;

/// Allocates memory in the given allocator and then places the result of
/// `f` into it.
///
/// This doesn't actually allocate if `Self::Inner` is zero-sized.
///
/// When building with optimization enabled, this is expected to avoid
/// copies, contrary to `Box::new_in`.
///
/// # Examples
///
/// ```
/// extern crate allocator_api;
/// extern crate boxext;
/// use allocator_api::Box;
/// use boxext::BoxInExt;
/// # include!("dummy.rs");
///
/// struct Foo(usize, usize);
///
/// impl Foo {
/// fn new(a: usize, b: usize) -> Self {
/// Foo(a, b)
/// }
/// }
///
/// fn main() {
/// // equivalent to `Box::new_in(Foo(1, 2), MyHeap)`
/// let buf = Box::new_in_with(|| Foo(1, 2), MyHeap);
///
/// // equivalent to `Box::new_in(Foo::new(2, 3), MyHeap)`
/// let buf = Box::new_in_with(|| Foo::new(2, 3), MyHeap);
/// }
/// ```
fn new_in_with<F: FnOnce() -> Self::Inner>(f: F, a: A) -> Self;

/// Allocates zeroed memory in the given allocator.
///
/// This doesn't actually allocate if `Self::Inner` is zero-sized.
///
/// This will get zeroed memory directly from the allocator.
///
/// # Example
///
/// ```
/// extern crate allocator_api;
/// extern crate boxext;
/// use allocator_api::Box;
/// use boxext::BoxInExt;
/// # include!("dummy.rs");
///
/// fn main() {
/// // equivalent to `Box::new_in([0usize; 64], MyHeap)`
/// let buf: Box<[usize; 64], _> = Box::new_zeroed_in(MyHeap);
/// }
/// ```
///
/// # Safety
///
/// This method is only assumed safe for `Self::Inner` types implementing
/// the [`Zero`] trait, and not available otherwise. See the definition
/// of that trait.
///
/// [`Zero`]: trait.Zero.html
fn new_zeroed_in(a: A) -> Self
where
Self: Sized,
Self::Inner: Zero;
}

impl<T, A: Alloc + Clone> BoxInExt<A> for Box<T, A> {
type Inner = T;

#[inline]
fn new_in_with<F: FnOnce() -> Self::Inner>(f: F, a: A) -> Self {
unsafe {
let v = RawVec::<T, A>::with_capacity_in(1, a.clone());
let raw = Box::into_raw(v.into_box()) as *mut T;
ptr::write(raw, f());
Box::from_raw_in(raw, a)
}
}

#[inline]
fn new_zeroed_in(a: A) -> Self
where
T: Zero,
{
unsafe {
let v = RawVec::<T, A>::with_capacity_zeroed_in(1, a.clone());
let raw = Box::into_raw(v.into_box());
Box::from_raw_in(raw as *mut T, a)
}
}
}

impl<T, A: Alloc + Clone + Default> BoxExt for Box<T, A> {
type Inner = <Self as BoxInExt<A>>::Inner;

/// Allocates memory in the given allocator and then places the result of
/// `f` into it.
///
/// This doesn't actually allocate if `Self::Inner` is zero-sized.
///
/// When building with optimization enabled, this is expected to avoid
/// copies, contrary to `allocator_api::Box::new_in`.
///
/// # Examples
///
/// ```
/// extern crate allocator_api;
/// extern crate boxext;
/// use allocator_api::Box;
/// use boxext::BoxExt;
/// # include!("dummy.rs");
///
/// struct Foo(usize, usize);
///
/// impl Foo {
/// fn new(a: usize, b: usize) -> Self {
/// Foo(a, b)
/// }
/// }
///
/// fn main() {
/// // equivalent to `Box::new_in(Foo(1, 2), MyHeap)`
/// let buf: Box<_, MyHeap> = Box::new_with(|| Foo(1, 2));
///
/// // equivalent to `Box::new_in(Foo::new(2, 3), MyHeap)`
/// let buf: Box<_, MyHeap> = Box::new_with(|| Foo::new(2, 3));
/// }
/// ```
///
/// This is a convenience wrapper around [`allocator_api::Box::new_in_with`]
/// when the allocator implements `Default`.
///
/// [`allocator_api::Box::new_in_with`]: trait.BoxInExt.html#tymethod.new_in_with
#[inline]
fn new_with<F: FnOnce() -> Self::Inner>(f: F) -> Self {
BoxInExt::new_in_with(f, Default::default())
}

/// Allocates zeroed memory in the given allocator.
///
/// This doesn't actually allocate if `Self::Inner` is zero-sized.
///
/// This will get zeroed memory directly from it.
///
/// # Example
///
/// ```
/// extern crate allocator_api;
/// extern crate boxext;
/// use allocator_api::Box;
/// use boxext::BoxExt;
/// # include!("dummy.rs");
///
/// fn main() {
/// // equivalent to `Box::new_in([0usize; 64], MyHeap)`
/// let buf: Box<[usize; 64], MyHeap> = Box::new_zeroed();
/// }
/// ```
///
/// This is a convenience wrapper around [`allocator_api::Box::new_zeroed_in`]
/// when the allocator implements `Default`.
///
/// [`allocator_api::Box::new_zeroed_in`]: trait.BoxInExt.html#tymethod.new_zeroed_in
///
/// # Safety
///
/// This method is only assumed safe for `Self::Inner` types implementing
/// the [`Zero`] trait, and not available otherwise. See the definition
/// of that trait.
///
/// [`Zero`]: trait.Zero.html
#[inline]
fn new_zeroed() -> Self
where
T: Zero,
{
BoxInExt::new_zeroed_in(Default::default())
}
}
34 changes: 34 additions & 0 deletions src/dummy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/// A dummy allocator for tests.
mod dummy {
extern crate core;
use super::allocator_api::{Alloc, AllocErr, Layout, Opaque};
use self::core::ptr::NonNull;

#[derive(Clone, Default)]
pub struct MyHeap;

static mut HEAP_BUF: [u8; 4096] = [0; 4096];
static mut HEAP_CURSOR: usize = 0;

unsafe impl<'a> Alloc for MyHeap {
unsafe fn alloc(&mut self, layout: Layout) -> Result<NonNull<Opaque>, AllocErr> {
let ptr = HEAP_BUF.as_ptr() as usize;
let mut start = HEAP_CURSOR;
let modulo = (ptr + start) & (layout.align() - 1);
if modulo != 0 {
start += layout.align() - modulo;
}
assert_eq!((ptr + start) & (layout.align() - 1), 0);
let end = start + layout.size();
let buf = HEAP_BUF.get_mut(start..end);
HEAP_CURSOR = end;
buf.map(|b| NonNull::new_unchecked(b as *mut [u8] as *mut u8 as *mut Opaque))
.ok_or_else(|| AllocErr)
}
unsafe fn dealloc(&mut self, _ptr: NonNull<Opaque>, _layout: Layout) {}
}

}

use self::dummy::MyHeap;
Loading

0 comments on commit 9b56f8b

Please sign in to comment.