/*
 * 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::Arc;

use allocative::Allocative;
use anyhow::Context;
use async_trait::async_trait;
use buck2_build_api::analysis::calculation::RuleAnalysisCalculation;
use buck2_build_api::interpreter::rule_defs::provider::builtin::platform_info::PlatformInfo;
use buck2_build_api::interpreter::rule_defs::provider::collection::FrozenProviderCollectionValue;
use buck2_build_api::transition::TransitionCalculation;
use buck2_build_api::transition::TRANSITION_CALCULATION;
use buck2_common::result::SharedResult;
use buck2_common::result::ToSharedResultExt;
use buck2_common::result::ToUnsharedResultExt;
use buck2_core::configuration::cfg_diff::cfg_diff;
use buck2_core::configuration::data::ConfigurationData;
use buck2_core::configuration::transition::applied::TransitionApplied;
use buck2_core::configuration::transition::id::TransitionId;
use buck2_core::target::label::TargetLabel;
use buck2_events::dispatch::get_dispatcher;
use buck2_interpreter::dice::starlark_provider::with_starlark_eval_provider;
use buck2_interpreter::print_handler::EventDispatcherPrintHandler;
use buck2_interpreter::starlark_profiler::StarlarkProfilerOrInstrumentation;
use buck2_node::attrs::coerced_attr::CoercedAttr;
use buck2_node::attrs::display::AttrDisplayWithContextExt;
use buck2_node::attrs::inspect_options::AttrInspectOptions;
use buck2_node::nodes::unconfigured::TargetNode;
use buck2_util::collections::ordered_map::OrderedMap;
use buck2_util::collections::sorted_map::SortedMap;
use derive_more::Display;
use dice::DiceComputations;
use dice::Key;
use dupe::Dupe;
use gazebo::prelude::*;
use itertools::Itertools;
use more_futures::cancellation::CancellationContext;
use starlark::environment::Module;
use starlark::eval::Evaluator;
use starlark::values::dict::DictOf;
use starlark::values::structs::AllocStruct;
use starlark::values::UnpackValue;
use starlark::values::Value;
use thiserror::Error;

use crate::coerced_attr::CoercedAttrResolveExt;
use crate::transition::calculation_fetch_transition::FetchTransition;
use crate::transition::starlark::FrozenTransition;

#[derive(Error, Debug)]
enum ApplyTransitionError {
    #[error("transition function not marked as `split` must return a `PlatformInfo`")]
    NonSplitTransitionMustReturnPlatformInfo,
    #[error("transition function marked `split` must return a dict of `str` to `PlatformInfo`")]
    SplitTransitionMustReturnDict,
    #[error(
        "transition applied again to transition output \
        did not produce identical `PlatformInfo`, the diff:\n{0}"
    )]
    SplitTransitionAgainDifferentPlatformInfo(String),
    #[error(
        "Transition object is not consistent with transition computation params, \
        this may happen because of how DICE recomputation works, \
        a user should never see this message"
    )]
    InconsistentTransitionAndComputation,
}

fn call_transition_function<'v>(
    transition: &FrozenTransition,
    conf: &ConfigurationData,
    refs: Value<'v>,
    attrs: Option<Value<'v>>,
    eval: &mut Evaluator<'v, '_>,
) -> anyhow::Result<TransitionApplied> {
    let mut args = vec![
        (
            "platform",
            eval.heap()
                .alloc_complex(PlatformInfo::from_configuration(conf, eval.heap())?),
        ),
        ("refs", refs),
    ];
    if let Some(attrs) = attrs {
        args.push(("attrs", attrs));
    }
    let new_platforms = eval.eval_function(transition.implementation.to_value(), &[], &args)?;
    if transition.split {
        match DictOf::<&str, &PlatformInfo>::unpack_value(new_platforms) {
            Some(dict) => {
                let mut split = OrderedMap::new();
                for (k, v) in dict.to_dict() {
                    let prev = split.insert(k.to_owned(), v.to_configuration()?);
                    assert!(prev.is_none());
                }
                Ok(TransitionApplied::Split(SortedMap::from(split)))
            }
            None => Err(ApplyTransitionError::SplitTransitionMustReturnDict.into()),
        }
    } else {
        match <&PlatformInfo>::unpack_value(new_platforms) {
            Some(platform) => Ok(TransitionApplied::Single(platform.to_configuration()?)),
            None => Err(ApplyTransitionError::NonSplitTransitionMustReturnPlatformInfo.into()),
        }
    }
}

async fn do_apply_transition(
    ctx: &DiceComputations,
    attrs: Option<&[Option<CoercedAttr>]>,
    conf: &ConfigurationData,
    transition_id: &TransitionId,
) -> SharedResult<TransitionApplied> {
    let transition = ctx.fetch_transition(transition_id).await?;
    let module = Module::new();
    let mut refs = Vec::with_capacity(transition.refs.len());
    let mut refs_refs = Vec::new();
    for (s, t) in &transition.refs {
        let provider_collection_value = ctx.fetch_transition_function_reference(t).await?;
        refs.push((
            *s,
            // This is safe because we store a reference to provider collection in `refs_refs`.
            unsafe { provider_collection_value.value().to_frozen_value() },
        ));
        refs_refs.push(provider_collection_value);
    }
    let print = EventDispatcherPrintHandler(get_dispatcher());
    with_starlark_eval_provider(
        ctx,
        &mut StarlarkProfilerOrInstrumentation::disabled(),
        format!("transition:{}", transition_id),
        move |provider| {
            let mut eval = provider.make(&module)?;
            eval.set_print_handler(&print);
            let refs = module.heap().alloc(AllocStruct(refs));
            let attrs = match (&transition.attrs, attrs) {
                (Some(names), Some(values)) => {
                    if names.len() != values.len() {
                        return Err(
                            ApplyTransitionError::InconsistentTransitionAndComputation.into()
                        );
                    }
                    let mut attrs = Vec::with_capacity(names.len());
                    for (name, value) in names.iter().zip(values.iter()) {
                        let value = match value {
                            Some(value) => value.to_value(module.heap()).with_context(|| {
                                format!(
                                    "Error converting attribute `{}={}` to Starlark value",
                                    name.as_str(),
                                    value.as_display_no_ctx(),
                                )
                            })?,
                            None => Value::new_none(),
                        };
                        attrs.push((*name, value));
                    }
                    Some(module.heap().alloc(AllocStruct(attrs)))
                }
                (None, None) => None,
                (Some(_), None) | (None, Some(_)) => {
                    return Err(ApplyTransitionError::InconsistentTransitionAndComputation.into());
                }
            };
            match call_transition_function(&transition, conf, refs, attrs, &mut eval)? {
                TransitionApplied::Single(new) => {
                    let new_2 =
                        match call_transition_function(&transition, &new, refs, attrs, &mut eval)
                            .context("applying transition again on transition output")
                            .shared_error()?
                        {
                            TransitionApplied::Single(new_2) => new_2,
                            TransitionApplied::Split(_) => {
                                unreachable!(
                                    "split transition filtered out in call_transition_function"
                                )
                            }
                        };
                    if let Err(diff) = cfg_diff(&new, &new_2) {
                        return Err(
                            ApplyTransitionError::SplitTransitionAgainDifferentPlatformInfo(diff)
                                .into(),
                        );
                    }
                    Ok(TransitionApplied::Single(new))
                }
                TransitionApplied::Split(split) => {
                    // Not validating split transitions yet, because it's not 100% clear what to validate,
                    // and because it is not that important, because split transitions
                    // are not used in per-rule transitions.
                    Ok(TransitionApplied::Split(split))
                }
            }
        },
    )
    .await
    .shared_error()
}

#[async_trait]
pub(crate) trait ApplyTransition {
    /// Resolve `refs` param of transition function.
    async fn fetch_transition_function_reference(
        &self,
        target: &TargetLabel,
    ) -> SharedResult<FrozenProviderCollectionValue>;
}

#[async_trait]
impl ApplyTransition for DiceComputations {
    async fn fetch_transition_function_reference(
        &self,
        target: &TargetLabel,
    ) -> SharedResult<FrozenProviderCollectionValue> {
        Ok(self
            .get_configuration_analysis_result(target)
            .await?
            .providers()
            .dupe())
    }
}

struct TransitionCalculationImpl;

pub(crate) fn init_transition_calculation() {
    TRANSITION_CALCULATION.init(&TransitionCalculationImpl);
}

#[async_trait]
impl TransitionCalculation for TransitionCalculationImpl {
    async fn apply_transition(
        &self,
        ctx: &DiceComputations,
        target_node: &TargetNode,
        cfg: &ConfigurationData,
        transition_id: &TransitionId,
    ) -> anyhow::Result<Arc<TransitionApplied>> {
        #[derive(Debug, Eq, PartialEq, Hash, Clone, Display, Allocative)]
        #[display(fmt = "{} ({}){}", transition_id, cfg, "self.fmt_attrs()")]
        struct TransitionKey {
            cfg: ConfigurationData,
            transition_id: TransitionId,
            /// Attributes which requested by transition function, not all attributes.
            /// The attr value index is the index of attribute in transition object.
            /// Attributes are added here so multiple targets with the equal attributes
            /// (e.g. the same `java_version = 14`) share the transition computation.
            attrs: Option<Vec<Option<CoercedAttr>>>,
        }

        impl TransitionKey {
            fn fmt_attrs(&self) -> String {
                if let Some(attrs) = &self.attrs {
                    format!(
                        " [{}]",
                        attrs
                            .iter()
                            .map(|a| {
                                if let Some(attr) = a {
                                    attr.as_display_no_ctx().to_string()
                                } else {
                                    "None".to_owned()
                                }
                            })
                            .join(", ")
                    )
                } else {
                    String::new()
                }
            }
        }

        #[async_trait]
        impl Key for TransitionKey {
            type Value = SharedResult<Arc<TransitionApplied>>;

            async fn compute(
                &self,
                ctx: &DiceComputations,
                _cancellation: &CancellationContext,
            ) -> Self::Value {
                let v: SharedResult<_> = try {
                    do_apply_transition(ctx, self.attrs.as_deref(), &self.cfg, &self.transition_id)
                        .await?
                };

                Ok(Arc::new(v.with_context(|| {
                    format!("Error computing transition `{}`", self)
                })?))
            }

            fn equality(x: &Self::Value, y: &Self::Value) -> bool {
                if let (Ok(x), Ok(y)) = (x, y) {
                    x == y
                } else {
                    false
                }
            }
        }

        let transition = ctx.fetch_transition(transition_id).await?;

        #[allow(clippy::manual_map)]
        let attrs = if let Some(attrs) = &transition.attrs {
            Some(attrs.try_map(|attr| {
                target_node
                    .attr(attr, AttrInspectOptions::All)
                    .map(|o| o.cloned())
            })?)
        } else {
            None
        };

        let key = TransitionKey {
            cfg: cfg.dupe(),
            transition_id: transition_id.clone(),
            attrs,
        };

        ctx.compute(&key).await?.unshared_error()
    }
}


/*
 * 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 async_trait::async_trait;
use buck2_common::result::SharedError;
use buck2_common::result::SharedResult;
use buck2_core::configuration::transition::id::TransitionId;
use buck2_interpreter::load_module::InterpreterCalculation;
use dice::DiceComputations;
use starlark::values::OwnedFrozenValueTyped;
use thiserror::Error;

use crate::transition::starlark::FrozenTransition;

/// Fetch transition object (function plus context) by id.
#[async_trait]
pub(crate) trait FetchTransition {
    /// Fetch transition object by id.
    async fn fetch_transition(
        &self,
        id: &TransitionId,
    ) -> SharedResult<OwnedFrozenValueTyped<FrozenTransition>>;
}

#[derive(Debug, Error)]
enum FetchTransitionError {
    #[error("Transition object not found by id {:?}", _0)]
    NotFound(TransitionId),
    #[error("Transition id {:?} is resolved to not transition object", _0)]
    NotTransition(TransitionId),
}

#[async_trait]
impl FetchTransition for DiceComputations {
    async fn fetch_transition(
        &self,
        id: &TransitionId,
    ) -> SharedResult<OwnedFrozenValueTyped<FrozenTransition>> {
        let module = self.get_loaded_module_from_import_path(&id.path).await?;
        let transition = module
            .env()
            // This is a hashmap lookup, so we are not caching the result in DICE.
            .get_any_visibility(&id.name)
            .map_err(|_| SharedError::new(FetchTransitionError::NotFound(id.clone())))?
            .0;

        Ok(transition
            .downcast()
            .map_err(|_| SharedError::new(FetchTransitionError::NotTransition(id.clone())))?)
    }
}


/*
 * 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.
 */

pub(crate) mod calculation_apply_transition;
pub(crate) mod calculation_fetch_transition;
pub(crate) mod starlark;


/*
 * 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 std::collections::HashSet;
use std::sync::Arc;

use allocative::Allocative;
use buck2_core::bzl::ImportPath;
use buck2_core::configuration::transition::id::TransitionId;
use buck2_core::target::label::TargetLabel;
use buck2_interpreter::build_context::STARLARK_PATH_FROM_BUILD_CONTEXT;
use buck2_interpreter::coerce::COERCE_TARGET_LABEL;
use buck2_interpreter::functions::transition::REGISTER_TRANSITION;
use buck2_interpreter::types::transition::TransitionValue;
use derive_more::Display;
use dupe::Dupe;
use gazebo::prelude::*;
use itertools::Itertools;
use starlark::any::ProvidesStaticType;
use starlark::collections::SmallMap;
use starlark::environment::GlobalsBuilder;
use starlark::eval::Evaluator;
use starlark::starlark_complex_values;
use starlark::starlark_module;
use starlark::values::dict::DictOf;
use starlark::values::starlark_value;
use starlark::values::Demand;
use starlark::values::Freeze;
use starlark::values::Freezer;
use starlark::values::FrozenStringValue;
use starlark::values::FrozenValue;
use starlark::values::NoSerialize;
use starlark::values::StarlarkValue;
use starlark::values::StringValue;
use starlark::values::Trace;
use starlark::values::Value;

#[derive(Debug, thiserror::Error)]
enum TransitionError {
    #[error("Transition must be assigned to a variable, e.g. `android_cpus = transition(...)`")]
    TransitionNotAssigned,
    #[error("`transition` can only be declared in .bzl files")]
    OnlyBzl,
    #[error(
        "`transition` implementation must be def with two parameters: `platform` and `refs`, \
        but it is not a def"
    )]
    MustBeDefNotDef,
    #[error(
        "`transition` implementation must be def with parameters: {}, \
        but it is a def with signature `{0}`",
        _1.iter().map(|s| format!("`{}`", s)).join(", "))]
    MustBeDefWrongSig(String, &'static [&'static str]),
    #[error("Non-unique list of attrs")]
    NonUniqueAttrs,
}

/// Wrapper for `TargetLabel` which is `Trace`.
#[derive(Trace, Debug, Allocative)]
struct TargetLabelTrace(TargetLabel);

#[derive(Debug, Display, Trace, ProvidesStaticType, NoSerialize, Allocative)]
#[display(fmt = "transition")]
pub(crate) struct Transition<'v> {
    /// The name of this transition, filled in by `export_as()`. This must be set before this
    /// object can be used.
    id: RefCell<Option<Arc<TransitionId>>>,
    /// The path where this `Transition` is created and assigned.
    path: ImportPath,
    implementation: Value<'v>,
    /// Providers needed for the transition function. A map by target label.
    refs: SmallMap<StringValue<'v>, TargetLabelTrace>,
    /// Transition function accesses theses attributes.
    attrs: Option<Vec<StringValue<'v>>>,
    /// Is this split transition? I. e. transition to multiple configurations.
    split: bool,
}

#[derive(Debug, Display, ProvidesStaticType, NoSerialize, Allocative)]
#[display(fmt = "transition")]
pub(crate) struct FrozenTransition {
    id: Arc<TransitionId>,
    pub(crate) implementation: FrozenValue,
    pub(crate) refs: SmallMap<FrozenStringValue, TargetLabel>,
    pub(crate) attrs: Option<Vec<FrozenStringValue>>,
    pub(crate) split: bool,
}

#[starlark_value(type = "transition")]
impl<'v> StarlarkValue<'v> for Transition<'v> {
    fn export_as(&self, variable_name: &str, _eval: &mut Evaluator<'v, '_>) {
        let mut id = self.id.borrow_mut();
        // First export wins
        if id.is_none() {
            *id = Some(Arc::new(TransitionId {
                path: self.path.clone(),
                name: variable_name.to_owned(),
            }));
        }
    }

    fn provide(&'v self, demand: &mut Demand<'_, 'v>) {
        demand.provide_value::<&dyn TransitionValue>(self);
    }
}

#[starlark_value(type = "transition")]
impl<'v> StarlarkValue<'v> for FrozenTransition {
    fn provide(&'v self, demand: &mut Demand<'_, 'v>) {
        demand.provide_value::<&dyn TransitionValue>(self);
    }
}

impl<'v> Freeze for Transition<'v> {
    type Frozen = FrozenTransition;

    fn freeze(self, freezer: &Freezer) -> anyhow::Result<FrozenTransition> {
        let implementation = freezer.freeze(self.implementation)?;
        let id = self
            .id
            .into_inner()
            .ok_or(TransitionError::TransitionNotAssigned)?;
        let refs = self
            .refs
            .into_iter()
            .map(|(k, v)| Ok((k.freeze(freezer)?, v.0)))
            .collect::<anyhow::Result<_>>()?;
        let attrs = self
            .attrs
            .map(|a| a.into_try_map(|a| a.freeze(freezer)))
            .transpose()?;
        let split = self.split;
        Ok(FrozenTransition {
            id,
            implementation,
            refs,
            attrs,
            split,
        })
    }
}

starlark_complex_values!(Transition);

impl<'v> TransitionValue for Transition<'v> {
    fn transition_id(&self) -> anyhow::Result<Arc<TransitionId>> {
        self.id
            .borrow()
            .as_ref()
            .map(Dupe::dupe)
            .ok_or_else(|| TransitionError::TransitionNotAssigned.into())
    }
}

impl TransitionValue for FrozenTransition {
    fn transition_id(&self) -> anyhow::Result<Arc<TransitionId>> {
        Ok(self.id.dupe())
    }
}

#[starlark_module]
fn register_transition_function(builder: &mut GlobalsBuilder) {
    fn transition<'v>(
        #[starlark(require = named)] r#impl: Value<'v>,
        #[starlark(require = named)] refs: DictOf<'v, StringValue<'v>, StringValue<'v>>,
        #[starlark(require = named)] attrs: Option<Vec<StringValue<'v>>>,
        #[starlark(require = named, default = false)] split: bool,
        eval: &mut Evaluator<'v, '_>,
    ) -> anyhow::Result<Transition<'v>> {
        let implementation = r#impl;

        let refs = refs
            .collect_entries()
            .into_iter()
            .map(|(n, r)| Ok((n, TargetLabelTrace((COERCE_TARGET_LABEL.get()?)(eval, &r)?))))
            .collect::<anyhow::Result<_>>()?;

        let path: ImportPath = (*(STARLARK_PATH_FROM_BUILD_CONTEXT.get()?)(eval)?
            .unpack_load_file()
            .ok_or(TransitionError::OnlyBzl)?)
        .clone();

        let parameters_spec = match implementation.parameters_spec() {
            Some(parameters_spec) => parameters_spec,
            None => return Err(TransitionError::MustBeDefNotDef.into()),
        };
        let expected_params: &[&str] = if let Some(attrs) = &attrs {
            let attrs_set: HashSet<StringValue> = attrs.iter().copied().collect();
            if attrs_set.len() != attrs.len() {
                return Err(TransitionError::NonUniqueAttrs.into());
            }
            &["platform", "refs", "attrs"]
        } else {
            &["platform", "refs"]
        };
        if !parameters_spec.can_fill_with_args(0, expected_params) {
            return Err(TransitionError::MustBeDefWrongSig(
                parameters_spec.parameters_str(),
                expected_params,
            )
            .into());
        }

        Ok(Transition {
            id: RefCell::new(None),
            path,
            implementation,
            refs,
            attrs,
            split,
        })
    }
}

pub(crate) fn init_register_transition() {
    REGISTER_TRANSITION.init(register_transition_function);
}


/*
 * 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::fmt;

// Returns a function that produces commas every time apart from the first
pub fn commas<W>() -> impl FnMut(&mut W) -> fmt::Result
where
    W: fmt::Write,
{
    let mut with_comma = false;
    move |f: &mut W| -> fmt::Result {
        if with_comma {
            write!(f, ", ")?;
        }
        with_comma = true;
        Ok(())
    }
}


/*
 * 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::HashMap;
use std::collections::HashSet;
use std::collections::VecDeque;
use std::fmt::Debug;
use std::fmt::Display;
use std::future::Future;
use std::hash::Hash;
use std::mem::ManuallyDrop;
use std::time::Duration;

use anyhow::Context;
use futures::lock::Mutex;
use starlark_map::small_set::SmallSet;
use tokio::select;
use tokio::sync::mpsc;
use tokio::sync::oneshot;
use tokio::task::JoinHandle;
use tracing::debug;
use tracing::error;
use tracing::trace;

pub trait CycleDescriptor: Debug + 'static {
    type Key: Debug + Display + Clone + Eq + Hash + Send + Sync;
    type Error: Debug + Display + Clone + Send + Sync;

    fn cycle_error(cycle: Vec<&Self::Key>) -> Self::Error;
}

/// The LazyCycleDetector is used to detect cycles in a parallel graph traversal. In essence, it
/// operates on a sequence of events for nodes that inform it when a node is "started", an edge is
/// discovered and a node is "finished" (which indicates that we won't see any new edges for that
/// node and that the entire graph below that node is guaranteed to have no cycles). After receiving
/// any events, the cycle detector will wait for an idle period (without new events) and then traverse
/// the active graph to check for cycles. Any nodes that are in or depend on a cycle will have their active
/// LazyCycleDetectorGuard notified of the cycle. The guard provides a function `guard_this` that
/// should be used to wrap futures where one "node" is waiting on dependencies, this will then
/// return a future that will exit early with an error if a cycle is detected.
///
/// For example:
///
/// ```ignore
/// struct FibDescriptor;
/// impl CycleDescriptor for FibDescriptor {
///     type Key: u32;
///     type Error: String;
///     fn cycle_error(cycle: Vec<&u32>) -> String {
///         "Detected Cycle".to_owned()
///     }
/// }
///
/// let cycle_detector = CycleDetector::<FibDescriptor>::new();
/// async fn fib(key: u32) -> u32 {
///   let guard = cycle_detector.start(key);
///   if key < 2 {
///     guard.finish();
///     return key;
///   }
///   guard.add_edge(key - 2);
///   guard.add_edge(key - 1);
///   let res = guard.guard_this(async move {
///     fib(key - 2).await + fib(key - 1).await
///   }.boxed()).await.unwrap().unwrap();
///   guard.finish();
///   res
/// }
/// ```
#[derive(Debug)]
pub struct LazyCycleDetector<C: CycleDescriptor> {
    events_sender: mpsc::UnboundedSender<Event<C>>,
    #[allow(unused)] // used in tests
    task: JoinHandle<()>,
}

impl<C: CycleDescriptor> LazyCycleDetector<C> {
    pub fn new() -> Self {
        Self::new_with_delay(Duration::from_millis(1000))
    }

    pub fn new_with_delay(idle_delay: Duration) -> Self {
        let (sender, receiver) = mpsc::unbounded_channel();

        let task = tokio::spawn(async move {
            // We place the receiver into a ManuallyDrop so that if the runtime shuts down (and
            // this future gets dropped), callers won't panic when they send. This allows us to
            // enforce the invariant that senders can expect this task to be alive, unless shutdown
            // is ongoing, in which case the cycle detector simply does not do anything (which is
            // fine since those tasks won't hang anyway: the runtime is shutting down!).
            let mut receiver = ManuallyDrop::new(receiver);

            CycleDetectorState {
                nodes: Vec::new(),
                node_ids: HashMap::new(),
                dirtied_nodes: HashSet::new(),
                idle_delay,
            }
            .run(&mut receiver)
            .await;

            // If `run` is done then let's drop the receiver.
            let _receiver = ManuallyDrop::into_inner(receiver);
        });

        Self {
            events_sender: sender,
            task,
        }
    }

    pub fn start(&self, key: C::Key) -> LazyCycleDetectorGuard<C> {
        let (sender, receiver) = oneshot::channel();
        self.events_sender
            .send(Event::Started(key.clone(), sender))
            .expect("cycle detector events receiver died while cycle detector still alive");

        LazyCycleDetectorGuard {
            key,
            events_sender: self.events_sender.clone(),
            error_receiver: Mutex::new(receiver),
        }
    }

    pub fn finish(&self, key: C::Key) {
        self.events_sender
            .send(Event::Finished(key))
            .expect("cycle detector events receiver died while cycle detector still alive");
    }
}

pub struct LazyCycleDetectorGuard<C: CycleDescriptor> {
    key: C::Key,
    error_receiver: Mutex<oneshot::Receiver<C::Error>>,
    events_sender: mpsc::UnboundedSender<Event<C>>,
}

impl<C: CycleDescriptor> LazyCycleDetectorGuard<C> {
    pub async fn guard_this<R, Fut: Future<Output = R>>(
        &self,
        fut: Fut,
    ) -> anyhow::Result<Result<R, C::Error>> {
        let mut guard = {
            match self.error_receiver.try_lock() {
                Some(v) => v,
                None => self.error_receiver.lock().await,
            }
        };

        select! {
            res = fut => {
                Ok(Ok(res))
            }
            cycle = &mut *guard => {
                match cycle {
                    Ok(e) => Ok(Err(e)),
                    Err(e) => Err(anyhow::anyhow!("error on cycle detector guard receiver: {}", e)).context(format!("for key `{}`", self.key))
                }
            }
        }
    }

    pub fn add_edge(&self, dep: C::Key) {
        self.events_sender
            .send(Event::Edge(self.key.clone(), dep))
            .expect("cycle detector events receiver died while cycle detector still alive");
    }
}

#[derive(Debug)]
enum Event<C: CycleDescriptor> {
    // Guard is dropped for a node.
    Finished(C::Key),
    // A guard is created for a node.
    Started(C::Key, oneshot::Sender<C::Error>),
    // A (new) set of deps for a node is discovered. It must be the case that the node is no longer waiting on any previous set of deps.
    Edge(C::Key, C::Key),
}

// In typical use, this CycleDetectorState will sit with all nodes finished and
// so we want to keep this object small and so states with data are boxed.
enum NodeState<C: CycleDescriptor> {
    // Once a node is finished, we just track that as we no longer need to traverse it to detect cycles (as cycles
    // cause hangs, a node cannot get to the finished state if it has cycles within its deps).
    Finished,
    // A node will be in the "Known" state when we've seen an edge to it, but haven't yet seen its Started event.
    Known,
    // We store that a cycle has been detected so that any new edges pointing to this node can get informed of
    // the cycle.
    CycleDetected(Box<C::Error>),
    Working(Box<(VecDeque<u32>, oneshot::Sender<C::Error>)>),
}

struct CycleDetectorState<C: CycleDescriptor> {
    node_ids: HashMap<C::Key, u32>,
    nodes: Vec<(C::Key, NodeState<C>)>,
    // These are nodes for which we've seen a new out-edge since last we checked for cycles. We will start our next search at these nodes.
    dirtied_nodes: HashSet<u32>,
    idle_delay: Duration,
}

impl<C: CycleDescriptor> CycleDetectorState<C> {
    async fn run(mut self, events_receiver: &mut mpsc::UnboundedReceiver<Event<C>>) {
        'outer: while let Some(ev) = events_receiver.recv().await {
            self.handle_event(ev);
            loop {
                // drain the queue without .awaiting.
                while let Ok(ev) = events_receiver.try_recv() {
                    self.handle_event(ev);
                }
                // Now we'll start the idle delay, if we get any events
                let timer = tokio::time::sleep(self.idle_delay);
                tokio::select! {
                    () = timer => {
                        self.check_for_cycles().await;
                        // break to the outer loop so that we don't start the timer again until we see a new event.
                        break;
                    }
                    // recv() is cancel-safe, see its doc.
                    ev = events_receiver.recv() => {
                        match ev {
                            Some(ev) => self.handle_event(ev),
                            None => {
                                self.check_for_cycles().await;
                                // the events_senders are all gone, break all the way to shutdown.
                                break 'outer;
                            },
                        }
                    }
                }
            }
        }
        debug!("shutting down cycle detector");
    }

    fn node_id(&mut self, k: &C::Key) -> u32 {
        if let Some(v) = self.node_ids.get(k) {
            return *v;
        }
        let id = self.node_ids.len() as u32;
        self.node_ids.insert(k.clone(), id);
        self.nodes.push((k.clone(), NodeState::Known));
        id
    }

    fn node_mut_by_id(&mut self, id: u32) -> &mut NodeState<C> {
        &mut self.nodes.get_mut(id as usize).unwrap().1
    }

    fn node_by_id(&self, id: u32) -> &NodeState<C> {
        &self.nodes.get(id as usize).unwrap().1
    }

    fn key_for_id(&self, id: u32) -> &C::Key {
        &self.nodes.get(id as usize).unwrap().0
    }

    fn node_mut(&mut self, k: &C::Key) -> &mut NodeState<C> {
        let id = self.node_id(k);
        self.node_mut_by_id(id)
    }

    fn handle_event(&mut self, ev: Event<C>) {
        match ev {
            Event::Finished(k) => {
                debug!("finished {}", k);
                *self.node_mut(&k) = NodeState::Finished;
            }
            Event::Started(k, sender) => {
                debug!("started {}", k);
                match self.node_mut(&k) {
                    NodeState::CycleDetected(e) => {
                        let _ignored = sender.send((**e).clone());
                    }
                    v @ NodeState::Finished => {
                        // we cancelled/finished once, but due to eager deps, are restarting the
                        // node, so replace the entire node
                        debug!("replace existing finished {}", k);
                        *v = NodeState::Working(Box::new((VecDeque::new(), sender)))
                    }
                    v @ NodeState::Working(_) => {
                        debug!("replace existing worker sender {}", k);
                        // we cancelled/finished once, but due to eager deps, are restarting the
                        // node, so replace the entire node
                        *v = NodeState::Working(Box::new((VecDeque::new(), sender)))
                    }
                    v @ NodeState::Known => {
                        debug!("known to working state {}", k);
                        *v = NodeState::Working(Box::new((VecDeque::new(), sender)));
                    }
                }
            }
            Event::Edge(k, dep) => {
                let k_id = self.node_id(&k);
                let dep_id = self.node_id(&dep);
                let mut dirtied = false;
                match self.node_mut_by_id(k_id) {
                    NodeState::Finished => {
                        // ignore the event
                    }
                    NodeState::CycleDetected(_) => {
                        // ignored, if a node has a cycle (1) we already sent the cycle error and (2) we don't need to traverse it any more
                    }
                    NodeState::Known => {
                        error!(
                            "cycle detector received ::Edge event for a non-started node {}",
                            k
                        );
                    }
                    NodeState::Working(node) => {
                        dirtied = true;
                        node.0.push_back(dep_id);
                    }
                }

                if dirtied {
                    self.dirtied_nodes.insert(k_id);
                }
            }
        }
    }

    async fn check_for_cycles(&mut self) {
        enum PendingItem<T> {
            Pop,
            Node(T),
        }

        let mut pending = Vec::new();
        for id in std::mem::take(&mut self.dirtied_nodes) {
            // The node may have been dirtied by an event and then we saw
            // a ::Finished event before getting to idle.
            if let NodeState::Working(..) = self.node_by_id(id) {
                pending.push(PendingItem::Node(id));
            }
        }

        let mut visited = HashSet::new();

        let mut stack = SmallSet::new();

        // TODO(cjhopman): We should try to find the smallest cycle.
        // TODO(cjhopman): If we find a cycle, we should consider looking at the non-dirtied nodes
        // as well since they could depend on the cycle we found and so maybe we should be alerting
        // them as well. In practice, we know that any such nodes will be waiting on other such nodes
        // or ones that are dirtied and will get the error and we'd expect user code to then propagate
        // that up such that no nodes are waiting on the cycle. In any case, we still ensure that no
        // nodes are hanging waiting on the cycle.

        // We perform a dfs, maintaining a set of items in the current stack so that we can quickly check
        // for a cycle.
        while let Some(v) = pending.pop() {
            match v {
                PendingItem::Pop => {
                    let v = stack.pop().unwrap();
                    trace!("popping {}", v);
                }
                PendingItem::Node(v) => {
                    trace!("got item {}", v);
                    if !visited.insert(v) {
                        continue;
                    }

                    trace!("visiting {}", v);
                    let mut found_cycle: Option<C::Error> = None;

                    match self.node_by_id(v) {
                        // Above a node is only added to pending if it's in the ::Working state, below the same. It can only
                        // be changed to ::CycleDetected while its on the stack (and so is already visited and would've already
                        // passed this check).
                        NodeState::Finished | NodeState::Known | NodeState::CycleDetected(_) => {
                            unreachable!()
                        }
                        NodeState::Working(node) => {
                            if !stack.insert(v) {
                                unreachable!("v: {}, stack: {:?}", v, stack);
                            }
                            // Pop this item off the stack (children get pushed after this).
                            pending.push(PendingItem::Pop);
                            for child in node.0.iter() {
                                match self.node_by_id(*child) {
                                    NodeState::Finished | NodeState::Known => {
                                        // nothing to do
                                    }
                                    NodeState::CycleDetected(v) => {
                                        found_cycle = Some((**v).clone());
                                    }
                                    _ => {
                                        // It's important that this cycle detection happens after the check for whether child is already a Node::CycleDetected to
                                        // ensure that each node only ever gets added to a cycle_items vec once. Without that property, we could get N^2 runtime as
                                        // we could potentially compute the cycle_items list repeatedly for every node.
                                        if stack.contains(child) {
                                            // detected a cycle, now just collect the items in the cycle
                                            let mut cycle_items = Vec::new();
                                            let mut in_cycle = false;
                                            for item in &stack {
                                                if item == child {
                                                    in_cycle = true;
                                                }

                                                if in_cycle {
                                                    cycle_items.push(self.key_for_id(*item));
                                                }
                                            }
                                            found_cycle = Some(C::cycle_error(cycle_items));
                                        } else if !visited.contains(child) {
                                            trace!("pushing {}", child);
                                            pending.push(PendingItem::Node(*child));
                                        }
                                    }
                                }
                            }
                        }
                    }

                    if let Some(cycle) = found_cycle {
                        debug!("found cycle {}", cycle);
                        // We want to make sure that we don't iterate over the whole stack for every node. To ensure that, we iterate from the bottom and
                        // stop at the first that already has a detected cycle. This works fine because this loop is the only place that we mark CycleDetected
                        // and so we know if one item in the stack has it, everything before does as well.
                        for key in stack.iter().rev() {
                            let node = self.node_mut_by_id(*key);
                            // node should never be a ::Finished or ::Started because they never end up in the stack. If it's CycleDetected, we
                            // don't need to inform it about this cycle (which may or may not be the same).

                            if matches!(node, NodeState::CycleDetected(..)) {
                                break;
                            }

                            if matches!(node, NodeState::Working(..)) {
                                debug!("sending cycle error for {}", key);
                                let mut working = NodeState::CycleDetected(Box::new(cycle.clone()));
                                std::mem::swap(&mut *node, &mut working);
                                match working {
                                    NodeState::Working(v) => {
                                        let _ignored = v.1.send(cycle.clone());
                                    }
                                    _ => {
                                        unreachable!()
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

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

    use futures::FutureExt;

    use super::*;

    #[derive(Debug)]
    struct SimpleCycleDescriptor;

    #[derive(Clone, Debug)]
    struct Error(Arc<Vec<u64>>);

    impl std::fmt::Display for Error {
        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
            write!(f, "CycleError")
        }
    }

    impl CycleDescriptor for SimpleCycleDescriptor {
        type Key = u64;

        type Error = Error;

        fn cycle_error(cycle: Vec<&Self::Key>) -> Self::Error {
            Error(Arc::new(cycle.into_iter().copied().collect()))
        }
    }

    #[tokio::test]
    async fn should_detect_simple_cycle() {
        let task = {
            let detector = LazyCycleDetector::<SimpleCycleDescriptor>::new_with_delay(
                Duration::from_millis(1),
            );
            let g0 = detector.start(0);
            let g1 = detector.start(1);
            let g2 = detector.start(2);

            g0.add_edge(1);
            g1.add_edge(2);
            g2.add_edge(0);

            let res = g0
                .guard_this(tokio::time::sleep(Duration::from_secs(120)).boxed())
                .await;
            match res {
                Ok(Err(..)) => {
                    // good
                }
                Ok(Ok(..)) => {
                    panic!("should've detected a cycle")
                }
                Err(..) => {
                    panic!("should not have gotten a cycle detector error")
                }
            }

            detector.task
        };

        task.await.unwrap();
    }
}


/*
 * 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::fmt::Display;

/// Indent a text block: split text into lines, prepend each line with `indent`,
/// and join the lines back together by appending a newline to each line.
pub fn indent<'a>(indent: &'a str, text: &'a str) -> impl Display + 'a {
    struct Indent<'a> {
        indent: &'a str,
        text: &'a str,
    }

    impl<'a> Display for Indent<'a> {
        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
            for line in self.text.lines() {
                writeln!(f, "{}{}", self.indent, line)?;
            }
            Ok(())
        }
    }

    Indent { indent, text }
}

#[cfg(test)]
mod tests {
    use crate::indent::indent;

    #[test]
    fn test_indent() {
        assert_eq!("", indent("  ", "").to_string());
        assert_eq!("  foo\n", indent("  ", "foo").to_string());
        assert_eq!("  foo\n", indent("  ", "foo\n").to_string());
        assert_eq!("  foo\n  bar\n", indent("  ", "foo\nbar").to_string());
        assert_eq!("  foo\n  bar\n", indent("  ", "foo\nbar\n").to_string());
    }
}


/*
 * 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 anyhow::Context;
use once_cell::sync::OnceCell;

/// Value (typically a function pointer) that is initialized at program start.
pub struct LateBinding<T> {
    /// Name for diagnostic.
    name: &'static str,
    symbol: OnceCell<T>,
}

impl<T> LateBinding<T> {
    pub const fn new(name: &'static str) -> LateBinding<T> {
        LateBinding {
            name,
            symbol: OnceCell::new(),
        }
    }

    pub fn init(&self, symbol: T) {
        if self.symbol.set(symbol).is_err() {
            panic!("{} already set", self.name);
        }
    }

    #[inline]
    pub fn get(&self) -> anyhow::Result<&T> {
        self.symbol
            .get()
            .with_context(|| format!("{} not set (internal error)", self.name))
    }
}


/*
 * 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.
 */

#![feature(round_char_boundary)]

pub mod arc_str;
pub mod collections;
pub mod commas;
pub mod cycle_detector;
pub mod indent;
pub mod late_binding;
pub mod process;
pub mod process_stats;
pub mod rtabort;
pub mod system_stats;
pub mod thin_box;
pub mod truncate;


/*
 * 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::ffi::OsStr;

/// Creates `std::process::Command` which doesn't show any windows on Windows.
pub fn background_command<S: AsRef<OsStr>>(program: S) -> std::process::Command {
    #[allow(unused_mut)]
    let mut cmd = std::process::Command::new(program);
    #[cfg(windows)]
    {
        use std::os::windows::process::CommandExt;
        cmd.creation_flags(winapi::um::winbase::CREATE_NO_WINDOW);
    }
    cmd
}

/// Creates `tokio::process::Command` which doesn't show any windows on Windows.
pub fn async_background_command<S: AsRef<OsStr>>(program: S) -> tokio::process::Command {
    background_command(program).into()
}


/*
 * 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::default::Default;

#[derive(Default)]
pub struct ProcessStats {
    pub rss_bytes: Option<u64>,
    pub max_rss_bytes: Option<u64>,
    pub user_cpu_us: Option<u64>,
    pub system_cpu_us: Option<u64>,
}

#[cfg(unix)]
pub fn process_stats() -> ProcessStats {
    use crate::process_stats::proc_self_stat::ProcSelfStat;

    let usage = unsafe {
        let mut usage: libc::rusage = std::mem::zeroed();
        match libc::getrusage(libc::RUSAGE_SELF, &mut usage as *mut _) {
            0 => usage,
            _ => return ProcessStats::default(),
        }
    };
    // POSIX didn't specify unit of ru_maxrss. Linux uses KB while BSD and
    // OSX use bytes (despite their manpages might say differently).
    let rss_scale = if cfg!(target_os = "linux") {
        1024
    } else {
        // Assume BSD-ish
        1
    };

    fn tv_to_micros(tv: &libc::timeval) -> u64 {
        (1_000_000 * tv.tv_sec as u64) + (tv.tv_usec as u64)
    }

    let rss_bytes = if cfg!(target_os = "linux") {
        // Buck2 snapshot is made once per second, so this shouldn't be too expensive.
        ProcSelfStat::read().map(|stat| {
            // `getconf PAGESIZE`, but practically it's always 4096.
            stat.rss * 4096
        })
    } else {
        None
    };

    ProcessStats {
        rss_bytes,
        max_rss_bytes: Some((usage.ru_maxrss as u64) * rss_scale),
        user_cpu_us: Some(tv_to_micros(&usage.ru_utime)),
        system_cpu_us: Some(tv_to_micros(&usage.ru_stime)),
    }
}

#[cfg(windows)]
pub fn process_stats() -> ProcessStats {
    use winapi::shared::minwindef::DWORD;
    use winapi::um::processthreadsapi::GetCurrentProcess;
    use winapi::um::psapi::K32GetProcessMemoryInfo;
    use winapi::um::psapi::PROCESS_MEMORY_COUNTERS;

    let mut pmc: PROCESS_MEMORY_COUNTERS = unsafe { std::mem::zeroed() };
    pmc.cb = std::mem::size_of_val(&pmc) as DWORD;
    // Code is referenced from eden/scm/lib/procinfo/src/lib.rs
    // API reference: https://learn.microsoft.com/en-us/windows/win32/api/psapi/ns-psapi-process_memory_counters
    let (wss_bytes, max_wss_bytes) =
        match unsafe { K32GetProcessMemoryInfo(GetCurrentProcess(), &mut pmc, pmc.cb) } {
            0 => (None, None),
            _ => (
                Some(pmc.WorkingSetSize as u64),
                Some(pmc.PeakWorkingSetSize as u64),
            ),
        };

    // Technically, `GetProcessMemoryInfo` returns working set size, not resident set size. However,
    // we log it as rss for two reasons:
    // (1) Reading https://learn.microsoft.com/en-us/windows/win32/memory/working-set, the definition of
    // working set size is "set of pages in the virtual address space of the process that are currently resident
    // in physical memory", which sound just like RSS in unix.
    // (2) We can reuse our existing logging for rss without having duplicate column and logic for wss.
    ProcessStats {
        rss_bytes: wss_bytes,
        max_rss_bytes: max_wss_bytes,
        user_cpu_us: None,
        system_cpu_us: None,
    }
}

#[cfg(not(any(unix, windows)))]
pub fn process_stats() -> ProcessStats {
    ProcessStats::default()
}

pub fn process_cpu_time_us() -> Option<u64> {
    let stats = process_stats();
    if let (Some(user_cpu_us), Some(system_cpu_us)) = (stats.user_cpu_us, stats.system_cpu_us) {
        Some(user_cpu_us + system_cpu_us)
    } else {
        None
    }
}

#[cfg_attr(not(unix), allow(dead_code))]
mod proc_self_stat {
    use std::fs;

    /// Parsed `/proc/self/stat` file.
    pub struct ProcSelfStat {
        /// Resident Set Size: number of pages the process has in real memory.
        /// Raw value.
        pub rss: u64,
    }

    impl ProcSelfStat {
        pub fn parse(stat: &str) -> Option<ProcSelfStat> {
            let rss = stat.split(' ').nth(23)?.parse().ok()?;
            Some(ProcSelfStat { rss })
        }

        pub fn read() -> Option<ProcSelfStat> {
            fs::read_to_string("/proc/self/stat")
                .ok()
                .and_then(|s| ProcSelfStat::parse(&s))
        }
    }
}

#[cfg(test)]
mod tests {
    use crate::process_stats::proc_self_stat::ProcSelfStat;
    use crate::process_stats::process_stats;

    #[test]
    fn test_process_stats() {
        let process_stats = process_stats();
        if cfg!(unix) {
            // Sometimes tests start too quickly and CPU counters are zero.
            if false {
                assert!(process_stats.user_cpu_us.unwrap() > 0);
                assert!(process_stats.system_cpu_us.unwrap() > 0);
            }
        }
        assert!(process_stats.max_rss_bytes.unwrap() > 0);
        if cfg!(target_os = "linux") || cfg!(target_os = "windows") {
            let rss_bytes = process_stats.rss_bytes.unwrap();
            assert!(rss_bytes > 0);
        }
    }

    #[test]
    fn test_proc_self_stat_parse() {
        let stat = "1736324 (cat) R 53088 1736324 53088 34816 1736324 4194304 113 \
            0 0 0 0 0 0 0 20 0 1 0 \
            2018135 222441472 215 \
            18446744073709551615 94565082071040 94565082102344 140727456826704 \
            0 0 0 0 0 0 0 0 0 17 11 0 0 0 0 0 \
            94565084199504 94565084201152 94565084205056 140727456831309 \
            140727456831329 140727456831329 140727456833519 0";
        assert_eq!(215, ProcSelfStat::parse(stat).unwrap().rss);
    }

    #[test]
    fn test_proc_self_stat_read() {
        if cfg!(target_os = "linux") {
            let stat = ProcSelfStat::read().unwrap();
            assert!(stat.rss > 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 std::fmt::Arguments;
use std::io;
use std::process;

/// Like `panic!`, but aborts the process instead of unwinding.
///
/// Although we compile buck2 with `panic=abort`, this is safer because
/// others may copy-paste code.
#[macro_export]
macro_rules! rtabort {
    ($m:literal) => {
        $crate::rtabort::rtabort_impl_fixed_string(
            file!(),
            line!(),
            $m,
        )
    };
    ($($t:tt)*) => {
        $crate::rtabort::rtabort_impl(
            file!(),
            line!(),
            format_args!($($t)*),
        )
    };
}

#[cold]
pub fn rtabort_impl_fixed_string(file: &str, line: u32, message: &str) -> ! {
    rtabort_impl(file, line, format_args!("{}", message));
}

#[cold]
pub fn rtabort_impl(file: &str, line: u32, msg: Arguments) -> ! {
    // Make sure we abort even if formatting panics.
    let _abort = AbortOnDrop;

    // `eprintln!` followed by `abort` does not print anything in tests.
    io::Write::write_fmt(
        &mut io::stderr(),
        format_args!("{}:{}: abort: {}\n", file, line, msg),
    )
    .ok();

    // Tell the compiler that we never return.
    process::abort();
}

pub struct AbortOnDrop;

impl Drop for AbortOnDrop {
    fn drop(&mut self) {
        process::abort();
    }
}

#[cfg(test)]
mod tests {
    fn _test_compiles_fixed_string() {
        rtabort!("test");
    }

    fn _test_compiles_with_format_args() {
        rtabort!("test {}", 17);
    }

    #[test]
    fn test_rtabort() {
        // Uncomment to test.
        // rtabort!("test {}", 17);
        // rtabort!("test {}", { panic!(); 17 });
    }
}


/*
 * 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.
 */

pub struct UnixSystemStats {
    pub load1: f64,
    pub load5: f64,
    pub load15: f64,
}

impl UnixSystemStats {
    #[cfg(unix)]
    pub fn get() -> Option<Self> {
        let mut loadavg: [f64; 3] = [0.0, 0.0, 0.0];
        if unsafe { libc::getloadavg(&mut loadavg[0], 3) } != 3 {
            // This doesn't seem to set errno (or at least it's not documented to do so).
            return None;
        }
        Some(Self {
            load1: loadavg[0],
            load5: loadavg[1],
            load15: loadavg[2],
        })
    }

    #[cfg(not(unix))]
    pub fn get() -> Option<Self> {
        None
    }
}


/*
 * 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.
 */

#![allow(clippy::from_iter_instead_of_collect)]

use std::alloc;
use std::alloc::Layout;
use std::fmt::Debug;
use std::hash::Hash;
use std::hash::Hasher;
use std::mem;
use std::mem::MaybeUninit;
use std::ops::Deref;
use std::ops::DerefMut;
use std::ptr;
use std::ptr::NonNull;
use std::slice;

use allocative::Allocative;

/// `Box<[T]>` but thin pointer.
///
/// Statically allocated for empty slice.
pub struct ThinBoxSlice<T> {
    /// Pointer to the first element.
    ///
    /// Memory layout:
    /// ```ignore
    /// [len, T, T, ...]
    ///       ^ points here
    /// ```
    ptr: NonNull<T>,
}

unsafe impl<T: Sync> Sync for ThinBoxSlice<T> {}
unsafe impl<T: Send> Send for ThinBoxSlice<T> {}

impl<T> ThinBoxSlice<T> {
    const _ASSERTS: () = {
        // Otherwise empty slice is not aligned properly.
        assert!(mem::align_of::<T>() <= mem::align_of::<usize>());
    };

    #[inline]
    pub const fn empty() -> ThinBoxSlice<T> {
        const LEN_ZERO: usize = 0;
        unsafe {
            let ptr = (&LEN_ZERO as *const usize).add(1) as *mut T;
            let ptr = NonNull::new_unchecked(ptr);
            ThinBoxSlice { ptr }
        }
    }

    /// Allocation layout for a slice of length `len`.
    #[inline]
    fn layout_for_len(len: usize) -> Layout {
        let (layout, offset_of_data) = Layout::new::<usize>()
            .extend(Layout::array::<T>(len).unwrap())
            .unwrap();
        assert_eq!(mem::size_of::<usize>(), offset_of_data);
        layout
    }

    /// Length of the slice.
    // Not called `len` to avoid overload with `Deref::len`.
    #[inline]
    fn read_len(&self) -> usize {
        unsafe { *self.ptr.cast::<usize>().as_ptr().sub(1) }
    }

    /// Allocate uninitialized memory for a slice of length `len`.
    #[inline]
    pub fn new_uninit(len: usize) -> ThinBoxSlice<MaybeUninit<T>> {
        if len == 0 {
            ThinBoxSlice::empty()
        } else {
            let layout = Self::layout_for_len(len);
            unsafe {
                let alloc = alloc::alloc(layout);
                if alloc.is_null() {
                    alloc::handle_alloc_error(layout);
                }
                ptr::write(alloc as *mut usize, len);
                let ptr = alloc.add(mem::size_of::<usize>()) as *mut MaybeUninit<T>;
                let ptr = NonNull::new_unchecked(ptr);
                ThinBoxSlice { ptr }
            }
        }
    }

    #[inline]
    pub fn new<const N: usize>(data: [T; N]) -> ThinBoxSlice<T> {
        ThinBoxSlice::from_iter(data)
    }
}

impl<T> Deref for ThinBoxSlice<T> {
    type Target = [T];

    #[inline]
    fn deref(&self) -> &Self::Target {
        unsafe { slice::from_raw_parts(self.ptr.as_ptr(), self.read_len()) }
    }
}

impl<T> DerefMut for ThinBoxSlice<T> {
    #[inline]
    fn deref_mut(&mut self) -> &mut Self::Target {
        unsafe { slice::from_raw_parts_mut(self.ptr.as_ptr(), self.read_len()) }
    }
}

impl<T> ThinBoxSlice<MaybeUninit<T>> {
    #[inline]
    pub unsafe fn assume_init(self) -> ThinBoxSlice<T> {
        let result = ThinBoxSlice {
            ptr: self.ptr.cast::<T>(),
        };
        mem::forget(self);
        result
    }
}

impl<T> Drop for ThinBoxSlice<T> {
    #[inline]
    fn drop(&mut self) {
        unsafe {
            let len = self.read_len();
            if len != 0 {
                let slice = ptr::slice_from_raw_parts_mut(self.ptr.as_ptr(), len);
                ptr::drop_in_place(slice);
                let alloc = self.ptr.cast::<usize>().as_ptr().sub(1);
                alloc::dealloc(alloc as *mut u8, Self::layout_for_len(len));
            }
        }
    }
}

impl<T> Default for ThinBoxSlice<T> {
    #[inline]
    fn default() -> Self {
        ThinBoxSlice::empty()
    }
}

impl<T: Debug> Debug for ThinBoxSlice<T> {
    #[inline]
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        <[T] as Debug>::fmt(&**self, f)
    }
}

impl<T: PartialEq> PartialEq for ThinBoxSlice<T> {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        <[T] as PartialEq>::eq(&**self, &**other)
    }
}

impl<T: Eq> Eq for ThinBoxSlice<T> {}

impl<T: PartialOrd> PartialOrd for ThinBoxSlice<T> {
    #[inline]
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        <[T] as PartialOrd>::partial_cmp(&**self, &**other)
    }
}

impl<T: Ord> Ord for ThinBoxSlice<T> {
    #[inline]
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        <[T] as Ord>::cmp(&**self, &**other)
    }
}

impl<T: Hash> Hash for ThinBoxSlice<T> {
    #[inline]
    fn hash<H: Hasher>(&self, state: &mut H) {
        <[T] as Hash>::hash(&**self, state)
    }
}

impl<T> FromIterator<T> for ThinBoxSlice<T> {
    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
        let iter = iter.into_iter();
        let (lower, upper) = iter.size_hint();
        if Some(lower) == upper {
            let mut thin = ThinBoxSlice::<T>::new_uninit(lower);
            let mut i = 0;
            for item in iter {
                assert!(i < lower, "iterator produced more than promised");
                MaybeUninit::write(&mut thin[i], item);
                i += 1;
            }
            assert_eq!(i, lower, "iterator produced less than promised");
            unsafe { thin.assume_init() }
        } else {
            // TODO(nga): we can collect into partially initialized `ThinBoxSlice`
            //   to get a chance of avoiding last reallocation.
            let vec = Vec::from_iter(iter);
            Self::from_iter(vec)
        }
    }
}

impl<T: Allocative> Allocative for ThinBoxSlice<T> {
    fn visit<'a, 'b: 'a>(&self, visitor: &'a mut allocative::Visitor<'b>) {
        let mut visitor = visitor.enter_self_sized::<Self>();
        {
            let mut visitor =
                visitor.enter_unique(allocative::Key::new("ptr"), mem::size_of_val(&self.ptr));
            {
                let mut visitor = visitor.enter(
                    allocative::Key::new("alloc"),
                    Self::layout_for_len(self.len()).size(),
                );
                visitor.visit_simple(allocative::Key::new("len"), mem::size_of::<usize>());
                {
                    let mut visitor = visitor.enter(allocative::Key::new("data"), self.len());
                    visitor.visit_slice::<T>(self);
                    visitor.exit();
                }
                visitor.exit();
            }
            visitor.exit();
        }
        visitor.exit();
    }
}

#[cfg(test)]
mod tests {
    use crate::thin_box::ThinBoxSlice;

    #[test]
    fn test_empty() {
        assert_eq!(0, ThinBoxSlice::<String>::empty().len());
    }

    #[test]
    fn test_from_iter_sized() {
        let thin = ThinBoxSlice::from_iter(["a".to_owned(), "b".to_owned(), "c".to_owned()]);
        assert_eq!(["a".to_owned(), "b".to_owned(), "c".to_owned()], *thin);
    }

    #[test]
    fn test_from_iter_unknown_size() {
        let thin = ThinBoxSlice::from_iter(
            ["a".to_owned(), "b".to_owned(), "c".to_owned()]
                .into_iter()
                .filter(|_| true),
        );
        assert_eq!(["a".to_owned(), "b".to_owned(), "c".to_owned()], *thin);
    }

    /// If there are obvious memory violations, this test will catch them.
    #[test]
    fn test_stress() {
        for i in 0..1000 {
            let slice = ThinBoxSlice::from_iter((0..i).map(|j| j.to_string()));
            assert_eq!(i, slice.len());
            assert_eq!((0..i).map(|j| j.to_string()).collect::<Vec<_>>(), *slice);
        }
    }
}


/*
 * 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::alloc;
use std::alloc::Layout;
use std::cmp::Ordering;
use std::hash::Hash;
use std::hash::Hasher;
use std::marker::PhantomData;
use std::mem;
use std::ptr;
use std::ptr::NonNull;
use std::slice;
use std::str;
use std::sync::atomic;
use std::sync::atomic::AtomicU32;

use allocative::Allocative;
use dupe::Dupe;

use crate::rtabort;

/// Describes the strategy that an `ArcStr`-like implementation uses to allocate its length.
///
/// # Safety
///
/// - The `pack` and `unpack` implementations must roundtrip,
///   i.e. `unpack_len(pack_len(x))` must equal `x`.
/// - `unpack_len(EMPTY)` must return zero.
/// - `inner_empty()` must return `&ArcStrBaseInner::EMPTY`.
pub(crate) unsafe trait ArcStrLenStrategy: Sized + 'static {
    /// Payload allocated with the string data.
    type AllocatedPayload: Copy + Allocative;
    /// Payload stored in the string value.
    type ValuePayload: Copy;
    /// Payload for an empty string.
    const EMPTY: (Self::AllocatedPayload, Self::ValuePayload);

    /// Get the length of the string.
    fn unpack_len(d: (Self::AllocatedPayload, Self::ValuePayload)) -> u32;
    /// Pack the length of the string into either of payloads.
    fn pack_len(len: u32) -> (Self::AllocatedPayload, Self::ValuePayload);

    /// Empty string data. Must be statically allocated,
    /// and Rust does not allow static allocation in generic code.
    fn inner_empty() -> &'static ArcStrBaseInnerConst<Self>;
}

#[repr(C)]
#[derive(Allocative)]
#[allocative(bound = "")]
pub(crate) struct ArcStrBaseInner<P: ArcStrLenStrategy> {
    pub(crate) refcount: AtomicU32,
    pub(crate) allocated_payload: P::AllocatedPayload,
    // data: [u8],
}

/// Exactly the same layout as `ArcStrBaseInner`, but refcount is immutable,
/// so this struct is statically allocated in immutable memory,
/// and attempt to modify the counter will result in a segfault (which is good).
#[repr(C)]
pub(crate) struct ArcStrBaseInnerConst<P: ArcStrLenStrategy> {
    pub(crate) refcount: u32,
    pub(crate) allocated_payload: P::AllocatedPayload,
    // data: [u8; 0],
}

impl<P: ArcStrLenStrategy> ArcStrBaseInner<P> {
    const OFFSET_OF_DATA: usize = mem::size_of::<Self>();
    const ASSERT: () = {
        assert!(Layout::new::<Self>().size() == Layout::new::<ArcStrBaseInnerConst<P>>().size());
        assert!(Layout::new::<Self>().align() == Layout::new::<ArcStrBaseInnerConst<P>>().align());
    };

    pub(crate) const EMPTY: ArcStrBaseInnerConst<P> = ArcStrBaseInnerConst {
        refcount: 0,
        allocated_payload: P::EMPTY.0,
    };

    fn layout_for_len(len: usize) -> Layout {
        #[allow(clippy::let_unit_value)]
        let () = Self::ASSERT;

        let size = Self::OFFSET_OF_DATA.checked_add(len).unwrap();
        let align = mem::align_of::<Self>();
        Layout::from_size_align(size, align).unwrap()
    }

    unsafe fn layout_for_len_unchecked(len: usize) -> Layout {
        let size = Self::OFFSET_OF_DATA + len;
        let align = mem::align_of::<Self>();
        unsafe { Layout::from_size_align_unchecked(size, align) }
    }
}

pub(crate) struct ArcStrBase<P: ArcStrLenStrategy> {
    /// Points to the end of `ArcStrBaseInner` struct.
    /// When string is empty, `ArcStrBaseInner` is statically allocated.
    data: NonNull<u8>,
    pub(crate) value_payload: P::ValuePayload,
}

unsafe impl<P: ArcStrLenStrategy> Send for ArcStrBase<P> {}
unsafe impl<P: ArcStrLenStrategy> Sync for ArcStrBase<P> {}

impl<P: ArcStrLenStrategy> ArcStrBase<P> {
    pub(crate) fn inner(&self) -> &ArcStrBaseInner<P> {
        unsafe {
            let ptr = self.data.as_ptr().sub(ArcStrBaseInner::<P>::OFFSET_OF_DATA);
            &*(ptr as *const ArcStrBaseInner<P>)
        }
    }

    #[inline]
    fn len(&self) -> usize {
        P::unpack_len((self.inner().allocated_payload, self.value_payload)) as usize
    }

    #[inline]
    fn is_empty(&self) -> bool {
        self.len() == 0
    }

    #[inline]
    pub(crate) fn as_str(&self) -> &str {
        unsafe {
            let bytes = slice::from_raw_parts(self.data.as_ptr(), self.len());
            str::from_utf8_unchecked(bytes)
        }
    }
}

impl<P: ArcStrLenStrategy> PartialEq for ArcStrBase<P> {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        ptr::eq(self.data.as_ptr(), other.data.as_ptr()) || self.as_str() == other.as_str()
    }
}

impl<P: ArcStrLenStrategy> Eq for ArcStrBase<P> {}

impl<P: ArcStrLenStrategy> Hash for ArcStrBase<P> {
    #[inline]
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.as_str().hash(state)
    }
}

impl<P: ArcStrLenStrategy> PartialOrd for ArcStrBase<P> {
    #[inline]
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        self.as_str().partial_cmp(other.as_str())
    }
}

impl<P: ArcStrLenStrategy> Ord for ArcStrBase<P> {
    #[inline]
    fn cmp(&self, other: &Self) -> Ordering {
        self.as_str().cmp(other.as_str())
    }
}

impl<P: ArcStrLenStrategy> Default for ArcStrBase<P> {
    #[inline]
    fn default() -> ArcStrBase<P> {
        unsafe {
            ArcStrBase {
                data: NonNull::new_unchecked(
                    (P::inner_empty() as *const ArcStrBaseInnerConst<P>).add(1) as *mut u8,
                ),
                value_payload: P::EMPTY.1,
            }
        }
    }
}

impl<P: ArcStrLenStrategy> Clone for ArcStrBase<P> {
    #[inline]
    fn clone(&self) -> ArcStrBase<P> {
        if !self.is_empty() {
            // This is what `Arc::clone` does.
            let old_count = self
                .inner()
                .refcount
                .fetch_add(1, atomic::Ordering::Relaxed);
            const MAX_REFCOUNT: u32 = i32::MAX as u32;
            if old_count > MAX_REFCOUNT {
                rtabort!("refcount overflow");
            }
        }
        ArcStrBase {
            data: self.data,
            value_payload: self.value_payload,
        }
    }
}

impl<P: ArcStrLenStrategy> Dupe for ArcStrBase<P> {}

impl<P: ArcStrLenStrategy> Drop for ArcStrBase<P> {
    #[inline]
    fn drop(&mut self) {
        let len = self.len();
        if len != 0 {
            unsafe {
                let arc_str_inner = self
                    .data
                    .as_ptr()
                    .sub(ArcStrBaseInner::<P>::OFFSET_OF_DATA)
                    .cast::<ArcStrBaseInner<P>>();
                // Again, copy-paste from `Arc::drop`.
                if (*arc_str_inner)
                    .refcount
                    .fetch_sub(1, atomic::Ordering::Release)
                    != 1
                {
                    return;
                }
                atomic::fence(atomic::Ordering::Acquire);
                let layout = ArcStrBaseInner::<P>::layout_for_len_unchecked(len);
                alloc::dealloc(arc_str_inner.cast::<u8>(), layout);
            }
        }
    }
}

impl<'a, P: ArcStrLenStrategy> From<&'a str> for ArcStrBase<P> {
    fn from(s: &'a str) -> ArcStrBase<P> {
        if s.is_empty() {
            ArcStrBase::default()
        } else {
            let layout = ArcStrBaseInner::<P>::layout_for_len(s.len());
            let len: u32 = s.len().try_into().unwrap();
            let (allocated_payload, value_payload) = P::pack_len(len);
            unsafe {
                let alloc = alloc::alloc(layout);
                ptr::write(
                    alloc.cast::<ArcStrBaseInner<P>>(),
                    ArcStrBaseInner {
                        refcount: AtomicU32::new(1),
                        allocated_payload,
                    },
                );
                let data = alloc.add(ArcStrBaseInner::<P>::OFFSET_OF_DATA);
                ptr::copy_nonoverlapping(s.as_ptr(), data, s.len());
                ArcStrBase {
                    data: NonNull::new(data).unwrap(),
                    value_payload,
                }
            }
        }
    }
}

impl<P: ArcStrLenStrategy> Allocative for ArcStrBase<P> {
    fn visit<'a, 'b: 'a>(&self, visitor: &'a mut allocative::Visitor<'b>) {
        let mut visitor = visitor.enter_self_sized::<Self>();
        if !self.is_empty() {
            if let Some(mut visitor) = visitor.enter_shared(
                allocative::Key::new("data"),
                mem::size_of::<*const u8>(),
                self.data.as_ptr() as *const (),
            ) {
                {
                    struct ArcStrInner<P>(PhantomData<P>);

                    let mut visitor = visitor.enter(
                        allocative::Key::for_type_name::<ArcStrInner<P>>(),
                        ArcStrBaseInner::<P>::layout_for_len(self.len()).size(),
                    );
                    visitor.visit_field(allocative::Key::new("header"), self.inner());
                    visitor.visit_field(allocative::Key::new("str"), self.as_str());
                    visitor.exit();
                }
                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.
 */

use std::borrow::Borrow;
use std::fmt::Debug;
use std::fmt::Display;
use std::hash::Hash;
use std::ops::Deref;
use std::str;
use std::sync::Arc;

use allocative::Allocative;
use dupe::Dupe;
use serde::Serialize;
use static_assertions::assert_eq_size;

use crate::arc_str::base::ArcStrBase;
use crate::arc_str::base::ArcStrBaseInner;
use crate::arc_str::base::ArcStrBaseInnerConst;
use crate::arc_str::base::ArcStrLenStrategy;

struct ArcStrProperties;

unsafe impl ArcStrLenStrategy for ArcStrProperties {
    type AllocatedPayload = ();
    type ValuePayload = u32;
    const EMPTY: (Self::AllocatedPayload, Self::ValuePayload) = ((), 0);

    #[inline]
    fn unpack_len(s: (Self::AllocatedPayload, Self::ValuePayload)) -> u32 {
        s.1
    }

    #[inline]
    fn inner_empty() -> &'static ArcStrBaseInnerConst<Self> {
        &INNER_EMPTY
    }

    #[inline]
    fn pack_len(len: u32) -> (Self::AllocatedPayload, Self::ValuePayload) {
        ((), len)
    }
}

/// Wrapper for `Arc<str>`.
#[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Allocative, Clone, Dupe, Default)]
pub struct ArcStr {
    base: ArcStrBase<ArcStrProperties>,
}

assert_eq_size!(ArcStr, Arc<str>);
assert_eq_size!(Option<ArcStr>, Arc<str>);

static INNER_EMPTY: ArcStrBaseInnerConst<ArcStrProperties> = ArcStrBaseInner::EMPTY;

impl ArcStr {
    #[inline]
    pub fn as_str(&self) -> &str {
        self.base.as_str()
    }
}

impl Deref for ArcStr {
    type Target = str;

    #[inline]
    fn deref(&self) -> &str {
        self.as_str()
    }
}

impl AsRef<str> for ArcStr {
    #[inline]
    fn as_ref(&self) -> &str {
        self.as_str()
    }
}

impl Borrow<str> for ArcStr {
    #[inline]
    fn borrow(&self) -> &str {
        self.as_str()
    }
}

impl Display for ArcStr {
    #[inline]
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        Display::fmt(self.as_str(), f)
    }
}

impl Debug for ArcStr {
    #[inline]
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        Debug::fmt(self.as_str(), f)
    }
}

impl<'a> From<&'a str> for ArcStr {
    #[inline]
    fn from(s: &'a str) -> ArcStr {
        ArcStr {
            base: ArcStrBase::from(s),
        }
    }
}

impl From<String> for ArcStr {
    #[inline]
    fn from(s: String) -> ArcStr {
        ArcStr::from(s.as_str())
    }
}

impl Serialize for ArcStr {
    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        self.as_str().serialize(serializer)
    }
}

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

    use dupe::Dupe;

    use crate::arc_str::ArcStr;

    #[test]
    fn test_serialize() {
        assert_eq!(
            "\"hello\"",
            serde_json::to_string(&ArcStr::from("hello")).unwrap()
        );
        assert_eq!("\"\"", serde_json::to_string(&ArcStr::from("")).unwrap());
    }

    #[test]
    fn test_hash() {
        fn hash<H: Hash + ?Sized>(h: &H) -> u64 {
            let mut hasher = DefaultHasher::new();
            h.hash(&mut hasher);
            hasher.finish()
        }

        assert_eq!(hash(""), hash(&ArcStr::from("")));
        assert_eq!(hash("hello"), hash(&ArcStr::from("hello")));
    }

    #[test]
    fn test_clone_drop_ref_count() {
        fn ref_count(s: &ArcStr) -> u32 {
            assert!(!s.is_empty());
            s.base.inner().refcount.load(atomic::Ordering::Relaxed)
        }

        let s = ArcStr::from("hello");
        assert_eq!(1, ref_count(&s));
        let s2 = s.dupe();
        assert_eq!(2, ref_count(&s));
        drop(s);
        assert_eq!(1, ref_count(&s2));
    }

    #[test]
    fn test_little_stress_test() {
        for i in 0..100 {
            let strings = (0..i)
                .map(|i| ArcStr::from(i.to_string()))
                .collect::<Vec<_>>();
            for j in 0..1000 {
                let _ignore = ArcStr::from(j.to_string());
            }
            for (k, s) in strings.iter().enumerate() {
                assert_eq!(k.to_string(), s.as_str());
            }
        }
    }
}


/*
 * 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.
 */

pub(crate) struct IteratorAsExactSizeIterator<I>(pub(crate) I);

impl<I: Iterator> Iterator for IteratorAsExactSizeIterator<I> {
    type Item = I::Item;

    fn next(&mut self) -> Option<Self::Item> {
        self.0.next()
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        self.0.size_hint()
    }
}

impl<J: Iterator> ExactSizeIterator for IteratorAsExactSizeIterator<J> {
    fn len(&self) -> usize {
        self.0.size_hint().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.
 */

pub(crate) mod base;
mod fat;
pub(crate) mod iterator_as_exact_size_iterator;
mod slice;
mod string_like;
mod thin;
mod thin_slice;

pub use crate::arc_str::fat::ArcStr;
pub use crate::arc_str::slice::ArcSlice;
pub use crate::arc_str::string_like::ArcS;
pub use crate::arc_str::string_like::StringInside;
pub use crate::arc_str::thin::ThinArcStr;
pub use crate::arc_str::thin_slice::ThinArcSlice;


/*
 * 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.
 */

#![allow(clippy::from_iter_instead_of_collect)]

use std::borrow::Borrow;
use std::hash::Hash;
use std::ops::Deref;

use allocative::Allocative;
use dupe::Dupe;
use triomphe::Arc;

use crate::arc_str::iterator_as_exact_size_iterator::IteratorAsExactSizeIterator;

/// `Arc<[T]>` but more efficient.
#[derive(Debug, Allocative)]
pub struct ArcSlice<T> {
    // This can be `NonNull<[T]>` when `Arc::from_raw` is available:
    // https://github.com/Manishearth/triomphe/pull/57/files
    slice: Option<Arc<[T]>>,
}

impl<T> ArcSlice<T> {
    #[inline]
    pub fn new<const N: usize>(array: [T; N]) -> ArcSlice<T> {
        ArcSlice::from_iter(array)
    }
}

impl<T> Clone for ArcSlice<T> {
    #[inline]
    fn clone(&self) -> ArcSlice<T> {
        ArcSlice {
            slice: self.slice.clone(),
        }
    }
}

impl<T> Dupe for ArcSlice<T> {}

impl<T> Deref for ArcSlice<T> {
    type Target = [T];

    #[inline]
    fn deref(&self) -> &[T] {
        match self.slice {
            Some(ref slice) => slice,
            None => &[],
        }
    }
}

impl<T> Default for ArcSlice<T> {
    #[inline]
    fn default() -> ArcSlice<T> {
        ArcSlice { slice: None }
    }
}

impl<T: Eq> PartialEq for ArcSlice<T> {
    #[inline]
    fn eq(&self, other: &ArcSlice<T>) -> bool {
        if let (Some(this), Some(other)) = (&self.slice, &other.slice) {
            // We require `T: Eq` so we can use `Arc::ptr_eq`.
            if Arc::ptr_eq(this, other) {
                return true;
            }
        }
        self[..] == other[..]
    }
}

impl<T: Eq> Eq for ArcSlice<T> {}

impl<T: Hash> Hash for ArcSlice<T> {
    #[inline]
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        // This must hash as slice because we implement `Borrow<[T]>`.
        self[..].hash(state)
    }
}

impl<T> Borrow<[T]> for ArcSlice<T> {
    #[inline]
    fn borrow(&self) -> &[T] {
        self
    }
}

impl<T> FromIterator<T> for ArcSlice<T> {
    #[inline]
    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
        let iter = iter.into_iter();
        let (lower, upper) = iter.size_hint();
        if upper == Some(0) {
            ArcSlice::default()
        } else if Some(lower) == upper {
            let arc = Arc::from_header_and_iter((), IteratorAsExactSizeIterator(iter));
            let arc: Arc<[T]> = arc.into();
            if arc.is_empty() {
                ArcSlice::default()
            } else {
                ArcSlice { slice: Some(arc) }
            }
        } else {
            let vec = Vec::from_iter(iter);
            ArcSlice::from(vec)
        }
    }
}

impl<T> From<Vec<T>> for ArcSlice<T> {
    #[inline]
    fn from(vec: Vec<T>) -> ArcSlice<T> {
        if vec.is_empty() {
            ArcSlice::default()
        } else {
            ArcSlice {
                slice: Some(Arc::from(vec)),
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use crate::arc_str::slice::ArcSlice;

    #[test]
    fn test_from_iter_exact_size() {
        let slice = ArcSlice::from_iter([17, 19]);
        assert_eq!([17, 19], *slice);
        let slice = ArcSlice::from_iter([21; 0]);
        assert_eq!([23; 0], *slice);
    }

    #[test]
    fn test_from_iter_unknown_size() {
        let slice = ArcSlice::from_iter(vec![17, 19].into_iter().filter(|_| true));
        assert_eq!([17, 19], *slice);
        let slice = ArcSlice::from_iter(vec![17, 19].into_iter().filter(|_| false));
        assert_eq!([23; 0], *slice);
    }
}


/*
 * 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::fmt;
use std::fmt::Display;
use std::marker::PhantomData;
use std::ops::Deref;

use allocative::Allocative;
use dupe::Clone_;
use dupe::Dupe_;

use crate::arc_str::ArcStr;

/// Unsized type which is a string inside.
pub trait StringInside {
    /// Convert to a string.
    fn as_str(wrapper: &Self) -> &str;
    /// Convert from a string.
    fn from_str(s: &str) -> &Self;
}

#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Clone_, Dupe_, Debug, Allocative)]
#[allocative(bound = "")]
pub struct ArcS<S: StringInside + ?Sized> {
    s: ArcStr,
    _marker: PhantomData<*const S>,
}

// Copy-paste these two lines from `std::sync::Arc`.
unsafe impl<S: StringInside + ?Sized + Sync + Send> Send for ArcS<S> {}
unsafe impl<S: StringInside + ?Sized + Sync + Send> Sync for ArcS<S> {}

impl<S: StringInside + ?Sized> ArcS<S> {
    // Cannot implement `TryFrom` trait, something about conflicting implementations.
    #[inline]
    pub fn try_from<'a>(s: &'a str) -> anyhow::Result<ArcS<S>>
    where
        &'a S: TryFrom<&'a str, Error = anyhow::Error>,
        S: 'a,
    {
        let s: &S = TryFrom::try_from(s)?;
        Ok(ArcS::from(s))
    }
}

impl<'a, S: StringInside + ?Sized> From<&'a S> for ArcS<S> {
    #[inline]
    fn from(s: &'a S) -> Self {
        Self {
            s: ArcStr::from(S::as_str(s)),
            _marker: PhantomData,
        }
    }
}

impl<S: StringInside + ?Sized> Deref for ArcS<S> {
    type Target = S;

    #[inline]
    fn deref(&self) -> &Self::Target {
        S::from_str(&self.s)
    }
}

impl<S: StringInside + ?Sized> Borrow<S> for ArcS<S> {
    #[inline]
    fn borrow(&self) -> &S {
        self
    }
}

impl<S: StringInside + Display + ?Sized> Display for ArcS<S> {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        Display::fmt(&**self, f)
    }
}

#[cfg(test)]
mod tests {
    use std::fmt;
    use std::fmt::Display;
    use std::hash::Hash;
    use std::hash::Hasher;

    use crate::arc_str::string_like::ArcS;
    use crate::arc_str::string_like::StringInside;

    #[derive(Hash)]
    struct MyStringWrapper(str);

    impl Display for MyStringWrapper {
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
            write!(f, "<{}>", &self.0)
        }
    }

    impl StringInside for MyStringWrapper {
        #[inline]
        fn as_str(wrapper: &Self) -> &str {
            &wrapper.0
        }
        #[inline]
        fn from_str(s: &str) -> &Self {
            unsafe { &*(s as *const str as *const Self) }
        }
    }

    impl MyStringWrapper {
        fn new(s: &str) -> &MyStringWrapper {
            unsafe { &*(s as *const str as *const Self) }
        }
    }

    #[test]
    fn test_simple() {
        let original = MyStringWrapper::new("hello");
        let s = ArcS::<MyStringWrapper>::from(original);
        let returned: &MyStringWrapper = &s;
        assert_eq!("hello", &returned.0);
    }

    #[test]
    fn test_hash_is_transparent() {
        fn hash<T: Hash + ?Sized>(t: &T) -> u64 {
            let mut hasher = std::collections::hash_map::DefaultHasher::new();
            t.hash(&mut hasher);
            hasher.finish()
        }

        assert_eq!(
            hash("hello"),
            hash(&ArcS::from(MyStringWrapper::new("hello")))
        );
    }

    #[test]
    fn test_display() {
        let s = ArcS::<MyStringWrapper>::from(MyStringWrapper::new("hello"));
        assert_eq!("<hello>", format!("{}", s));
    }
}


/*
 * 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::fmt::Debug;
use std::fmt::Display;
use std::hash::Hash;
use std::ops::Deref;
use std::str;

use allocative::Allocative;
use dupe::Dupe;
use serde::Serialize;
use static_assertions::assert_eq_size;

use crate::arc_str::base::ArcStrBase;
use crate::arc_str::base::ArcStrBaseInner;
use crate::arc_str::base::ArcStrBaseInnerConst;
use crate::arc_str::base::ArcStrLenStrategy;

struct ThinArcStrProperties;

unsafe impl ArcStrLenStrategy for ThinArcStrProperties {
    type AllocatedPayload = u32;
    type ValuePayload = ();
    const EMPTY: (Self::AllocatedPayload, Self::ValuePayload) = (0, ());

    #[inline]
    fn unpack_len(d: (Self::AllocatedPayload, Self::ValuePayload)) -> u32 {
        d.0
    }

    #[inline]
    fn pack_len(len: u32) -> (Self::AllocatedPayload, Self::ValuePayload) {
        (len, ())
    }

    #[inline]
    fn inner_empty() -> &'static ArcStrBaseInnerConst<Self> {
        &INNER_EMPTY
    }
}

/// Wrapper for `Arc<str>`.
#[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Allocative, Clone, Dupe, Default)]
pub struct ThinArcStr {
    base: ArcStrBase<ThinArcStrProperties>,
}

assert_eq_size!(ThinArcStr, usize);
assert_eq_size!(Option<ThinArcStr>, usize);

static INNER_EMPTY: ArcStrBaseInnerConst<ThinArcStrProperties> = ArcStrBaseInner::EMPTY;

impl ThinArcStr {
    #[inline]
    pub fn as_str(&self) -> &str {
        self.base.as_str()
    }
}

impl Deref for ThinArcStr {
    type Target = str;

    #[inline]
    fn deref(&self) -> &str {
        self.as_str()
    }
}

impl AsRef<str> for ThinArcStr {
    #[inline]
    fn as_ref(&self) -> &str {
        self.as_str()
    }
}

impl Borrow<str> for ThinArcStr {
    #[inline]
    fn borrow(&self) -> &str {
        self.as_str()
    }
}

impl Display for ThinArcStr {
    #[inline]
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        Display::fmt(self.as_str(), f)
    }
}

impl Debug for ThinArcStr {
    #[inline]
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        Debug::fmt(self.as_str(), f)
    }
}

impl<'a> From<&'a str> for ThinArcStr {
    #[inline]
    fn from(s: &'a str) -> ThinArcStr {
        ThinArcStr {
            base: ArcStrBase::from(s),
        }
    }
}

impl From<String> for ThinArcStr {
    #[inline]
    fn from(s: String) -> ThinArcStr {
        ThinArcStr::from(s.as_str())
    }
}

impl Serialize for ThinArcStr {
    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        self.as_str().serialize(serializer)
    }
}

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

    use dupe::Dupe;

    use crate::arc_str::thin::ThinArcStr;

    #[test]
    fn test_serialize() {
        assert_eq!(
            "\"hello\"",
            serde_json::to_string(&ThinArcStr::from("hello")).unwrap()
        );
        assert_eq!(
            "\"\"",
            serde_json::to_string(&ThinArcStr::from("")).unwrap()
        );
    }

    #[test]
    fn test_hash() {
        fn hash<H: Hash + ?Sized>(h: &H) -> u64 {
            let mut hasher = DefaultHasher::new();
            h.hash(&mut hasher);
            hasher.finish()
        }

        assert_eq!(hash(""), hash(&ThinArcStr::from("")));
        assert_eq!(hash("hello"), hash(&ThinArcStr::from("hello")));
    }

    #[test]
    fn test_clone_drop_ref_count() {
        fn ref_count(s: &ThinArcStr) -> u32 {
            assert!(!s.is_empty());
            s.base.inner().refcount.load(atomic::Ordering::Relaxed)
        }

        let s = ThinArcStr::from("hello");
        assert_eq!(1, ref_count(&s));
        let s2 = s.dupe();
        assert_eq!(2, ref_count(&s));
        drop(s);
        assert_eq!(1, ref_count(&s2));
    }

    #[test]
    fn test_little_stress_test() {
        for i in 0..100 {
            let strings = (0..i)
                .map(|i| ThinArcStr::from(i.to_string()))
                .collect::<Vec<_>>();
            for j in 0..1000 {
                let _ignore = ThinArcStr::from(j.to_string());
            }
            for (k, s) in strings.iter().enumerate() {
                assert_eq!(k.to_string(), s.as_str());
            }
        }
    }
}


/*
 * 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::hash::Hash;
use std::mem;
use std::ops::Deref;
use std::slice;

use allocative::Allocative;
use dupe::Dupe;
use triomphe::ThinArc;

use crate::arc_str::iterator_as_exact_size_iterator::IteratorAsExactSizeIterator;

#[derive(Allocative, Debug)]
pub struct ThinArcSlice<T> {
    slice: Option<ThinArc<(), T>>,
}

impl<T> Clone for ThinArcSlice<T> {
    #[inline]
    fn clone(&self) -> ThinArcSlice<T> {
        ThinArcSlice {
            slice: self.slice.clone(),
        }
    }
}

impl<T> Dupe for ThinArcSlice<T> {}

impl<T: PartialEq> PartialEq for ThinArcSlice<T> {
    fn eq(&self, other: &Self) -> bool {
        self.as_slice() == other.as_slice()
    }
}

impl<T: Eq> Eq for ThinArcSlice<T> {}

impl<T: Hash> Hash for ThinArcSlice<T> {
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        self.as_slice().hash(state)
    }
}

impl<T: PartialOrd> PartialOrd for ThinArcSlice<T> {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        self.as_slice().partial_cmp(other.as_slice())
    }
}

impl<T: Ord> Ord for ThinArcSlice<T> {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        self.as_slice().cmp(other.as_slice())
    }
}

impl<T> ThinArcSlice<T> {
    #[inline]
    pub const fn empty() -> ThinArcSlice<T> {
        ThinArcSlice { slice: None }
    }

    #[inline]
    pub fn new<const N: usize>(array: [T; N]) -> ThinArcSlice<T> {
        ThinArcSlice::from_iter(array)
    }

    #[inline]
    pub fn as_slice(&self) -> &[T] {
        self
    }
}

const _: () = assert!(mem::size_of::<*const u8>() == mem::size_of::<ThinArcSlice<String>>());

impl<T> Deref for ThinArcSlice<T> {
    type Target = [T];

    #[inline]
    fn deref(&self) -> &[T] {
        match &self.slice {
            Some(slice) => &slice.slice,
            None => &[],
        }
    }
}

impl<T> Default for ThinArcSlice<T> {
    #[inline]
    fn default() -> ThinArcSlice<T> {
        ThinArcSlice { slice: None }
    }
}

impl<'a, T> IntoIterator for &'a ThinArcSlice<T> {
    type Item = &'a T;
    type IntoIter = slice::Iter<'a, T>;

    #[inline]
    fn into_iter(self) -> slice::Iter<'a, T> {
        self.iter()
    }
}

impl<T> FromIterator<T> for ThinArcSlice<T> {
    #[allow(clippy::from_iter_instead_of_collect)]
    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
        let iter = iter.into_iter();
        let (lower, upper) = iter.size_hint();
        if upper == Some(0) {
            ThinArcSlice::default()
        } else if Some(lower) == upper {
            let arc = ThinArc::from_header_and_iter((), IteratorAsExactSizeIterator(iter));
            if arc.slice.is_empty() {
                ThinArcSlice::default()
            } else {
                ThinArcSlice { slice: Some(arc) }
            }
        } else {
            let vec = Vec::from_iter(iter);
            ThinArcSlice::from_iter(vec)
        }
    }
}

#[cfg(test)]
mod tests {
    use crate::arc_str::thin_slice::ThinArcSlice;

    #[test]
    fn test_empty() {
        let slice = ThinArcSlice::<String>::default();
        assert_eq!(0, slice.len());
    }

    #[test]
    fn test_new() {
        let slice = ThinArcSlice::new(["a".to_owned(), "b".to_owned()]);
        assert_eq!(["a".to_owned(), "b".to_owned()], *slice);
    }

    #[allow(clippy::from_iter_instead_of_collect)]
    #[test]
    fn test_from_iter() {
        // Iterator without size hint.
        let iter = vec!["a".to_owned(), "b".to_owned()]
            .into_iter()
            .filter(|_| true);
        let slice = ThinArcSlice::from_iter(iter);
        assert_eq!(["a".to_owned(), "b".to_owned()], *slice);
    }
}


/*
 * 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.
 */

pub mod ordered_map;
pub mod ordered_set;
pub mod sorted_map;
pub mod sorted_set;
pub mod sorted_vec;
pub mod unordered_map;
pub mod unordered_set;