use std::alloc::{alloc_zeroed, dealloc, handle_alloc_error, Layout};
use std::cmp::max;
use std::marker::PhantomData;
use std::ops::{Index, IndexMut};
use std::ptr::NonNull;
use std::sync::Mutex;
use std::{mem, ptr, usize};
pub struct Subject<T> {
layout: Mutex<SubjectLayout>,
_phantom: PhantomData<fn(T)>,
}
struct SubjectLayout {
size: usize,
align: usize,
init_words: Vec<InitWord>,
drop_props: Vec<DropProperty>,
}
struct InitWord {
offset: usize,
in_use: usize,
}
struct DropProperty {
offset: usize,
init_bit_offset: usize,
drop: unsafe fn(NonNull<u8>),
}
struct PropertyInfo {
offset: usize,
init_bit_offset: usize,
}
impl<T> Subject<T> {
pub fn new() -> Self {
Subject {
layout: Mutex::new(SubjectLayout {
size: 0,
align: 1,
init_words: Vec::new(),
drop_props: Vec::new(),
}),
_phantom: PhantomData,
}
}
pub fn new_prop<'a, P, I: Init<T, P>>(&'a self, initer: I) -> Property<'a, T, P, I> {
let info = self.alloc_prop::<P>();
return Property {
subject: self,
offset: info.offset,
init_bit_offset: info.init_bit_offset,
initer,
_phantom: PhantomData,
};
}
pub fn new_prop_default_init<'a, P: Default>(&'a self) -> DefaultInitProperty<'a, T, P> {
self.new_prop(DefaultInit)
}
pub fn new_prop_const_init<'a, P: Clone>(&'a self, value: P) -> ConstInitProperty<'a, T, P> {
self.new_prop(ConstInit { value })
}
pub fn new_prop_fn_init<'a, P, F: Fn(&Extended<T>) -> P>(
&'a self,
init_fn: F,
) -> FnInitProperty<'a, T, P, F> {
self.new_prop(FnInit { init_fn })
}
fn pin_layout(&self) -> Layout {
let guard = self.layout.lock().unwrap();
unsafe {
return Layout::from_size_align_unchecked(guard.size, guard.align);
}
}
fn alloc_prop<P>(&self) -> PropertyInfo {
let mut layout = self.layout.lock().unwrap();
return layout.alloc_prop::<P>();
}
fn free_prop<P>(&self, offset: usize) {
if !mem::needs_drop::<P>() {
let mut layout = self.layout.lock().unwrap();
return layout.free_nodrop_prop(offset);
}
}
}
impl SubjectLayout {
fn alloc_prop<P>(&mut self) -> PropertyInfo {
let init_bit_offset = self.alloc_init_bit();
let offset = self.alloc::<P>();
if mem::needs_drop::<P>() {
let drop = Self::drop_option_in_place::<P>;
self.drop_props.push(DropProperty {
offset,
init_bit_offset,
drop,
});
}
return PropertyInfo {
offset,
init_bit_offset,
};
}
unsafe fn drop_option_in_place<P>(ptr: NonNull<u8>) {
ptr::drop_in_place(ptr.cast::<Option<P>>().as_ptr());
}
fn alloc_init_bit(&mut self) -> usize {
for init_word in self.init_words.iter_mut() {
if init_word.in_use != usize::MAX {
let bit = init_word.in_use.trailing_ones() as usize;
init_word.in_use |= 1 << bit;
return init_word.offset * 8 + bit;
}
}
let offset = self.alloc::<usize>();
let mut in_use = 0;
let bit = 0;
in_use |= 1 << bit;
self.init_words.push(InitWord { offset, in_use });
return offset * 8 + bit;
}
fn alloc<P>(&mut self) -> usize {
self.alloc_raw(mem::size_of::<P>(), mem::align_of::<P>())
}
fn alloc_raw(&mut self, size: usize, align: usize) -> usize {
let offset = (self.size + align - 1) & !(align - 1);
self.size = offset + size;
self.align = max(self.align, align);
return offset;
}
fn free_nodrop_prop(&mut self, _offset: usize) {
}
}
pub struct Property<'a, T, P, I: 'a + Init<T, P>> {
subject: &'a Subject<T>,
offset: usize,
init_bit_offset: usize,
initer: I,
_phantom: PhantomData<fn() -> P>,
}
pub type DefaultInitProperty<'a, T, P> = Property<'a, T, P, DefaultInit>;
pub type ConstInitProperty<'a, T, P> = Property<'a, T, P, ConstInit<P>>;
pub type FnInitProperty<'a, T, P, F> = Property<'a, T, P, FnInit<F>>;
pub type DynInitProperty<'a, T, P> = Property<'a, T, P, DynInit<'a, T, P>>;
impl<'a, T, P, I: Init<T, P>> Property<'a, T, P, I> {
pub fn subject(&self) -> &Subject<T> {
self.subject
}
}
impl<'a, T, P, I: Init<T, P> + Sync> Property<'a, T, P, I> {
pub fn into_dyn_init(self) -> DynInitProperty<'a, T, P> {
unsafe {
let result = Property {
subject: self.subject,
offset: self.offset,
init_bit_offset: self.init_bit_offset,
initer: Box::new(ptr::read(&self.initer)) as DynInit<'a, T, P>,
_phantom: PhantomData
};
mem::forget(self);
return result;
}
}
}
impl<'a, T, P, I: Init<T, P>> Drop for Property<'a, T, P, I> {
fn drop(&mut self) {
self.subject.free_prop::<P>(self.offset);
}
}
pub trait Init<T, P> {
fn init(&self, obj: &Extended<T>) -> P;
}
pub struct DefaultInit;
pub struct ConstInit<P: Clone> {
pub value: P,
}
pub struct FnInit<F> {
pub init_fn: F,
}
pub type DynInit<'a, T, P> = Box<dyn 'a + Sync + Init<T, P>>;
impl<T, P, F: Fn(&Extended<T>) -> P> Init<T, P> for FnInit<F> {
fn init(&self, obj: &Extended<T>) -> P {
(self.init_fn)(obj)
}
}
impl<T, P: Clone> Init<T, P> for ConstInit<P> {
fn init(&self, _obj: &Extended<T>) -> P {
self.value.clone()
}
}
impl<T, P: Default> Init<T, P> for DefaultInit {
fn init(&self, _obj: &Extended<T>) -> P {
Default::default()
}
}
impl<'a, T, P> Init<T, P> for DynInit<'a, T, P> {
fn init(&self, obj: &Extended<T>) -> P {
self.as_ref().init(obj)
}
}
pub struct Extended<'a, T> {
pub value: T,
subject: &'a Subject<T>,
data: Mutex<ExtendedData>,
}
pub type Dynamic<'a> = Extended<'a, ()>;
impl<'a, T> Extended<'a, T> {
pub fn new_extend(value: T, subject: &'a Subject<T>) -> Self {
Extended {
value,
subject,
data: Mutex::new(ExtendedData::new(subject.pin_layout())),
}
}
pub fn subject(&self) -> &Subject<T> {
self.subject
}
fn index_raw<P, I: Init<T, P>>(&self, index: &Property<'a, T, P, I>) -> NonNull<P> {
if (self.subject as *const Subject<T>) != (index.subject as *const Subject<T>) {
panic!("Subject mismatch");
}
let get_data_layout = || self.subject.pin_layout();
let init_word_offset = (index.init_bit_offset / 8) & !(mem::align_of::<usize>() - 1);
let init_word_bit = index.init_bit_offset - (init_word_offset * 8);
unsafe {
let mut data = self.data.lock().unwrap();
let init_word = data
.get_ptr(get_data_layout, init_word_offset)
.cast::<usize>()
.as_mut();
let value_ptr = data.get_ptr(get_data_layout, index.offset).cast::<P>();
drop(data);
let init_bit = (*init_word & (1 << init_word_bit)) != 0;
if !init_bit {
let init_value = index.initer.init(&self);
let data = self.data.lock().unwrap();
let init_bit = (*init_word & (1 << init_word_bit)) != 0;
if !init_bit {
ptr::write(value_ptr.as_ptr(), init_value);
*init_word |= 1 << init_word_bit;
}
drop(data);
}
return value_ptr;
}
}
}
impl<'a> Dynamic<'a> {
pub fn new(subject: &'a Subject<()>) -> Self {
Self::new_extend((), subject)
}
}
impl<'a, 'b, T, P, I: Init<T, P>> Index<&'b Property<'a, T, P, I>> for Extended<'b, T> {
type Output = P;
fn index(&self, index: &Property<'a, T, P, I>) -> &Self::Output {
unsafe {
return &(*self.index_raw(index).as_ref());
}
}
}
impl<'a, 'b, T, P, I: Init<T, P>> IndexMut<&'b Property<'a, T, P, I>> for Extended<'b, T> {
fn index_mut(&mut self, index: &Property<'a, T, P, I>) -> &mut Self::Output {
unsafe {
return &mut (*self.index_raw(index).as_mut());
}
}
}
impl<'a, T> Drop for Extended<'a, T> {
fn drop(&mut self) {
let mut data = self.data.lock().unwrap();
let layout = self.subject.layout.lock().unwrap();
for prop in layout.drop_props.iter() {
let get_data_layout = || self.subject.pin_layout();
let init_word_offset = (prop.init_bit_offset / 8) & !(mem::align_of::<usize>() - 1);
let init_word_bit = prop.init_bit_offset - (init_word_offset * 8);
unsafe {
let init_word = data
.get_ptr(get_data_layout, init_word_offset)
.cast::<usize>()
.as_mut();
let init_bit = (*init_word & (1 << init_word_bit)) != 0;
if init_bit {
let value_ptr = data.get_ptr(get_data_layout, prop.offset);
(prop.drop)(value_ptr);
}
}
}
}
}
struct ExtendedData {
head_chunk: Chunk,
overflow_chunks: Vec<Chunk>,
}
struct Chunk {
ptr: NonNull<u8>,
layout: Layout,
data_end: usize,
}
impl ExtendedData {
fn new(head_layout: Layout) -> Self {
ExtendedData {
head_chunk: Chunk::new(head_layout, head_layout.size()),
overflow_chunks: Vec::new(),
}
}
fn get_ptr(&mut self, get_data_layout: impl FnOnce() -> Layout, offset: usize) -> NonNull<u8> {
if offset < self.head_chunk.data_end {
unsafe {
return NonNull::new_unchecked(self.head_chunk.ptr.as_ptr().add(offset));
}
}
let mut overflow_data_end = self.head_chunk.data_end;
match self.overflow_chunks.last() {
Some(last_overflow_chunk) => {
overflow_data_end = last_overflow_chunk.data_end;
if offset < last_overflow_chunk.data_end {
let mut lo_chunk_index = 0;
let mut hi_chunk_index = self.overflow_chunks.len() - 1;
let mut chunk_data_start = self.head_chunk.data_end;
loop {
if !(lo_chunk_index < hi_chunk_index) {
break;
}
let mid_chunk_index = (lo_chunk_index + hi_chunk_index) / 2;
let mid_chunk = &self.overflow_chunks[mid_chunk_index];
if offset < mid_chunk.data_end {
hi_chunk_index = mid_chunk_index;
} else {
chunk_data_start = mid_chunk.data_end;
lo_chunk_index = mid_chunk_index + 1;
}
}
unsafe {
let overflow_chunk = &self.overflow_chunks[lo_chunk_index];
return NonNull::new_unchecked(
overflow_chunk
.ptr
.as_ptr()
.sub(chunk_data_start)
.add(offset),
);
}
}
}
_ => {}
}
let data_layout = get_data_layout();
let new_data_end = data_layout.size();
assert!(offset < new_data_end);
let chunk_layout =
Layout::from_size_align(new_data_end - overflow_data_end, data_layout.align()).unwrap();
let overflow_chunk = Chunk::new(chunk_layout, new_data_end);
let result = unsafe {
NonNull::new_unchecked(
overflow_chunk
.ptr
.as_ptr()
.sub(overflow_data_end)
.add(offset),
)
};
self.overflow_chunks.push(overflow_chunk);
return result;
}
}
impl Chunk {
fn new(layout: Layout, data_end: usize) -> Self {
let ptr = if layout.size() > 0 {
unsafe {
match NonNull::new(alloc_zeroed(layout)) {
Some(ptr) => ptr,
None => handle_alloc_error(layout),
}
}
} else {
NonNull::dangling()
};
Chunk {
ptr,
layout,
data_end,
}
}
}
impl Drop for Chunk {
fn drop(&mut self) {
if self.layout.size() > 0 {
unsafe {
dealloc(self.ptr.as_ptr(), self.layout);
}
}
}
}
#[cfg(test)]
mod tests;