// Copyright (c) Meta Platforms, Inc. and affiliates.
//
// This source code is dual-licensed under either the MIT license found in the
// LICENSE-MIT file in the root directory of this source tree or the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
// of this source tree. You may select, at your option, one of the above-listed licenses.

#![allow(dead_code)]
#![allow(unused_variables)]
#![allow(unused_imports)]

use akd::ecvrf::HardCodedAkdVRF;
use akd::storage::{Database, StorageManager};
use akd::Directory;
use akd_mysql::mysql::AsyncMySqlDatabase;
use clap::{Parser, ValueEnum};
use commands::Command;
use log::{debug, error, info, warn};
use rand::distributions::Alphanumeric;
use rand::rngs::StdRng;
use rand::{Rng, SeedableRng};
use std::convert::From;
use std::io::*;
use std::marker::PhantomData;
use std::str::FromStr;
use std::time::{Duration, Instant};
use tokio::sync::mpsc::*;
use tokio::time::timeout;

mod commands;
mod directory_host;
mod logs;

use logs::ConsoleLogger;

#[derive(ValueEnum, Clone, Debug)]
enum PublicLogLevels {
    Error,
    Warn,
    Info,
    Debug,
    Trace,
}

impl PublicLogLevels {
    pub(crate) fn to_log_level(&self) -> log::Level {
        match &self {
            PublicLogLevels::Error => log::Level::Error,
            PublicLogLevels::Warn => log::Level::Warn,
            PublicLogLevels::Info => log::Level::Info,
            PublicLogLevels::Debug => log::Level::Debug,
            PublicLogLevels::Trace => log::Level::Trace,
        }
    }
}

/// applicationModes
#[derive(Parser)]
enum OtherMode {
    #[clap(about = "Benchmark publish API")]
    BenchPublish {
        num_users: u64,
        num_updates_per_user: u64,
    },
    #[clap(about = "Benchmark lookup API")]
    BenchLookup {
        num_users: u64,
        num_lookups_per_user: u64,
    },
    #[clap(about = "Benchmark database insertion")]
    BenchDbInsert { num_users: u64 },
    #[clap(about = "Flush data from database tables")]
    Flush,
    #[clap(about = "Drop existing database tables (for schema migration etc.)")]
    Drop,
}

#[derive(Parser)]
struct Cli {
    /// The database implementation to utilize
    #[clap(long = "memory", name = "Use in-memory database")]
    memory_db: bool,

    /// Activate debugging mode
    #[clap(long = "debug", short = 'd', name = "Enable debugging mode")]
    debug: bool,

    #[clap(
        value_enum,
        long = "log_level",
        short = 'l',
        name = "Adjust the console log-level (default = INFO)",
        ignore_case = true,
        default_value = "Info"
    )]
    console_debug: PublicLogLevels,

    #[clap(subcommand)]
    other_mode: Option<OtherMode>,

    #[clap(
        long = "multirow_size",
        short = 'm',
        name = "MySQL multi-row insert size",
        default_value = "100"
    )]
    mysql_insert_depth: usize,
}

// MAIN //
#[tokio::main]
async fn main() {
    ConsoleLogger::touch();

    let cli = Cli::parse();

    // Initialize logging facades
    let mut loggers: Vec<Box<dyn log::Log>> = vec![Box::new(ConsoleLogger {
        level: cli.console_debug.to_log_level(),
    })];

    let level = if cli.debug {
        // File-logging enabled in debug mode
        match logs::FileLogger::new("akd_app.log") {
            Err(err) => println!("Error initializing file logger {err}"),
            Ok(flogger) => loggers.push(Box::new(flogger)),
        }
        // drop the log level to debug (console has a max-level of "Info")
        log::Level::Debug
    } else {
        cli.console_debug.to_log_level()
    };

    if let Err(err) = multi_log::MultiLogger::init(loggers, level) {
        println!("Error initializing multi-logger {err}");
    }

    let (tx, mut rx) = channel(2);

    let vrf = HardCodedAkdVRF {};
    if cli.memory_db {
        let db = akd::storage::memory::AsyncInMemoryDatabase::new();
        let storage_manager = StorageManager::new_no_cache(db);
        let mut directory = Directory::<_, _>::new(storage_manager, vrf).await.unwrap();
        if let Some(()) = pre_process_input(&cli, &tx, None).await {
            return;
        }
        tokio::spawn(async move {
            directory_host::init_host::<_, HardCodedAkdVRF>(&mut rx, &mut directory).await
        });
        process_input(&cli, &tx, None).await;
    } else {
        // MySQL (the default)
        let mysql_db = AsyncMySqlDatabase::new(
            "localhost",
            "default",
            Option::from("root"),
            Option::from("example"),
            Option::from(8001),
            cli.mysql_insert_depth,
        )
        .await
        .expect("Failed to create async mysql db");
        if let Some(()) = pre_process_input(&cli, &tx, Some(&mysql_db)).await {
            return;
        }
        let storage_manager = StorageManager::new(
            mysql_db,
            Some(Duration::from_secs(10 * 60)),
            None,
            Some(Duration::from_secs(15)),
        );
        let mut directory = Directory::<_, _>::new(storage_manager.clone(), vrf)
            .await
            .unwrap();
        tokio::spawn(async move {
            directory_host::init_host::<_, HardCodedAkdVRF>(&mut rx, &mut directory).await
        });
        process_input(&cli, &tx, Some(storage_manager)).await;
    }
}

// Helpers //
// If () is returned, it means the command execution is complete and CLI should
// return
async fn pre_process_input(
    cli: &Cli,
    tx: &Sender<directory_host::Rpc>,
    db: Option<&AsyncMySqlDatabase>,
) -> Option<()> {
    if let Some(OtherMode::Drop) = &cli.other_mode {
        println!("======= Dropping database ======= ");
        if let Some(mysql_db) = db {
            if let Err(error) = mysql_db.drop_tables().await {
                error!("Error dropping database: {}", error);
            } else {
                info!("Database dropped.");
            }
            return Option::from(());
        }
    }
    None
}

async fn process_input(
    cli: &Cli,
    tx: &Sender<directory_host::Rpc>,
    db: Option<StorageManager<AsyncMySqlDatabase>>,
) {
    if let Some(other_mode) = &cli.other_mode {
        match other_mode {
            OtherMode::BenchDbInsert { num_users } => {
                println!("======= Benchmark operation requested ======= ");
                println!("Beginning DB INSERT benchmark of {num_users} users");

                let mut values: Vec<String> = vec![];
                for i in 0..*num_users {
                    values.push(
                        StdRng::seed_from_u64(i)
                            .sample_iter(&Alphanumeric)
                            .take(30)
                            .map(char::from)
                            .collect(),
                    );
                }

                let mut data = Vec::new();
                for value in values.iter() {
                    let state = akd::storage::types::DbRecord::build_user_state(
                        value.as_bytes().to_vec(),
                        value.as_bytes().to_vec(),
                        1u64,
                        1u32,
                        [1u8; 32],
                        1u64,
                    );
                    data.push(akd::storage::types::DbRecord::ValueState(state));
                }

                if let Some(storage) = db {
                    debug!("Starting the storage request");

                    let tic = Instant::now();
                    let len = data.len();
                    assert_eq!(Ok(()), storage.batch_set(data).await);
                    let toc: Duration = Instant::now() - tic;
                    println!("Insert batch of {} items in {} ms", len, toc.as_millis());
                    storage.log_metrics(log::Level::Warn).await;
                } else {
                    error!("Command available with MySQL db's only");
                }
            }
            OtherMode::BenchPublish {
                num_users,
                num_updates_per_user,
            } => {
                println!("======= Benchmark operation requested ======= ");
                println!(
                    "Beginning PUBLISH benchmark of {num_users} users with {num_updates_per_user} updates/user"
                );

                let users: Vec<String> = (1..=*num_users)
                    .map(|i| {
                        StdRng::seed_from_u64(i)
                            .sample_iter(&Alphanumeric)
                            .take(256)
                            .map(char::from)
                            .collect()
                    })
                    .collect();
                let data: Vec<String> = (1..=*num_updates_per_user)
                    .map(|i| {
                        StdRng::seed_from_u64(i)
                            .sample_iter(&Alphanumeric)
                            .take(1024)
                            .map(char::from)
                            .collect()
                    })
                    .collect();

                let tic = Instant::now();

                let mut code = None;
                for value in data {
                    let user_data: Vec<(String, String)> = users
                        .iter()
                        .map(|user| (user.clone(), value.clone()))
                        .collect();
                    let (rpc_tx, rpc_rx) = tokio::sync::oneshot::channel();
                    let rpc = directory_host::Rpc(
                        directory_host::DirectoryCommand::PublishBatch(user_data),
                        Some(rpc_tx),
                    );
                    let sent = tx.clone().send(rpc).await;
                    if sent.is_err() {
                        error!("Error sending message to directory");
                        continue;
                    }
                    match rpc_rx.await {
                        Err(err) => code = Some(format!("{err}")),
                        Ok(Err(dir_err)) => code = Some(dir_err),
                        Ok(Ok(msg)) => info!("{}", msg),
                    }
                    if code.is_some() {
                        break;
                    }
                }

                if let Some(err) = code {
                    error!("Benchmark operation error {}", err);
                } else {
                    let toc = tic.elapsed();

                    let millis = toc.as_millis();
                    println!(
                        "Benchmark output: Inserted {} users with {} updates/user\nExecution time: {} ms\nTime-per-user (avg): {} \u{00B5}s\nTime-per-op (avg): {} \u{00B5}s",
                        num_users,
                        num_updates_per_user,
                        toc.as_millis(),
                        toc.as_micros() / *num_users as u128,
                        toc.as_micros() / *num_users as u128 / *num_updates_per_user as u128
                    );
                }
            }
            OtherMode::BenchLookup {
                num_users,
                num_lookups_per_user,
            } => {
                println!("======= Benchmark operation requested ======= ");
                println!(
                    "Beginning LOOKUP benchmark of {num_users} users with {num_lookups_per_user} lookups/user"
                );

                let user_data: Vec<(String, String)> = (1..=*num_users)
                    .map(|i| {
                        (
                            StdRng::seed_from_u64(i)
                                .sample_iter(&Alphanumeric)
                                .take(256)
                                .map(char::from)
                                .collect(),
                            StdRng::seed_from_u64(i)
                                .sample_iter(&Alphanumeric)
                                .take(1024)
                                .map(char::from)
                                .collect(),
                        )
                    })
                    .collect();

                info!("Inserting {} users", num_users);
                let (rpc_tx, rpc_rx) = tokio::sync::oneshot::channel();
                let rpc = directory_host::Rpc(
                    directory_host::DirectoryCommand::PublishBatch(user_data.clone()),
                    Some(rpc_tx),
                );
                let sent = tx.clone().send(rpc).await;

                let tic = Instant::now();

                let mut code = None;
                for i in 1..=*num_lookups_per_user {
                    for (user, _) in &user_data {
                        let (rpc_tx, rpc_rx) = tokio::sync::oneshot::channel();
                        let rpc = directory_host::Rpc(
                            directory_host::DirectoryCommand::Lookup(String::from(user)),
                            Some(rpc_tx),
                        );
                        let sent = tx.clone().send(rpc).await;
                        if sent.is_err() {
                            error!("Error sending message to directory");
                            continue;
                        }
                        match rpc_rx.await {
                            Err(err) => code = Some(format!("{err}")),
                            Ok(Err(dir_err)) => code = Some(dir_err),
                            Ok(Ok(msg)) => {}
                        }
                        if code.is_some() {
                            break;
                        }
                    }
                    info!("LOOKUP of {} users complete (iteration {})", num_users, i);
                }

                if let Some(err) = code {
                    error!("Benchmark operation error {}", err);
                } else {
                    let toc = tic.elapsed();

                    let millis = toc.as_millis();
                    println!(
                        "Benchmark output: Looked up and verified {} users with {} lookups/user\nExecution time: {} ms\nTime-per-user (avg): {} \u{00B5}s\nTime-per-op (avg): {} \u{00B5}s",
                        num_users,
                        num_lookups_per_user,
                        toc.as_millis(),
                        toc.as_micros() / *num_users as u128,
                        toc.as_micros() / *num_users as u128 / *num_lookups_per_user as u128
                    );
                }
            }
            OtherMode::Flush => {
                println!("======= One-off flushing of the database ======= ");
                if let Some(mysql_db) = db {
                    if let Err(error) = mysql_db.get_db().delete_data().await {
                        error!("Error flushing database: {}", error);
                    } else {
                        info!("Database flushed.");
                    }
                }
            }
            OtherMode::Drop => {
                println!("======= Dropping database ======= ");
                if let Some(mysql_db) = db {
                    if let Err(error) = mysql_db.get_db().drop_tables().await {
                        error!("Error dropping database: {}", error);
                    } else {
                        info!("Database dropped.");
                    }
                }
            }
        }
    } else {
        // Traditional REPL processing loop
        loop {
            println!("Please enter a command");
            print!("> ");
            stdout().flush().unwrap();

            let mut line = String::new();
            stdin().read_line(&mut line).unwrap();

            match Command::parse(&mut line) {
                Command::Unknown(other) => {
                    println!("Input '{other}' is not supported, enter 'help' for the help menu")
                }
                Command::InvalidArgs(message) => println!("Invalid arguments: {message}"),
                Command::Exit => {
                    info!("Exiting...");
                    break;
                }
                Command::Help => {
                    Command::print_help_menu();
                }
                Command::Flush => {
                    println!("Flushing the database...");
                    if let Some(mysql_db) = &db {
                        if let Err(error) = mysql_db.get_db().delete_data().await {
                            println!("Error flushing database: {error}");
                        } else {
                            println!(
                                "Database flushed, exiting application. Please restart to create a new VKD"
                            );
                            break;
                        }
                    }
                }
                Command::Info => {
                    if cli.debug {
                        println!("\t**** DEBUG mode ACTIVE ****");
                    }
                    println!("===== Auditable Key Directory Information =====");
                    if let Some(mysql) = &db {
                        println!("      Database properties ({})", mysql.get_db());
                    } else {
                        println!("      Connected to an in-memory database");
                    }
                    println!();
                }
                Command::Directory(cmd) => {
                    let (rpc_tx, rpc_rx) = tokio::sync::oneshot::channel();
                    let rpc = directory_host::Rpc(cmd, Some(rpc_tx));
                    let sent = tx.clone().send(rpc).await;
                    if sent.is_err() {
                        warn!("Error sending message to directory");
                        continue;
                    }
                    if cli.debug {
                        match rpc_rx.await {
                            Ok(Ok(success)) => {
                                println!("Response: {success}");
                            }
                            Ok(Err(dir_err)) => {
                                error!("Error in directory processing command: {}", dir_err);
                            }
                            Err(_) => {
                                error!("Failed to receive result from directory");
                            }
                        }
                    } else {
                        match timeout(Duration::from_millis(1000), rpc_rx).await {
                            Ok(Ok(Ok(success))) => {
                                println!("Response: {success}");
                            }
                            Ok(Ok(Err(dir_err))) => {
                                error!("Error in directory processing command: {}", dir_err);
                            }
                            Ok(Err(_)) => {
                                error!("Failed to receive result from directory");
                            }
                            Err(_) => {
                                warn!("Timeout waiting on receive from directory");
                            }
                        }
                    }
                }
            }
        }
    }
    // terminate the server proc
    let shutdown = tx
        .clone()
        .send(directory_host::Rpc(
            directory_host::DirectoryCommand::Terminate,
            None,
        ))
        .await;
    if shutdown.is_err() {
        error!("Error shutting down directory");
    }
}


// Copyright (c) Meta Platforms, Inc. and affiliates.
//
// This source code is dual-licensed under either the MIT license found in the
// LICENSE-MIT file in the root directory of this source tree or the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
// of this source tree. You may select, at your option, one of the above-listed licenses.

fn main() -> Result<(), anyhow::Error> {
    xtaskops::tasks::main()
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

fn main() {
    rust_nightly();
}

fn rust_nightly() {
    let rustc = std::env::var("RUSTC").unwrap();
    let version = std::process::Command::new(rustc)
        .arg("--version")
        .output()
        .unwrap();

    assert!(version.status.success());

    // Nightly output:
    // rustc 1.64.0-nightly (affe0d3a0 2022-08-05)
    // Stable output:
    // rustc 1.64.0 (a55dd71d5 2022-09-19)
    // Meta internal output:
    // rustc 1.64.0-dev

    let stdout = String::from_utf8(version.stdout).unwrap();
    assert!(stdout.contains("rustc"), "Sanity check");
    let nightly = stdout.contains("nightly") || stdout.contains("dev");
    if nightly {
        println!("cargo:rustc-cfg=rust_nightly");
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

use crate::Visitor;

/// This trait allows traversal of object graph.
///
/// # Proc macro
///
/// Typically implemented with proc macro. Like this:
///
/// ```
/// use allocative::Allocative;
///
/// #[derive(Allocative)]
/// struct Foo {
///     x: u32,
///     y: String,
/// }
/// ```
///
/// Proc macro supports two attributes: `#[allocative(skip)]` and `#[allocative(bound = "")]`.
///
/// ## `#[allocative(skip)]`
///
/// `#[allocative(skip)]` can be used to skip field from traversal
/// (for example, to skip fields which are not `Allocative`,
/// and can be skipped because they are cheap).
///
/// ```
/// use allocative::Allocative;
///
/// /// This does not implement `Allocative`.
/// struct Unsupported;
///
/// #[derive(Allocative)]
/// struct Bar {
///     #[allocative(skip)]
///     unsupported: Unsupported,
/// }
/// ```
///
/// ## `#[allocative(bound = "")]`
///
/// `#[allocative(bound = "")]` can be used to not add `T: Allocative` bound
/// to `Allocative` trait implementation, like this:
///
/// ```
/// use std::marker::PhantomData;
/// use allocative::Allocative;
///
/// struct Unsupported;
///
/// #[derive(Allocative)]
/// #[allocative(bound = "")]
/// struct Baz<T> {
///     _marker: PhantomData<T>,
/// }
///
/// // So `Baz<Unsupported>` is `Allocative` even though `Unsupported` is not.
/// let allocative: &dyn Allocative = &Baz::<Unsupported> { _marker: PhantomData };
/// ```
pub trait Allocative {
    fn visit<'a, 'b: 'a>(&self, visitor: &'a mut Visitor<'b>);
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

use std::collections::hash_map;
use std::collections::HashMap;
use std::collections::HashSet;
use std::fmt::Write as _;
use std::mem;
use std::ops::Index;
use std::ops::IndexMut;

use crate::global_root::roots;
use crate::key::Key;
use crate::visitor::NodeKind;
use crate::visitor::Visitor;
use crate::visitor::VisitorImpl;
use crate::Allocative;

/// Node in flamegraph tree.
#[derive(Debug, Default, Clone)]
pub struct FlameGraph {
    children: HashMap<Key, FlameGraph>,
    /// Total size of all children, cached.
    children_size: usize,
    /// Node size excluding children.
    node_size: usize,
}

impl FlameGraph {
    pub fn total_size(&self) -> usize {
        self.node_size + self.children_size
    }

    /// Add another flamegraph to this one.
    pub fn add(&mut self, other: FlameGraph) {
        self.node_size += other.node_size;
        for (key, child) in other.children {
            self.add_child(key, child);
        }
    }

    /// Add a child node to the flamegraph, merging if it already exists.
    pub fn add_child(&mut self, key: Key, child: FlameGraph) {
        self.children_size += child.total_size();
        match self.children.entry(key) {
            hash_map::Entry::Occupied(mut entry) => {
                entry.get_mut().add(child);
            }
            hash_map::Entry::Vacant(entry) => {
                entry.insert(child);
            }
        }
    }

    /// Add size to this node.
    pub fn add_self(&mut self, size: usize) {
        self.node_size += size;
    }

    #[allow(clippy::from_iter_instead_of_collect)]
    fn write_flame_graph_impl(&self, stack: &[&str], w: &mut String) {
        if self.node_size != 0 {
            if !stack.is_empty() {
                writeln!(w, "{} {}", stack.join(";"), self.node_size).unwrap();
            } else {
                // Don't care.
            }
        }
        let mut stack = stack.to_vec();
        let mut children = Vec::from_iter(self.children.iter());
        children.sort_by_key(|(key, _)| *key);
        for (key, child) in children {
            stack.push(key);
            child.write_flame_graph_impl(&stack, w);
            stack.pop().unwrap();
        }
    }

    /// Write flamegraph in format suitable for `flamegraph.pl` or `inferno`.
    pub fn write(&self) -> String {
        let mut r = String::new();
        self.write_flame_graph_impl(&[], &mut r);
        r
    }
}

#[derive(Debug)]
pub struct FlameGraphOutput {
    flamegraph: FlameGraph,
    warnings: String,
}

impl FlameGraphOutput {
    /// Flamegraph source, can be fed to `flamegraph.pl` or `inferno`.
    pub fn flamegraph(&self) -> &FlameGraph {
        &self.flamegraph
    }

    /// Warnings. Text file in unspecified format.
    pub fn warnings(&self) -> String {
        self.warnings.clone()
    }
}

#[derive(Default, Eq, PartialEq, Clone, Debug)]
struct TreeData {
    /// Size of this node including children but excluding unique/shared children.
    /// For example for `String` this would be `size_of::<String>()`.
    size: usize,
    /// Size excluding children. This value is output to flamegraph for given stack.
    /// Can be negative if nodes provides sizes incorrectly.
    rem_size: isize,
    /// Whether this node is `Box` something.
    unique: bool,
    /// Child nodes.
    children: HashMap<Key, TreeId>,
}

impl TreeData {
    fn inline_children_size(&self) -> isize {
        self.size as isize - self.rem_size
    }
}

struct TreeRef<'a> {
    trees: &'a Trees,
    tree_id: TreeId,
}

impl<'a> TreeRef<'a> {
    fn write_flame_graph(&self, stack: &[&str], warnings: &mut String) -> FlameGraph {
        let mut flame_graph = FlameGraph::default();
        let tree = &self.trees[self.tree_id];
        if tree.rem_size > 0 {
            if stack.is_empty() {
                // don't care.
            } else {
                flame_graph.node_size = tree.rem_size as usize;
            }
        } else if tree.rem_size < 0 && !stack.is_empty() {
            writeln!(
                warnings,
                "Incorrect size declaration for node `{}`, size of self: {}, size of inline children: {}",
                stack.join(";"),
                tree.size,
                tree.inline_children_size()
            )
                .unwrap();
        }
        let mut children: Vec<(&Key, &TreeId)> = Vec::from_iter(&tree.children);
        let mut stack = stack.to_vec();
        children.sort_by_key(|(k, _)| *k);
        for (key, child) in children {
            stack.push(key);
            let child = TreeRef {
                trees: self.trees,
                tree_id: *child,
            };
            let child_framegraph = child.write_flame_graph(&stack, warnings);
            flame_graph.add_child(key.clone(), child_framegraph);
            stack.pop().unwrap();
        }
        flame_graph
    }

    fn to_flame_graph(&self) -> (FlameGraph, String) {
        let mut warnings = String::new();
        let flame_graph = self.write_flame_graph(&[], &mut warnings);
        (flame_graph, warnings)
    }
}

#[derive(Debug, Eq, PartialEq)]
struct Tree {
    trees: Trees,
    tree_id: TreeId,
}

impl Tree {
    fn as_ref(&self) -> TreeRef {
        TreeRef {
            trees: &self.trees,
            tree_id: self.tree_id,
        }
    }

    fn to_flame_graph(&self) -> (FlameGraph, String) {
        self.as_ref().to_flame_graph()
    }
}

#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
struct TreeId(usize);

#[derive(Default, Clone, Debug, Eq, PartialEq)]
struct Trees {
    trees: Vec<TreeData>,
}

impl Trees {
    fn new_tree(&mut self) -> TreeId {
        let id = TreeId(self.trees.len());
        self.trees.push(TreeData::default());
        id
    }
}

impl Index<TreeId> for Trees {
    type Output = TreeData;

    fn index(&self, index: TreeId) -> &Self::Output {
        &self.trees[index.0]
    }
}

impl IndexMut<TreeId> for Trees {
    fn index_mut(&mut self, index: TreeId) -> &mut Self::Output {
        &mut self.trees[index.0]
    }
}

#[derive(Clone, Debug)]
struct TreeStack {
    stack: Vec<TreeId>,
    tree: TreeId,
}

struct TreeStackRef<'t, 's> {
    trees: &'t mut Trees,
    stack: &'s mut TreeStack,
}

impl<'t, 's> TreeStackRef<'t, 's> {
    fn current_data(&'t mut self) -> &'t mut TreeData {
        &mut self.trees[self.stack.tree]
    }

    fn down(&mut self, key: Key) {
        self.stack.stack.push(self.stack.tree);
        let next_tree_id = TreeId(self.trees.trees.len());
        let child = match self.trees[self.stack.tree].children.entry(key) {
            hash_map::Entry::Occupied(e) => *e.get(),
            hash_map::Entry::Vacant(e) => {
                e.insert(next_tree_id);
                let child = self.trees.new_tree();
                assert_eq!(child, next_tree_id);
                child
            }
        };
        self.stack.tree = child;
    }

    #[must_use]
    fn up(&mut self) -> bool {
        if let Some(pop) = self.stack.stack.pop() {
            self.stack.tree = pop;
            true
        } else {
            false
        }
    }
}

#[derive(Eq, PartialEq, Hash, Clone, Copy, Debug)]
struct VisitedSharedPointer(*const ());
unsafe impl Send for VisitedSharedPointer {}

/// Build a flamegraph from given root objects.
///
/// # Example
///
/// ```
/// use allocative::FlameGraphBuilder;
/// use allocative::Allocative;
///
/// #[derive(Allocative)]
/// struct Foo {
///     data: String,
/// };
///
/// let foo1 = Foo { data: "Hello, world!".to_owned() };
/// let foo2 = Foo { data: "Another message!".to_owned() };
///
/// let mut  flamegraph = FlameGraphBuilder::default();
/// flamegraph.visit_root(&foo1);
/// flamegraph.visit_root(&foo2);
/// let flamegraph_src = flamegraph.finish().flamegraph();
/// ```
///
/// And now `flamegraph_src` can be fed to either [flamegraph.pl] or [inferno].
///
/// [flamegraph.pl]: https://github.com/brendangregg/FlameGraph
/// [inferno]: https://github.com/jonhoo/inferno
#[derive(Debug)]
pub struct FlameGraphBuilder {
    /// Visited shared pointers.
    visited_shared: HashSet<VisitedSharedPointer>,
    /// Tree data storage.
    trees: Trees,
    /// Current node we are processing in `Visitor`.
    current: TreeStack,
    /// Previous stack when entering shared pointer.
    shared: Vec<TreeStack>,
    /// Data root.
    root: TreeId,
    /// Is root visitor created?
    entered_root_visitor: bool,
}

fn _assert_flame_graph_builder_is_send() {
    fn assert_send<T: Send>() {}
    assert_send::<FlameGraphBuilder>();
}

impl Default for FlameGraphBuilder {
    fn default() -> FlameGraphBuilder {
        let mut trees = Trees::default();
        let root = trees.new_tree();
        FlameGraphBuilder {
            trees,
            visited_shared: HashSet::new(),
            current: TreeStack {
                stack: Vec::new(),
                tree: root,
            },
            shared: Vec::new(),
            root,
            entered_root_visitor: false,
        }
    }
}

impl FlameGraphBuilder {
    pub fn root_visitor(&mut self) -> Visitor {
        assert!(!self.entered_root_visitor);
        self.entered_root_visitor = true;
        Visitor {
            visitor: self,
            node_kind: NodeKind::Root,
        }
    }

    /// Collect tree sizes starting from given root.
    pub fn visit_root(&mut self, root: &dyn Allocative) {
        let mut visitor = self.root_visitor();
        root.visit(&mut visitor);
        visitor.exit();
    }

    /// Collect data from global roots registered with
    /// [`register_root`](crate::register_root).
    pub fn visit_global_roots(&mut self) {
        for root in roots() {
            self.visit_root(root);
        }
    }

    fn finish_impl(mut self) -> Tree {
        assert!(self.shared.is_empty());
        assert!(self.current.stack.is_empty());
        assert!(!self.entered_root_visitor);
        Self::update_sizes(self.root, &mut self.trees);
        Tree {
            trees: self.trees,
            tree_id: self.root,
        }
    }

    /// Finish building the flamegraph.
    pub fn finish(self) -> FlameGraphOutput {
        let tree = self.finish_impl();
        let (flamegraph, warnings) = tree.to_flame_graph();
        FlameGraphOutput {
            flamegraph,
            warnings,
        }
    }

    /// Finish building the flamegraph and return the flamegraph output.
    pub fn finish_and_write_flame_graph(self) -> String {
        self.finish().flamegraph.write()
    }

    fn update_sizes(tree_id: TreeId, trees: &mut Trees) {
        let tree = &mut trees[tree_id];
        for child in tree.children.values().copied().collect::<Vec<_>>() {
            Self::update_sizes(child, trees);
        }
        let tree = &mut trees[tree_id];
        let children_size = if tree.unique {
            0
        } else {
            let tree = &trees[tree_id];
            tree.children
                .values()
                .map(|child| trees[*child].size)
                .sum::<usize>()
        };
        let tree = &mut trees[tree_id];
        let size = tree.size;
        tree.rem_size = (size as isize).saturating_sub(children_size as isize);
    }

    fn current(&mut self) -> TreeStackRef {
        TreeStackRef {
            trees: &mut self.trees,
            stack: &mut self.current,
        }
    }

    fn exit_impl(&mut self) {
        assert!(self.entered_root_visitor);

        let up = self.current().up();
        if !up {
            if let Some(shared) = self.shared.pop() {
                self.current = shared;
                assert!(self.current().up());
            } else {
                self.entered_root_visitor = false;
            }
        }
    }
}

impl VisitorImpl for FlameGraphBuilder {
    fn enter_inline_impl(&mut self, name: Key, size: usize, _parent: NodeKind) {
        self.current().down(name);
        self.current().current_data().size += size;
    }

    fn enter_unique_impl(&mut self, name: Key, size: usize, _parent: NodeKind) {
        self.current().down(name);
        self.current().current_data().size += size;
        // TODO: deal with potential issue when node is both unique and not.
        // TODO: record some malloc overhead.
        self.current().current_data().unique = true;
    }

    #[must_use]
    fn enter_shared_impl(
        &mut self,
        name: Key,
        size: usize,
        ptr: *const (),
        _parent: NodeKind,
    ) -> bool {
        self.current().down(name);
        self.current().current_data().size += size;

        if !self.visited_shared.insert(VisitedSharedPointer(ptr)) {
            self.exit_impl();
            return false;
        }

        self.shared.push(mem::replace(
            &mut self.current,
            TreeStack {
                stack: Vec::new(),
                tree: self.root,
            },
        ));
        true
    }

    fn exit_inline_impl(&mut self) {
        self.exit_impl();
    }

    fn exit_unique_impl(&mut self) {
        self.exit_impl();
    }

    fn exit_shared_impl(&mut self) {
        self.exit_impl();
    }

    fn exit_root_impl(&mut self) {
        self.exit_impl();
    }
}

#[cfg(test)]
mod tests {
    use crate::flamegraph::FlameGraphBuilder;
    use crate::flamegraph::Tree;
    use crate::flamegraph::Trees;
    use crate::key::Key;
    use crate::FlameGraph;

    #[test]
    fn test_empty() {
        let mut fg = FlameGraphBuilder::default();
        fg.root_visitor().exit();
        let tree = fg.finish_impl();

        let mut expected_trees = Trees::default();
        let expected_id = expected_trees.new_tree();
        let expected = Tree {
            trees: expected_trees,
            tree_id: expected_id,
        };

        assert_eq!(expected, tree);
        assert_eq!("", tree.to_flame_graph().0.write());
    }

    #[test]
    fn test_simple() {
        let mut fg = FlameGraphBuilder::default();
        fg.root_visitor().visit_simple(Key::new("a"), 10);
        let tree = fg.finish_impl();

        let mut expected = Trees::default();
        let expected_root = expected.new_tree();
        let expected_child = expected.new_tree();
        expected[expected_root].size = 0;
        expected[expected_root].rem_size = -10;
        expected[expected_root]
            .children
            .insert(Key::new("a"), expected_child);
        expected[expected_child].size = 10;
        expected[expected_child].rem_size = 10;
        let expected = Tree {
            trees: expected,
            tree_id: expected_root,
        };
        assert_eq!(expected, tree);
        assert_eq!("a 10\n", tree.to_flame_graph().0.write());
    }

    #[test]
    fn test_unique() {
        let mut fg = FlameGraphBuilder::default();
        let mut visitor = fg.root_visitor();
        let mut s = visitor.enter(Key::new("Struct"), 10);
        s.visit_simple(Key::new("a"), 3);
        let mut un = s.enter_unique(Key::new("p"), 6);
        un.visit_simple(Key::new("x"), 13);
        un.exit();
        s.exit();
        visitor.exit();

        let tree = fg.finish_impl();

        assert_eq!(
            "\
                Struct 1\n\
                Struct;a 3\n\
                Struct;p 6\n\
                Struct;p;x 13\n\
            ",
            tree.to_flame_graph().0.write(),
            "{:#?}",
            tree,
        );
    }

    #[test]
    fn test_shared() {
        let p = 10;

        let mut fg = FlameGraphBuilder::default();
        let mut visitor = fg.root_visitor();

        for _ in 0..2 {
            let mut s = visitor.enter(Key::new("Struct"), 10);
            s.visit_simple(Key::new("a"), 3);
            {
                let sh = s.enter_shared(Key::new("p"), 6, &p as *const i32 as *const ());
                if let Some(mut sh) = sh {
                    sh.visit_simple(Key::new("Shared"), 13);
                    sh.exit();
                }
            }
            s.exit();
        }

        visitor.exit();

        let tree = fg.finish_impl();

        assert_eq!(
            "\
            Shared 13\n\
            Struct 2\n\
            Struct;a 6\n\
            Struct;p 12\n\
        ",
            tree.to_flame_graph().0.write(),
            "{:#?}",
            tree,
        );
    }

    #[test]
    fn test_inline_children_too_large() {
        let mut fg = FlameGraphBuilder::default();
        let mut visitor = fg.root_visitor();
        let mut child_visitor = visitor.enter(Key::new("a"), 10);
        child_visitor.visit_simple(Key::new("b"), 13);
        child_visitor.exit();
        visitor.exit();
        let output = fg.finish();
        assert_eq!("a;b 13\n", output.flamegraph().write());
        assert_eq!(
            "Incorrect size declaration for node `a`, size of self: 10, size of inline children: 13\n",
            output.warnings()
        );
    }

    #[test]
    fn test_flamegraph_add() {
        let mut a = FlameGraph::default();

        let mut b_1 = FlameGraph::default();
        b_1.add_self(10);

        let mut b = FlameGraph::default();
        b.add_child(Key::new("x"), b_1);

        a.add(b);
        assert_eq!(10, a.total_size());
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

use std::sync::Mutex;

use crate::allocative_trait::Allocative;

static ROOTS: Mutex<Vec<&'static (dyn Allocative + Sync + 'static)>> = Mutex::new(Vec::new());

/// Register global root which can be later traversed by profiler.
///
/// [`root`](crate::root) macro can be used to register global root.
pub fn register_root(root: &'static (dyn Allocative + Sync + 'static)) {
    ROOTS.lock().unwrap().push(root);
}

pub(crate) fn roots() -> Vec<&'static (dyn Allocative + Sync + 'static)> {
    ROOTS.lock().unwrap().clone()
}

#[cfg(test)]
mod tests {
    use crate as allocative;
    use crate::Allocative;
    use crate::FlameGraphBuilder;

    #[derive(Allocative)]
    struct TestGlobalRoot {
        x: u32,
    }

    #[allocative::root]
    static TEST_GLOBAL_ROOT: TestGlobalRoot = TestGlobalRoot { x: 17 };

    #[test]
    fn test_derive() {
        let mut fg = FlameGraphBuilder::default();
        fg.visit_global_roots();
        let fg = fg.finish_and_write_flame_graph();
        assert!(fg.contains("TestGlobalRoot;x;u32 4"), "{}", fg);
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

#![cfg(test)]

use std::env;
use std::fs;

use crate::Allocative;
use crate::FlameGraphBuilder;

const REGENERATE_VAR_NAME: &str = "ALLOCATIVE_REGENERATE_TESTS";

fn golden_header() -> String {
    let at = "@";
    format!(
        "{at}generated\n\
    To regenerate, run:\n\
    ```\n\
    {REGENERATE_VAR_NAME}=1 cargo test -p allocative\n\
    ```\n"
    )
}

fn make_golden<T: Allocative>(value: &T) -> (String, String) {
    let mut builder = FlameGraphBuilder::default();
    builder.visit_root(value);
    let flamegraph = builder.finish_and_write_flame_graph();
    // Because crate name is `allocative_unittest` in fbcode buck2 tests.
    let flamegraph = flamegraph.replace("allocative_unittest::", "allocative::");
    let mut flamegraph_svg: Vec<u8> = Vec::new();
    let mut inferno_options = inferno::flamegraph::Options::default();
    inferno_options.deterministic = true;
    inferno_options.pretty_xml = true;
    inferno::flamegraph::from_reader(
        &mut inferno_options,
        flamegraph.as_bytes(),
        &mut flamegraph_svg,
    )
    .unwrap();
    let flamegraph_svg = String::from_utf8(flamegraph_svg).unwrap();

    let flamegraph = format!(
        "{header}{flamegraph}",
        header = golden_header()
            .lines()
            .map(|line| format!("# {}\n", line))
            .collect::<String>()
    );

    let flamegraph_svg = flamegraph_svg.replace(
        "<!DOCTYPE",
        &format!("\n<!--\n{}-->\n<!DOCTYPE", golden_header()),
    );

    (flamegraph, flamegraph_svg)
}

fn type_name_to_path(type_name: &str) -> String {
    let path = type_name.strip_suffix("::Marker").unwrap();
    let path = if let Some(path) = path.strip_prefix("allocative::") {
        path
    } else {
        // In buck2 test crate has suffix `_unittest`.
        path.strip_prefix("allocative_unittest::").unwrap()
    };
    path.replace("::tests::", "_").replace("::", "/")
}

pub(crate) fn golden_test_impl<T: Allocative>(value: &T, type_name: &str) {
    let path = type_name_to_path(type_name);
    let manifest_dir =
        env::var("CARGO_MANIFEST_DIR").expect("`CARGO_MANIFEST_DIR` variable must be set");

    let flamegraph_src_path = format!("{manifest_dir}/src/{path}.src");
    let flamegraph_svg_path = format!("{manifest_dir}/src/{path}.svg");

    let (flamegraph, flamegraph_svg) = make_golden(value);
    if env::var(REGENERATE_VAR_NAME).is_ok() {
        fs::write(flamegraph_src_path, &flamegraph).unwrap();
        fs::write(flamegraph_svg_path, flamegraph_svg).unwrap();
    } else {
        let expected_flamegraph = fs::read_to_string(flamegraph_src_path)
            .unwrap()
            .replace("\r\n", "\n");
        assert_eq!(expected_flamegraph, flamegraph);
    }
}

macro_rules! golden_test {
    ($expr:expr) => {{
        struct Marker;
        crate::golden::golden_test_impl($expr, std::any::type_name::<Marker>());
    }};
}

pub(crate) use golden_test;


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

use std::any;
use std::cmp::Ordering;
use std::hash::Hash;
use std::hash::Hasher;
use std::ops::Deref;

/// Hashed string, which is a key while descending into a tree (e.g. type name or field name).
#[derive(Clone, Eq, PartialEq, Debug)]
pub struct Key {
    hash: u64,
    s: &'static str,
}

impl PartialOrd for Key {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for Key {
    fn cmp(&self, other: &Self) -> Ordering {
        self.s.cmp(other.s)
    }
}

#[allow(clippy::derived_hash_with_manual_eq)]
impl Hash for Key {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.hash.hash(state);
    }
}

impl Deref for Key {
    type Target = str;

    fn deref(&self) -> &str {
        self.s
    }
}

impl Key {
    /// Must be identical to `allocative_derive::hash`.
    const fn hash(s: &str) -> u64 {
        let mut hash = 0xcbf29ce484222325;
        let mut i = 0;
        while i < s.as_bytes().len() {
            let b = s.as_bytes()[i];
            hash ^= b as u64;
            hash = hash.wrapping_mul(0x100000001b3);
            i += 1;
        }
        hash
    }

    /// Compute hash.
    pub const fn new(s: &'static str) -> Key {
        let hash = Self::hash(s);
        Key::new_unchecked(hash, s)
    }

    pub const fn new_unchecked(hash: u64, s: &'static str) -> Key {
        Key { hash, s }
    }

    pub fn for_type_name<T: ?Sized>() -> Key {
        // Compute hash at compile time.
        #[cfg(rust_nightly)]
        return Key {
            hash: AllocativeKeyForType::<T>::KEY.hash,
            s: AllocativeKeyForType::<T>::KEY.s,
        };
        // Hope optimizer folds this to constant (it doesn't for long type names).
        #[cfg(not(rust_nightly))]
        return Key::new(any::type_name::<T>());
    }
}

#[cfg(rust_nightly)]
struct AllocativeKeyForType<T: ?Sized>(std::marker::PhantomData<fn(&T)>);

#[cfg(rust_nightly)]
impl<T: ?Sized> AllocativeKeyForType<T> {
    /// Force compute it at compile time. Const fn does not guarantee that.
    pub const KEY: Key = Key::new(any::type_name::<T>());
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

//! # Allocative
//!
//! Crate implements lightweight memory profiler which allows
//! object traversal and size introspection.
//!
//! An object implementing [`Allocative`] trait is introspectable, and this crate
//! provides two utilities to work with such objects:
//! * [`FlameGraphBuilder`] to build a flame graph of object tree
//! * [`size_of_unique_allocated_data`] provides estimation
//!    of how much allocated memory the value holds
//!
//! ## Allocative overhead
//!
//! When allocative is used, binary size is slightly increased due to implementations
//! of [`Allocative`] trait, but it has no runtime/memory overhead when it is not used.
//!
//! ## How it is different from other call-stack malloc profilers like jemalloc heap profiler
//!
//! Allocative is not a substitute for call stack malloc profiler,
//! it provides a different view on memory usage.
//!
//! Here are some differences between allocative and call-stack malloc profiler:
//!
//! * Allocative requires implementation of [`Allocative`] trait for each type
//!   which needs to be measured, and some setup in the program to enable it
//! * Allocative flamegraph shows object by object tree, not by call stack
//! * Allocative shows gaps in allocated memory,
//!   e.g. spare capacity of collections or too large padding in structs or enums
//! * Allocative allows profiling non-malloc allocations (for example, allocations within [bumpalo])
//! * Allocative allows profiling of memory for subset of the process data
//!   (for example, measure the size of RPC response before serialization)
//!
//! [bumpalo]: https://github.com/fitzgen/bumpalo

#![cfg_attr(rust_nightly, feature(const_type_name))]
#![cfg_attr(rust_nightly, feature(never_type))]
#![deny(rustdoc::broken_intra_doc_links)]

mod allocative_trait;
mod flamegraph;
mod global_root;
pub(crate) mod golden;
mod impls;
mod key;
mod rc_str;
mod size_of;
mod test_derive;
mod visitor;

pub use allocative_derive::root;
pub use allocative_derive::Allocative;

pub use crate::allocative_trait::Allocative;
pub use crate::flamegraph::FlameGraph;
pub use crate::flamegraph::FlameGraphBuilder;
pub use crate::global_root::register_root;
pub use crate::key::Key;
pub use crate::size_of::size_of_unique;
pub use crate::size_of::size_of_unique_allocated_data;
pub use crate::visitor::Visitor;

#[doc(hidden)]
pub mod __macro_refs {
    pub use ctor;
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

use std::borrow::Borrow;
use std::hash::Hash;
use std::hash::Hasher;
use std::ops::Deref;
use std::rc::Rc;

#[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd)]
pub(crate) struct RcStr(Rc<str>);

impl<'a> From<&'a str> for RcStr {
    fn from(s: &'a str) -> RcStr {
        RcStr(Rc::from(s))
    }
}

impl Default for RcStr {
    fn default() -> RcStr {
        RcStr::from("")
    }
}

impl RcStr {
    pub(crate) fn as_str(&self) -> &str {
        &self.0
    }
}

impl Deref for RcStr {
    type Target = str;

    fn deref(&self) -> &str {
        self.as_str()
    }
}

impl Borrow<str> for RcStr {
    fn borrow(&self) -> &str {
        self.as_str()
    }
}

#[allow(clippy::derived_hash_with_manual_eq)]
impl Hash for RcStr {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.as_str().hash(state);
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

use crate::visitor::NodeKind;
use crate::visitor::VisitorImpl;
use crate::Allocative;
use crate::Key;
use crate::Visitor;

/// Size of data allocated in unique pointers in the struct.
///
/// * Exclude self
/// * Exclude shared pointers
/// * For unique pointers, include the size of the pointee plus this function recursively
///
/// # Example
///
/// ```
/// use allocative::Allocative;
///
/// #[derive(Allocative)]
/// struct Foo {
///     data: Vec<u8>,
/// }
///
/// assert_eq!(3, allocative::size_of_unique_allocated_data(&Foo { data: vec![10, 20, 30] }));
/// ```
pub fn size_of_unique_allocated_data(root: &dyn Allocative) -> usize {
    struct SizeOfUniqueAllocatedDataVisitor {
        /// Size we return.
        size: usize,
    }

    impl VisitorImpl for SizeOfUniqueAllocatedDataVisitor {
        fn enter_inline_impl(&mut self, _name: Key, size: usize, parent: NodeKind) {
            if let NodeKind::Unique = parent {
                self.size += size;
            }
        }

        fn enter_unique_impl(&mut self, _name: Key, _size: usize, _parent: NodeKind) {}

        fn enter_shared_impl(
            &mut self,
            _name: Key,
            _size: usize,
            _ptr: *const (),
            _parent: NodeKind,
        ) -> bool {
            false
        }

        fn exit_inline_impl(&mut self) {}

        fn exit_unique_impl(&mut self) {}

        fn exit_shared_impl(&mut self) {
            unreachable!("shared pointers are not visited")
        }

        fn exit_root_impl(&mut self) {}
    }

    let mut visitor_impl = SizeOfUniqueAllocatedDataVisitor { size: 0 };
    let mut visitor = Visitor {
        visitor: &mut visitor_impl,
        node_kind: NodeKind::Root,
    };
    root.visit(&mut visitor);
    visitor.exit();
    visitor_impl.size
}

/// Size of a piece of data and data allocated in unique pointers in the struct.
///
/// * Excludes shared pointers
///
/// # Example
///
/// ```
/// use allocative::Allocative;
///
/// #[derive(Allocative)]
/// struct Foo {
///     data: Vec<u8>,
/// }
///
/// assert_eq!(3 + std::mem::size_of::<Vec<u8>>(), allocative::size_of_unique(&Foo { data: vec![10, 20, 30] }));
/// ```
pub fn size_of_unique<T>(root: &T) -> usize
where
    T: Allocative,
{
    std::mem::size_of::<T>() + size_of_unique_allocated_data(root)
}

#[cfg(test)]
mod tests {
    use std::mem;

    use allocative_derive::Allocative;

    use crate as allocative;
    use crate::size_of_unique;
    use crate::size_of_unique_allocated_data;

    #[test]
    fn test_box() {
        #[derive(Allocative)]
        struct Boxed {
            data: Box<u32>,
        }

        let boxed = Boxed { data: Box::new(17) };

        assert_eq!(mem::size_of::<u32>(), size_of_unique_allocated_data(&boxed));

        assert_eq!(
            mem::size_of::<u32>() + mem::size_of::<Boxed>(),
            size_of_unique(&boxed)
        );
    }

    #[test]
    fn test_box_slice() {
        #[derive(Allocative)]
        struct Boxed {
            data: Box<[u32]>,
        }

        let boxed = Boxed {
            data: vec![1, 2, 3].into_boxed_slice(),
        };

        assert_eq!(
            mem::size_of::<u32>() * 3,
            size_of_unique_allocated_data(&boxed)
        );

        assert_eq!(
            mem::size_of::<Boxed>() + mem::size_of::<u32>() * 3,
            size_of_unique(&boxed)
        );
    }

    #[test]
    fn test_struct_in_box() {
        #[derive(Allocative)]
        struct Data {
            a: u8,
            b: Box<u32>,
        }

        #[derive(Allocative)]
        struct Boxed {
            data: Box<Data>,
        }

        let boxed = Boxed {
            data: Box::new(Data {
                a: 1,
                b: Box::new(2),
            }),
        };

        assert_eq!(
            mem::size_of::<Data>() + mem::size_of::<u32>(),
            size_of_unique_allocated_data(&boxed)
        );

        assert_eq!(
            mem::size_of::<Boxed>() + mem::size_of::<Data>() + mem::size_of::<u32>(),
            size_of_unique(&boxed)
        );
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

use std::mem;

use crate::allocative_trait::Allocative;
use crate::impls::common::CAPACITY_NAME;
use crate::impls::common::DATA_NAME;
use crate::impls::common::KEY_NAME;
use crate::impls::common::UNUSED_CAPACITY_NAME;
use crate::impls::common::VALUE_NAME;
use crate::key::Key;

/// Actual implementation of the visitor.
///
/// At the moment there's only one implementation, the one which generates flame graph,
/// and this trait is crate-private. This may change in the future.
pub(crate) trait VisitorImpl {
    /// Enter simple field like `u32`.
    /// All sizes are in bytes.
    fn enter_inline_impl(&mut self, name: Key, size: usize, parent: NodeKind);
    /// Enter field which points to heap-allocated unique memory (e.g. `Box<T>`).
    fn enter_unique_impl(&mut self, name: Key, size: usize, parent: NodeKind);
    /// Enter field which points to heap-allocated shared memory (e.g. `Arc<T>`).
    /// This function returns `false` if pointee already visited.
    #[must_use]
    fn enter_shared_impl(
        &mut self,
        name: Key,
        size: usize,
        ptr: *const (),
        parent: NodeKind,
    ) -> bool;

    /// Exit the field. Each `enter_` must be matched by `exit_`.
    /// `Visitor` wrapper guarantees that.
    fn exit_inline_impl(&mut self);
    fn exit_unique_impl(&mut self);
    fn exit_shared_impl(&mut self);
    // Exit "root" visitor.
    fn exit_root_impl(&mut self);
}

#[derive(Copy, Clone)]
pub(crate) enum NodeKind {
    Inline,
    Unique,
    Shared,
    Root,
}

#[must_use] // Must call `.exit()`.
pub struct Visitor<'a> {
    pub(crate) visitor: &'a mut dyn VisitorImpl,
    pub(crate) node_kind: NodeKind,
}

impl<'a> Drop for Visitor<'a> {
    fn drop(&mut self) {
        self.exit_impl();
    }
}

impl<'a> Visitor<'a> {
    pub fn enter<'b>(&'b mut self, name: Key, size: usize) -> Visitor<'b>
    where
        'a: 'b,
    {
        self.visitor.enter_inline_impl(name, size, self.node_kind);
        Visitor {
            visitor: self.visitor,
            node_kind: NodeKind::Inline,
        }
    }

    pub fn enter_unique<'b>(&'b mut self, name: Key, size: usize) -> Visitor<'b>
    where
        'a: 'b,
    {
        self.visitor.enter_unique_impl(name, size, self.node_kind);
        Visitor {
            visitor: self.visitor,
            node_kind: NodeKind::Unique,
        }
    }

    /// Enter a field containing a shared pointer.
    ///
    /// This functions does nothing and returns `None`
    /// if pointee (`ptr` argument) was previously visited.
    pub fn enter_shared<'b>(
        &'b mut self,
        name: Key,
        size: usize,
        ptr: *const (),
    ) -> Option<Visitor<'b>>
    where
        'a: 'b,
    {
        if self
            .visitor
            .enter_shared_impl(name, size, ptr, self.node_kind)
        {
            Some(Visitor {
                visitor: self.visitor,
                node_kind: NodeKind::Shared,
            })
        } else {
            None
        }
    }

    /// This function is typically called as the first function of an `Allocative`
    /// implementation to record self.
    pub fn enter_self_sized<'b, T>(&'b mut self) -> Visitor<'b>
    where
        'a: 'b,
    {
        self.enter(Key::for_type_name::<T>(), mem::size_of::<T>())
    }

    /// This function is typically called as first function of an `Allocative`
    /// implementation to record self.
    pub fn enter_self<'b, T: ?Sized>(&'b mut self, this: &T) -> Visitor<'b>
    where
        'a: 'b,
    {
        self.enter(Key::for_type_name::<T>(), mem::size_of_val(this))
    }

    /// Visit simple sized field (e.g. `u32`) without descending into children.
    pub fn visit_simple<'b>(&'b mut self, name: Key, size: usize)
    where
        'a: 'b,
    {
        self.enter(name, size).exit();
    }

    /// Visit simple sized field (e.g. `u32`) without descending into children.
    pub fn visit_simple_sized<'b, T>(&'b mut self)
    where
        'a: 'b,
    {
        self.enter_self_sized::<T>().exit();
    }

    pub fn visit_field<'b, T: Allocative + ?Sized>(&'b mut self, name: Key, field: &T)
    where
        'a: 'b,
    {
        let mut visitor = self.enter(name, mem::size_of_val::<T>(field));
        field.visit(&mut visitor);
        visitor.exit();
    }

    pub fn visit_slice<'b, T: Allocative>(&'b mut self, slice: &[T])
    where
        'a: 'b,
    {
        self.visit_iter(slice);
    }

    pub fn visit_iter<'b, 'i, T: Allocative + 'i, I: IntoIterator<Item = &'i T>>(
        &'b mut self,
        iter: I,
    ) where
        'a: 'b,
    {
        if !mem::needs_drop::<T>() || mem::size_of::<T>() == 0 {
            // `T` has no pointers it owns.
            self.visit_simple(
                Key::for_type_name::<T>(),
                mem::size_of::<T>() * iter.into_iter().count(),
            );
        } else {
            for item in iter {
                item.visit(self);
            }
        }
    }

    pub fn visit_vec_like_body<'b, T>(&'b mut self, data: &[T], capacity: usize)
    where
        'a: 'b,
        T: Allocative,
    {
        let mut visitor = self.enter(CAPACITY_NAME, mem::size_of::<T>() * capacity);
        visitor.visit_slice(data);
        visitor.visit_simple(
            UNUSED_CAPACITY_NAME,
            mem::size_of::<T>() * capacity.wrapping_sub(data.len()),
        );
        visitor.exit();
    }

    pub fn visit_generic_map_fields<'b, 'x, K: Allocative + 'x, V: Allocative + 'x>(
        &'b mut self,
        entries: impl IntoIterator<Item = (&'x K, &'x V)>,
    ) {
        let mut visitor = self.enter_unique(DATA_NAME, mem::size_of::<*const ()>());
        for (k, v) in entries {
            visitor.visit_field(KEY_NAME, k);
            visitor.visit_field(VALUE_NAME, v);
        }
        visitor.exit();
    }

    pub fn visit_generic_set_fields<'b, 'x, K: Allocative + 'x>(
        &'b mut self,
        entries: impl IntoIterator<Item = &'x K>,
    ) where
        'a: 'b,
    {
        let mut visitor = self.enter_unique(DATA_NAME, mem::size_of::<*const ()>());
        for k in entries {
            visitor.visit_field(KEY_NAME, k);
        }
        visitor.exit();
    }

    fn exit_impl(&mut self) {
        match self.node_kind {
            NodeKind::Inline => self.visitor.exit_inline_impl(),
            NodeKind::Unique => self.visitor.exit_unique_impl(),
            NodeKind::Shared => self.visitor.exit_shared_impl(),
            NodeKind::Root => self.visitor.exit_root_impl(),
        }
    }

    pub fn exit(mut self) {
        self.exit_impl();
        // Prevent `drop`.
        mem::forget(self);
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

#![cfg(feature = "anyhow")]

use crate::allocative_trait::Allocative;
use crate::visitor::Visitor;

impl Allocative for anyhow::Error {
    fn visit<'a, 'b: 'a>(&self, visitor: &'a mut Visitor<'b>) {
        // Don't know what the size is.
        let _ = visitor;
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

#![cfg(feature = "bumpalo")]

use std::mem;

use bumpalo::Bump;

use crate::allocative_trait::Allocative;
use crate::impls::common::DATA_NAME;
use crate::impls::common::PTR_NAME;
use crate::impls::common::UNUSED_CAPACITY_NAME;
use crate::visitor::Visitor;

struct BumpCounters {
    used: usize,
    unused: usize,
}

impl BumpCounters {
    fn collect(bump: &Bump) -> BumpCounters {
        let allocated = bump.allocated_bytes();
        let used = unsafe { bump.iter_allocated_chunks_raw().map(|(_, size)| size).sum() };
        let unused = allocated.wrapping_sub(used);
        BumpCounters { used, unused }
    }
}

impl Allocative for Bump {
    fn visit<'a, 'b: 'a>(&self, visitor: &'a mut Visitor<'b>) {
        let mut visitor = visitor.enter_self_sized::<Self>();
        {
            let mut visitor = visitor.enter_unique(PTR_NAME, mem::size_of::<*const ()>());

            let counters = BumpCounters::collect(self);

            visitor.visit_simple(DATA_NAME.clone(), counters.used);
            visitor.visit_simple(UNUSED_CAPACITY_NAME.clone(), counters.unused);
            // TODO(nga): Ignores header size.
            visitor.exit();
        }
        visitor.exit();
    }
}

#[cfg(test)]
mod tests {
    use bumpalo::Bump;

    use crate::impls::bumpalo::BumpCounters;

    #[test]
    fn test_bumpalo() {
        let bump = Bump::new();
        bump.alloc(10);

        let counters = BumpCounters::collect(&bump);
        assert_eq!(4, counters.used);
        assert!(counters.unused > 100);

        bump.alloc_str("xx");
        let counters_2 = BumpCounters::collect(&bump);
        assert_eq!(6, counters_2.used);
        assert_eq!(
            counters.used + counters.unused,
            counters_2.used + counters_2.unused
        );
    }

    #[test]
    fn test_bumpalo_large() {
        let bump = Bump::new();
        for i in 0..1000 {
            bump.alloc(i);
        }
        // Make sure total is odd, and bump allocates odd.
        bump.alloc(true);
        let counters = BumpCounters::collect(&bump);
        assert_eq!(4001, counters.used);
        assert!(counters.unused > 0);
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

use crate::key::Key;

/// "Field" describing allocated but unused capacity (e.g. in `Vec`).
pub(crate) const UNUSED_CAPACITY_NAME: Key = Key::new("unused_capacity");

/// "Field" describing all capacity (e.g. in `Vec`).
pub(crate) const CAPACITY_NAME: Key = Key::new("capacity");

/// Generic pointee field in types like `Box`.
pub(crate) const PTR_NAME: Key = Key::new("ptr");

/// Generic name for useful data (e.g. in `Vec`).
pub(crate) const DATA_NAME: Key = Key::new("data");

pub(crate) const KEY_NAME: Key = Key::new("key");
pub(crate) const VALUE_NAME: Key = Key::new("value");


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

#![cfg(feature = "compact_str")]

use compact_str::CompactString;

use crate::allocative_trait::Allocative;
use crate::impls::common::PTR_NAME;
use crate::impls::common::UNUSED_CAPACITY_NAME;
use crate::key::Key;
use crate::visitor::Visitor;

impl Allocative for CompactString {
    fn visit<'a, 'b: 'a>(&self, visitor: &'a mut Visitor<'b>) {
        let mut visitor = visitor.enter_self_sized::<Self>();
        if self.is_heap_allocated() {
            let mut visitor = visitor.enter_unique(PTR_NAME, std::mem::size_of::<*const u8>());
            visitor.visit_simple(Key::new("str"), self.len());
            let unused_capacity = self.capacity() - self.len();
            visitor.visit_simple(UNUSED_CAPACITY_NAME, unused_capacity);
        }
        visitor.exit();
    }
}

#[cfg(test)]
mod tests {
    use compact_str::CompactString;

    use crate::golden::golden_test;

    #[test]
    fn test_inline() {
        golden_test!(&CompactString::from("abc"));
    }

    #[test]
    fn test_heap() {
        golden_test!(&CompactString::from("abc".repeat(100)));
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

#![cfg(feature = "dashmap")]

use std::hash::BuildHasher;
use std::hash::Hash;
use std::mem;

use dashmap::DashMap;
use dashmap::DashSet;

use crate::allocative_trait::Allocative;
use crate::impls::common::CAPACITY_NAME;
use crate::impls::common::DATA_NAME;
use crate::impls::common::KEY_NAME;
use crate::impls::common::PTR_NAME;
use crate::impls::common::UNUSED_CAPACITY_NAME;
use crate::impls::common::VALUE_NAME;
use crate::visitor::Visitor;

impl<K: Allocative + Eq + Hash, V: Allocative, S: BuildHasher + Clone> Allocative
    for DashMap<K, V, S>
{
    fn visit<'a, 'b: 'a>(&self, visitor: &'a mut Visitor<'b>) {
        let mut visitor = visitor.enter_self_sized::<Self>();
        let mut visitor2 = visitor.enter_unique(PTR_NAME, mem::size_of::<*const ()>());
        {
            let mut capacity_visitor =
                visitor2.enter(CAPACITY_NAME, self.capacity() * mem::size_of::<(K, V)>());
            for entry in self.iter() {
                capacity_visitor.visit_field(KEY_NAME, entry.key());
                capacity_visitor.visit_field(VALUE_NAME, entry.value());
            }
            capacity_visitor.visit_simple(
                UNUSED_CAPACITY_NAME,
                self.capacity().saturating_sub(self.len()) * mem::size_of::<(K, V)>(),
            );
            capacity_visitor.exit();
        }
        visitor2.exit();
        visitor.exit();
    }
}

impl<T: Allocative + Eq + Hash, S: BuildHasher + Clone> Allocative for DashSet<T, S> {
    fn visit<'a, 'b: 'a>(&self, visitor: &'a mut Visitor<'b>) {
        let mut visitor = visitor.enter_self_sized::<Self>();
        let mut visitor2 = visitor.enter_unique(PTR_NAME, mem::size_of::<*const ()>());
        {
            let mut capacity_visitor =
                visitor2.enter(CAPACITY_NAME, self.capacity() * mem::size_of::<T>());
            for entry in self.iter() {
                capacity_visitor.visit_field(DATA_NAME, entry.key());
            }
            capacity_visitor.visit_simple(
                UNUSED_CAPACITY_NAME,
                self.capacity().saturating_sub(self.len()) * mem::size_of::<T>(),
            );
            capacity_visitor.exit();
        }
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

#![cfg(feature = "either")]

use either::Either;

use crate::allocative_trait::Allocative;
use crate::key::Key;
use crate::visitor::Visitor;

impl<A: Allocative, B: Allocative> Allocative for Either<A, B> {
    fn visit<'a, 'b: 'a>(&self, visitor: &'a mut Visitor<'b>) {
        let mut visitor = visitor.enter_self_sized::<Self>();
        match self {
            Either::Left(a) => visitor.visit_field::<A>(Key::new("Left"), a),
            Either::Right(b) => visitor.visit_field::<B>(Key::new("Right"), b),
        }
    }
}

#[cfg(test)]
mod tests {
    use either::Either;

    use crate::golden::golden_test;

    #[test]
    fn test_golden() {
        golden_test!(&Either::<u32, String>::Left(1));
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

#![cfg(feature = "futures")]

use std::future::Future;
use std::mem;

use futures::future::Shared;
use futures::task::AtomicWaker;

use crate::allocative_trait::Allocative;
use crate::impls::common::PTR_NAME;
use crate::visitor::Visitor;

impl<F: Future + 'static> Allocative for Shared<F>
where
    F::Output: Allocative + Clone,
{
    fn visit<'a, 'b: 'a>(&self, visitor: &'a mut Visitor<'b>) {
        let mut visitor = visitor.enter_self_sized::<Self>();
        if let Some(result) = self.peek() {
            if let Some(mut visitor) = visitor.enter_shared(
                PTR_NAME,
                mem::size_of::<*const ()>(),
                result as *const _ as *const (),
            ) {
                result.visit(&mut visitor);
                visitor.exit();
            }
        }
        visitor.exit();
    }
}

impl Allocative for AtomicWaker {
    fn visit<'a, 'b: 'a>(&self, visitor: &'a mut Visitor<'b>) {
        visitor.enter_self_sized::<Self>().exit()
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

#![cfg(feature = "hashbrown")]

use std::mem;

use hashbrown::raw::RawTable;

use crate::Allocative;
use crate::Key;
use crate::Visitor;

const CAPACITY_NAME: Key = Key::new("capacity");

impl<T: Allocative> Allocative for RawTable<T> {
    fn visit<'a, 'b: 'a>(&self, visitor: &'a mut Visitor<'b>) {
        use crate::impls::common::DATA_NAME;
        use crate::impls::hashbrown_util;

        let mut visitor = visitor.enter_self_sized::<Self>();
        {
            let mut visitor = visitor.enter_unique(DATA_NAME, mem::size_of::<*const T>());
            {
                let mut visitor = visitor.enter(
                    CAPACITY_NAME,
                    hashbrown_util::raw_table_alloc_size_for_len::<T>(self.capacity()),
                );
                unsafe { visitor.visit_iter(self.iter().map(|e| e.as_ref())) };
                visitor.exit();
            }
            visitor.exit();
        }
        visitor.exit();
    }
}

#[cfg(test)]
mod tests {
    use std::collections::hash_map::DefaultHasher;
    use std::hash::Hash;
    use std::hash::Hasher;

    use hashbrown::raw::RawTable;

    use crate::golden::golden_test;

    fn hash<H: Hash>(value: &H) -> u64 {
        let mut hasher = DefaultHasher::new();
        value.hash(&mut hasher);
        hasher.finish()
    }

    #[test]
    fn test_raw_table() {
        let mut table = RawTable::with_capacity(100);
        for i in 0..100 {
            table.insert(hash(&i.to_string()), i.to_string(), hash);
        }

        golden_test!(&table);
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

use std::mem;

/// Approximate allocated memory for hashbrown `RawTable`.
#[allow(dead_code)]
pub(crate) fn raw_table_alloc_size_for_len<T>(len: usize) -> usize {
    let buckets = if len == 0 {
        0
    } else if len < 4 {
        4
    } else {
        len.next_power_of_two()
    };
    let size_of_control_byte = 1;
    (mem::size_of::<T>() + size_of_control_byte) * buckets
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

#![cfg(feature = "indexmap")]

use std::mem;

use indexmap::IndexMap;
use indexmap::IndexSet;

use crate::allocative_trait::Allocative;
use crate::impls::common::UNUSED_CAPACITY_NAME;
use crate::impls::hashbrown_util::raw_table_alloc_size_for_len;
use crate::key::Key;
use crate::visitor::Visitor;

/// Add approximate allocations for hashbrown `RawTable`.
fn add_raw_table_for_len<T>(visitor: &mut Visitor, len: usize) {
    if len != 0 {
        // We don't depend on `RawTable`, so we don't know the size of `RawTable`.
        let size_of_raw_table = mem::size_of::<usize>();
        let mut visitor = visitor.enter_unique(Key::new("raw_table"), size_of_raw_table);
        visitor.visit_simple(Key::new("alloc"), raw_table_alloc_size_for_len::<T>(len));
        visitor.exit();
    }
}

impl<T: Allocative> Allocative for IndexSet<T> {
    fn visit<'a, 'b: 'a>(&self, visitor: &'a mut Visitor<'b>) {
        let mut visitor = visitor.enter_self_sized::<Self>();
        {
            let mut visitor = visitor.enter_unique(Key::new("data"), mem::size_of::<*const ()>());
            for v in self {
                visitor.visit_field(Key::new("value"), v);
            }
            let unused_capacity = self.capacity() - self.len();
            visitor.visit_simple(UNUSED_CAPACITY_NAME, unused_capacity * mem::size_of::<T>());
            visitor.exit();
        }
        add_raw_table_for_len::<usize>(&mut visitor, self.len());
    }
}

impl<K: Allocative, V: Allocative> Allocative for IndexMap<K, V> {
    fn visit<'a, 'b: 'a>(&self, visitor: &'a mut Visitor<'b>) {
        let mut visitor = visitor.enter_self_sized::<Self>();
        {
            let mut visitor = visitor.enter_unique(Key::new("data"), mem::size_of::<*const ()>());
            for (k, v) in self {
                visitor.visit_field(Key::new("key"), k);
                visitor.visit_field(Key::new("value"), v);
            }
            let unused_capacity = self.capacity() - self.len();
            visitor.visit_simple(
                UNUSED_CAPACITY_NAME,
                unused_capacity * mem::size_of::<(K, V)>(),
            );
            visitor.exit();
        }
        add_raw_table_for_len::<usize>(&mut visitor, self.len());
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

#![cfg(feature = "parking_lot")]

use parking_lot::lock_api::Mutex;
use parking_lot::lock_api::RawMutex;
use parking_lot::lock_api::RawRwLock;
use parking_lot::lock_api::RwLock;

use crate::allocative_trait::Allocative;
use crate::key::Key;
use crate::visitor::Visitor;

impl<R: RawMutex + 'static, T: Allocative> Allocative for Mutex<R, T> {
    fn visit<'a, 'b: 'a>(&self, visitor: &'a mut Visitor<'b>) {
        let mut visitor = visitor.enter_self_sized::<Self>();
        if let Some(data) = self.try_lock() {
            visitor.visit_field(Key::new("data"), &*data);
        }
        visitor.exit();
    }
}

impl<R: RawRwLock + 'static, T: Allocative> Allocative for RwLock<R, T> {
    fn visit<'a, 'b: 'a>(&self, visitor: &'a mut Visitor<'b>) {
        let mut visitor = visitor.enter_self_sized::<Self>();
        if let Some(data) = self.try_read() {
            visitor.visit_field(Key::new("data"), &*data);
        }
        visitor.exit();
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

//! Manual implementations of `Allocative` for various types.

mod anyhow;
mod bumpalo;
pub(crate) mod common;
mod compact_str;
mod dashmap;
mod either;
mod futures;
pub(crate) mod hashbrown;
pub(crate) mod hashbrown_util;
mod indexmap;
mod lock_api;
mod num_bigint;
mod once_cell;
mod parking_lot;
mod prost_types;
mod relative_path;
mod slab;
mod smallvec;
mod sorted_vector_map;
mod std;
mod tokio;
mod triomphe;


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

#![cfg(feature = "num-bigint")]

use std::mem;

use num_bigint::BigInt;
use num_bigint::BigUint;

use crate::impls::common::DATA_NAME;
use crate::impls::common::PTR_NAME;
use crate::Allocative;
use crate::Visitor;

impl Allocative for BigInt {
    fn visit<'a, 'b: 'a>(&self, visitor: &'a mut Visitor<'b>) {
        let mut visitor = visitor.enter_self_sized::<Self>();
        {
            let mut visitor = visitor.enter_unique(PTR_NAME, mem::size_of::<*const ()>());
            // We don't know capacity, so just use the length.
            visitor.visit_simple(
                DATA_NAME,
                self.iter_u64_digits().count() * mem::size_of::<u64>(),
            );
            visitor.exit();
        }
        visitor.exit();
    }
}

impl Allocative for BigUint {
    fn visit<'a, 'b: 'a>(&self, visitor: &'a mut Visitor<'b>) {
        let mut visitor = visitor.enter_self_sized::<Self>();
        {
            let mut visitor = visitor.enter_unique(PTR_NAME, mem::size_of::<*const ()>());
            // We don't know capacity, so just use the length.
            visitor.visit_simple(
                DATA_NAME,
                self.iter_u64_digits().count() * mem::size_of::<u64>(),
            );
            visitor.exit();
        }
        visitor.exit();
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

#![cfg(feature = "once_cell")]

use crate::allocative_trait::Allocative;
use crate::visitor::Visitor;

impl<T: Allocative> Allocative for once_cell::sync::OnceCell<T> {
    fn visit<'a, 'b: 'a>(&self, visitor: &'a mut Visitor<'b>) {
        let mut visitor = visitor.enter_self_sized::<Self>();
        if let Some(val) = self.get() {
            val.visit(&mut visitor);
        }
        visitor.exit();
    }
}

impl<T: Allocative> Allocative for once_cell::sync::Lazy<T> {
    fn visit<'a, 'b: 'a>(&self, visitor: &'a mut Visitor<'b>) {
        let mut visitor = visitor.enter_self_sized::<Self>();
        if let Some(val) = once_cell::sync::Lazy::get(self) {
            val.visit(&mut visitor);
        }
        visitor.exit();
    }
}

impl<T: Allocative> Allocative for once_cell::unsync::OnceCell<T> {
    fn visit<'a, 'b: 'a>(&self, visitor: &'a mut Visitor<'b>) {
        let mut visitor = visitor.enter_self_sized::<Self>();
        if let Some(val) = self.get() {
            val.visit(&mut visitor);
        }
        visitor.exit();
    }
}

impl<T: Allocative> Allocative for once_cell::unsync::Lazy<T> {
    fn visit<'a, 'b: 'a>(&self, visitor: &'a mut Visitor<'b>) {
        let mut visitor = visitor.enter_self_sized::<Self>();
        if let Some(val) = once_cell::unsync::Lazy::get(self) {
            val.visit(&mut visitor);
        }
        visitor.exit();
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

#![cfg(feature = "parking_lot")]

use parking_lot::RawMutex;
use parking_lot::RawRwLock;

use crate::allocative_trait::Allocative;
use crate::visitor::Visitor;

impl Allocative for RawMutex {
    fn visit<'a, 'b: 'a>(&self, visitor: &'a mut Visitor<'b>) {
        visitor.enter_self_sized::<Self>().exit();
    }
}

impl Allocative for RawRwLock {
    fn visit<'a, 'b: 'a>(&self, visitor: &'a mut Visitor<'b>) {
        visitor.enter_self_sized::<Self>().exit();
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

#![cfg(feature = "prost-types")]

use prost_types::Duration;
use prost_types::Timestamp;

use crate::allocative_trait::Allocative;
use crate::visitor::Visitor;

impl Allocative for Timestamp {
    fn visit<'a, 'b: 'a>(&self, visitor: &'a mut Visitor<'b>) {
        visitor.enter_self_sized::<Self>().exit();
    }
}

impl Allocative for Duration {
    fn visit<'a, 'b: 'a>(&self, visitor: &'a mut Visitor<'b>) {
        visitor.enter_self_sized::<Self>().exit();
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

#![cfg(feature = "relative-path")]

use std::mem;

use relative_path::RelativePathBuf;

use crate::allocative_trait::Allocative;
use crate::impls::common::PTR_NAME;
use crate::key::Key;
use crate::visitor::Visitor;

impl Allocative for RelativePathBuf {
    fn visit<'a, 'b: 'a>(&self, visitor: &'a mut Visitor<'b>) {
        let mut visitor = visitor.enter_self_sized::<Self>();
        {
            let mut visitor = visitor.enter_unique(PTR_NAME, mem::size_of::<*const u8>());
            visitor.visit_simple(Key::new("chars"), self.as_str().len());
            // TODO(nga): spare capacity.
            visitor.exit();
        }
        visitor.exit();
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

#![cfg(feature = "slab")]

use std::mem;

use slab::Slab;

use crate::allocative_trait::Allocative;
use crate::impls::common::PTR_NAME;
use crate::impls::common::UNUSED_CAPACITY_NAME;
use crate::visitor::Visitor;

impl<T> Allocative for Slab<T>
where
    T: Allocative,
{
    fn visit<'a, 'b: 'a>(&self, visitor: &'a mut Visitor<'b>) {
        let mut visitor = visitor.enter_self_sized::<Self>();

        if self.capacity() != 0 && mem::size_of::<T>() != 0 {
            let mut visitor = visitor.enter_unique(PTR_NAME, mem::size_of::<*const T>());
            // NOTE: this ignores the size of the 'Entry' within the slab, but it's probably okay
            // as a good approximation of the size.
            visitor.visit_iter(self.iter().map(|(_k, v)| v));
            visitor.visit_simple(
                UNUSED_CAPACITY_NAME,
                (self.capacity() - self.len()) * mem::size_of::<T>(),
            );
        }
        visitor.exit();
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

#![cfg(feature = "smallvec")]

use smallvec::Array;
use smallvec::SmallVec;

use crate::allocative_trait::Allocative;
use crate::impls::common::PTR_NAME;
use crate::key::Key;
use crate::visitor::Visitor;

impl<A> Allocative for SmallVec<A>
where
    A: Array + 'static,
    A::Item: Allocative,
{
    fn visit<'a, 'b: 'a>(&self, visitor: &'a mut Visitor<'b>) {
        let mut visitor = visitor.enter_self_sized::<Self>();
        if self.spilled() {
            let mut visitor = visitor.enter_unique(PTR_NAME, std::mem::size_of::<*const A::Item>());
            for item in self {
                visitor.visit_field(Key::new("data"), item);
            }
            // TODO(nga): spare capacity.
            visitor.exit();
        } else {
            for item in self {
                visitor.visit_field(Key::new("data"), item);
            }
        }
        visitor.exit();
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn test() {
        // No test here because sizes depend on whether "union" feature is enabled.
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

#![cfg(feature = "sorted_vector_map")]

use sorted_vector_map::SortedVectorMap;
use sorted_vector_map::SortedVectorSet;

use crate::allocative_trait::Allocative;
use crate::visitor::Visitor;

impl<K: Allocative + Ord, V: Allocative> Allocative for SortedVectorMap<K, V> {
    fn visit<'a, 'b: 'a>(&self, visitor: &'a mut Visitor<'b>) {
        let mut visitor = visitor.enter_self_sized::<Self>();
        visitor.visit_generic_map_fields(self.iter());
        // TODO(nga): spare capacity.
        visitor.exit();
    }
}

impl<V: Allocative + Ord> Allocative for SortedVectorSet<V> {
    fn visit<'a, 'b: 'a>(&self, visitor: &'a mut Visitor<'b>) {
        let mut visitor = visitor.enter_self_sized::<Self>();
        visitor.visit_generic_set_fields(self.iter());
        // TODO(nga): spare capacity.
        visitor.exit();
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

#![cfg(feature = "tokio")]

use tokio::sync::Mutex;
use tokio::sync::RwLock;

use crate::Allocative;
use crate::Key;
use crate::Visitor;

impl<T: Allocative> Allocative for RwLock<T> {
    fn visit<'a, 'b: 'a>(&self, visitor: &'a mut Visitor<'b>) {
        let mut visitor = visitor.enter_self_sized::<Self>();
        if let Ok(data) = self.try_read() {
            visitor.visit_field::<T>(Key::new("data"), &*data);
        }
        visitor.exit();
    }
}

impl<T: Allocative> Allocative for Mutex<T> {
    fn visit<'a, 'b: 'a>(&self, visitor: &'a mut Visitor<'b>) {
        let mut visitor = visitor.enter_self_sized::<Self>();
        if let Ok(data) = self.try_lock() {
            visitor.visit_field::<T>(Key::new("data"), &*data);
        }
        visitor.exit();
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

#![cfg(feature = "triomphe")]

use std::alloc::Layout;
use std::mem;
use std::sync::atomic::AtomicUsize;

use triomphe::Arc;
use triomphe::HeaderSlice;
use triomphe::HeaderWithLength;
use triomphe::ThinArc;

use crate::impls::common::PTR_NAME;
use crate::Allocative;
use crate::Key;
use crate::Visitor;

impl<H: Allocative, T: Allocative + ?Sized> Allocative for HeaderSlice<H, T> {
    fn visit<'a, 'b: 'a>(&self, visitor: &'a mut Visitor<'b>) {
        let mut visitor = visitor.enter_self(self);
        visitor.visit_field(Key::new("header"), &self.header);
        visitor.visit_field(Key::new("slice"), &self.slice);
        visitor.exit();
    }
}

impl<H: Allocative> Allocative for HeaderWithLength<H> {
    fn visit<'a, 'b: 'a>(&self, visitor: &'a mut Visitor<'b>) {
        let mut visitor = visitor.enter_self(self);
        visitor.visit_field(Key::new("header"), &self.header);
        visitor.visit_field(Key::new("len"), &self.length);
        visitor.exit();
    }
}

impl<H: Allocative, T: Allocative> Allocative for ThinArc<H, T> {
    fn visit<'a, 'b: 'a>(&self, visitor: &'a mut Visitor<'b>) {
        let mut visitor = visitor.enter_self_sized::<Self>();
        {
            if let Some(mut visitor) = visitor.enter_shared(
                PTR_NAME,
                mem::size_of::<*mut u8>(),
                ThinArc::ptr(self) as *const (),
            ) {
                #[repr(C)]
                struct ThinArcInnerRepr<H> {
                    _counter: AtomicUsize,
                    _header: H,
                    _len: usize,
                }
                let size = Layout::new::<ThinArcInnerRepr<H>>()
                    .extend(Layout::array::<T>(self.slice.len()).unwrap())
                    .unwrap()
                    .0
                    .size();
                {
                    let mut visitor =
                        visitor.enter(Key::for_type_name::<ThinArcInnerRepr<H>>(), size);
                    visitor.visit_field::<HeaderSlice<HeaderWithLength<H>, [T]>>(
                        Key::new("data"),
                        self,
                    );
                }
                visitor.exit();
            }
        }
        visitor.exit();
    }
}

impl<T: Allocative + ?Sized> Allocative for Arc<T> {
    fn visit<'a, 'b: 'a>(&self, visitor: &'a mut Visitor<'b>) {
        let mut visitor = visitor.enter_self_sized::<Self>();
        {
            if let Some(mut visitor) = visitor.enter_shared(
                PTR_NAME,
                mem::size_of::<*mut T>(),
                Arc::heap_ptr(self) as *const (),
            ) {
                struct ArcInnerRepr {
                    _counter: AtomicUsize,
                }
                let size = Layout::new::<ArcInnerRepr>()
                    .extend(
                        Layout::from_size_align(
                            mem::size_of_val::<T>(self),
                            mem::align_of_val::<T>(self),
                        )
                        .unwrap(),
                    )
                    .unwrap()
                    .0
                    .size();
                {
                    let mut visitor = visitor.enter(Key::for_type_name::<ArcInnerRepr>(), size);
                    visitor.visit_field::<T>(Key::new("data"), self);
                    visitor.exit();
                }
                visitor.exit();
            }
        }
        visitor.exit();
    }
}

#[cfg(test)]
mod tests {
    use triomphe::Arc;

    use crate::golden::golden_test;

    #[test]
    fn test_simple() {
        let arc = Arc::new("abc".to_owned());
        golden_test!(&arc);
    }

    #[test]
    fn test_shared() {
        let arc = Arc::new("a".repeat(100));
        let vec = vec![arc.clone(), arc.clone(), arc];
        golden_test!(&vec);
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

use std::cell::RefCell;

use crate::impls::common::DATA_NAME;
use crate::Allocative;
use crate::Visitor;

impl<T: Allocative> Allocative for RefCell<T> {
    fn visit<'a, 'b: 'a>(&self, visitor: &'a mut Visitor<'b>) {
        let mut visitor = visitor.enter_self_sized::<Self>();
        if let Ok(v) = self.try_borrow() {
            visitor.visit_field(DATA_NAME, &*v);
        }
        visitor.exit();
    }
}

#[cfg(test)]
mod tests {
    use std::cell::RefCell;

    use crate::golden::golden_test;

    #[test]
    fn test_default() {
        golden_test!(&RefCell::new("abc".to_owned()))
    }

    #[test]
    fn test_borrowed() {
        let cell = RefCell::new("abc".to_owned());
        let _lock = cell.borrow_mut();
        golden_test!(&cell)
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

use std::collections::BTreeMap;
use std::collections::BTreeSet;
use std::collections::HashMap;
use std::collections::HashSet;

use crate::allocative_trait::Allocative;
use crate::visitor::Visitor;

impl<K: Allocative, V: Allocative> Allocative for BTreeMap<K, V> {
    fn visit<'a, 'b: 'a>(&self, visitor: &'a mut Visitor<'b>) {
        let mut visitor = visitor.enter_self_sized::<Self>();
        visitor.visit_generic_map_fields(self);
        visitor.exit();
    }
}

impl<K: Allocative> Allocative for BTreeSet<K> {
    fn visit<'a, 'b: 'a>(&self, visitor: &'a mut Visitor<'b>) {
        let mut visitor = visitor.enter_self_sized::<Self>();
        visitor.visit_generic_set_fields(self);
        visitor.exit();
    }
}

impl<K: Allocative, V: Allocative, S> Allocative for HashMap<K, V, S> {
    fn visit<'a, 'b: 'a>(&self, visitor: &'a mut Visitor<'b>) {
        let mut visitor = visitor.enter_self_sized::<Self>();
        // TODO: can do better extra capacity.
        visitor.visit_generic_map_fields(self);
        visitor.exit();
    }
}

impl<K: Allocative, S> Allocative for HashSet<K, S> {
    fn visit<'a, 'b: 'a>(&self, visitor: &'a mut Visitor<'b>) {
        let mut visitor = visitor.enter_self_sized::<Self>();
        // TODO: can do better extra capacity.
        visitor.visit_generic_set_fields(self);
        visitor.exit();
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

use crate::allocative_trait::Allocative;
use crate::visitor::Visitor;

impl<R> Allocative for fn() -> R {
    fn visit<'a, 'b: 'a>(&self, visitor: &'a mut Visitor<'b>) {
        visitor.visit_simple_sized::<Self>();
    }
}

impl<R, A> Allocative for fn(A) -> R {
    fn visit<'a, 'b: 'a>(&self, visitor: &'a mut Visitor<'b>) {
        visitor.visit_simple_sized::<Self>();
    }
}
impl<R, A, B> Allocative for fn(A, B) -> R {
    fn visit<'a, 'b: 'a>(&self, visitor: &'a mut Visitor<'b>) {
        visitor.visit_simple_sized::<Self>();
    }
}
impl<R, A, B, C> Allocative for fn(A, B, C) -> R {
    fn visit<'a, 'b: 'a>(&self, visitor: &'a mut Visitor<'b>) {
        visitor.visit_simple_sized::<Self>();
    }
}

impl<R, A, B, C, D> Allocative for fn(A, B, C, D) -> R {
    fn visit<'a, 'b: 'a>(&self, visitor: &'a mut Visitor<'b>) {
        visitor.visit_simple_sized::<Self>();
    }
}

impl<R, A, B, C, D, E> Allocative for fn(A, B, C, D, E) -> R {
    fn visit<'a, 'b: 'a>(&self, visitor: &'a mut Visitor<'b>) {
        visitor.visit_simple_sized::<Self>();
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

use std::mem::ManuallyDrop;

use crate::Allocative;
use crate::Key;
use crate::Visitor;

impl<T: Allocative + ?Sized> Allocative for ManuallyDrop<T> {
    fn visit<'a, 'b: 'a>(&self, visitor: &'a mut Visitor<'b>) {
        let mut visitor = visitor.enter_self(self);
        visitor.visit_field::<T>(Key::new("inner"), self);
        visitor.exit();
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under both the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree and the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree.
 */

mod cell;
mod collections;
mod function;
mod mem;
mod primitive;
mod sync;
mod time;
mod tuple;
mod unsorted;