
pub mod global;
pub mod null;

#[derive(Clone, Debug, Default)]
pub struct PerIterationAllocProfile {
    pub alloc_calls: f64,
    pub dealloc_calls: f64,
    pub alloc_zeroed_calls: f64,
    pub realloc_calls: f64,
}

#[derive(Clone, Copy, Debug, Default, derive_more::AddAssign, derive_more::Sub)]
pub struct AllocProfile {
    pub iterations_count: u64,
    pub alloc_calls: u64,
    pub dealloc_calls: u64,
    pub alloc_zeroed_calls: u64,
    pub realloc_calls: u64,
}

impl AllocProfile {
    pub fn per_iteration(&self) -> PerIterationAllocProfile {
        let &AllocProfile {
            iterations_count,
            alloc_calls,
            dealloc_calls,
            alloc_zeroed_calls,
            realloc_calls,
        } = self;
        let iterations_count = iterations_count as f64;
        PerIterationAllocProfile {
            alloc_calls: alloc_calls as f64 / iterations_count,
            dealloc_calls: dealloc_calls as f64 / iterations_count,
            alloc_zeroed_calls: alloc_zeroed_calls as f64 / iterations_count,
            realloc_calls: realloc_calls as f64 / iterations_count,
        }
    }
}

#[derive(Debug)]
pub struct AllocProfileGuard<'a, A: AllocProfiler + ?Sized> {
    initial_profile: AllocProfile,
    total_profile: &'a mut AllocProfile,
    alloc_profiler: &'a A,
}

impl<'a, A: AllocProfiler> AllocProfileGuard<'a, A> {
    pub fn new(alloc_profiler: &'a A, total_profile: &'a mut AllocProfile) -> Self {
        let initial_profile = alloc_profiler.get_current_profile();
        AllocProfileGuard {
            initial_profile,
            total_profile,
            alloc_profiler,
        }
    }
}

impl<'a, A: AllocProfiler + ?Sized> Drop for AllocProfileGuard<'a, A> {
    fn drop(&mut self) {
        let final_profile = self.alloc_profiler.get_current_profile();
        let diff = final_profile - self.initial_profile;
        assert_eq!(diff.iterations_count, 0);
        *self.total_profile += diff;
        self.total_profile.iterations_count += 1;
    }
}

pub trait AllocProfiler {
    fn get_current_profile(&self) -> AllocProfile;

    fn profile_guard<'a>(&'a self, total: &'a mut AllocProfile) -> AllocProfileGuard<'a, Self>;
}

#[cfg(test)]
mod tests {
    // use super::*;

    #[test]
    fn it_works() {
        assert_eq!(4, 4);
    }
}
