/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This software may be used and distributed according to the terms of the
 * GNU General Public License version 2.
 */

use std::collections::HashMap;
use std::sync::Arc;

use anyhow::format_err;
use anyhow::Result;
use manifest::testutil::*;
use manifest::Manifest;
use minibytes::Bytes;
use parking_lot::Mutex;
use parking_lot::RwLock;
use storemodel::TreeFormat;
use types::testutil::*;
use types::HgId;
use types::Key;
use types::RepoPath;
use types::RepoPathBuf;

use crate::FileMetadata;
use crate::TreeManifest;
use crate::TreeStore;

pub fn make_tree_manifest<'a>(
    store: Arc<TestStore>,
    paths: impl IntoIterator<Item = &'a (&'a str, &'a str)>,
) -> TreeManifest {
    let mut tree = TreeManifest::ephemeral(store);
    for (path, filenode) in paths {
        tree.insert(repo_path_buf(path), make_meta(filenode))
            .unwrap();
    }
    tree
}

pub fn make_tree_manifest_from_meta(
    store: Arc<TestStore>,
    paths: impl IntoIterator<Item = (RepoPathBuf, FileMetadata)>,
) -> TreeManifest {
    let mut tree = TreeManifest::ephemeral(store);
    for (path, meta) in paths {
        tree.insert(path, meta).unwrap();
    }
    tree
}

/// An in memory `Store` implementation backed by HashMaps. Primarily intended for tests.
pub struct TestStore {
    entries: RwLock<HashMap<RepoPathBuf, HashMap<HgId, Bytes>>>,
    pub prefetched: Mutex<Vec<Vec<Key>>>,
    format: TreeFormat,
}

impl TestStore {
    pub fn new() -> Self {
        TestStore {
            entries: RwLock::new(HashMap::new()),
            prefetched: Mutex::new(Vec::new()),
            format: TreeFormat::Hg,
        }
    }

    pub fn with_format(mut self, format: TreeFormat) -> Self {
        self.format = format;
        self
    }

    #[allow(unused)]
    pub fn fetches(&self) -> Vec<Vec<Key>> {
        self.prefetched.lock().clone()
    }
}

impl TreeStore for TestStore {
    fn get(&self, path: &RepoPath, hgid: HgId) -> Result<Bytes> {
        let underlying = self.entries.read();
        let result = underlying
            .get(path)
            .and_then(|hgid_hash| hgid_hash.get(&hgid))
            .map(|entry| entry.clone());
        result.ok_or_else(|| format_err!("Could not find manifest entry for ({}, {})", path, hgid))
    }

    fn insert(&self, path: &RepoPath, hgid: HgId, data: Bytes) -> Result<()> {
        let mut underlying = self.entries.write();
        underlying
            .entry(path.to_owned())
            .or_insert(HashMap::new())
            .insert(hgid, data);
        Ok(())
    }

    fn prefetch(&self, keys: Vec<Key>) -> Result<()> {
        self.prefetched.lock().push(keys);
        Ok(())
    }

    fn format(&self) -> TreeFormat {
        self.format
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This software may be used and distributed according to the terms of the
 * GNU General Public License version 2.
 */

pub static METALOG_TRACKED: [&str; 6] = [
    "bookmarks",
    "cloudsyncstate",
    "config",
    "remotenames",
    "tip",
    "visibleheads",
];


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This software may be used and distributed according to the terms of the
 * GNU General Public License version 2.
 */

use std::fmt;

#[derive(Debug)]
pub struct Error(pub(crate) String);

pub type Result<T> = std::result::Result<T, Error>;

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.0.fmt(f)
    }
}

impl std::error::Error for Error {}

pub trait ForeignError: ToString {}

impl<T: ForeignError> From<T> for Error {
    fn from(err: T) -> Error {
        Error(err.to_string())
    }
}

impl ForeignError for anyhow::Error {}
impl ForeignError for indexedlog::Error {}
impl ForeignError for mincode::Error {}
impl ForeignError for std::io::Error {}
impl ForeignError for std::str::Utf8Error {}
impl ForeignError for std::num::ParseIntError {}
impl ForeignError for zstore::Error {}
impl ForeignError for types::hash::LengthMismatchError {}
impl ForeignError for String {}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This software may be used and distributed according to the terms of the
 * GNU General Public License version 2.
 */

use std::collections::HashMap;
use std::collections::HashSet;
use std::io::Write;
use std::path::Path;
use std::process::Command;
use std::process::Stdio;

use crate::metalog::load_root;
use crate::metalog::Id20;
use crate::metalog::SerId20;
use crate::MetaLog;
use crate::Result;

impl MetaLog {
    /// Export metalog to a git repo for investigation.
    pub fn export_git(&self, repo_path: &Path) -> Result<()> {
        let mut payload = FastImportPayload::new("metalog <metalog@example.com>");

        let root_ids = Self::list_roots(&self.path)?;
        let mut blob_id_map = HashMap::new(); // Metalog Blob SHA1 -> BlobId
        let mut commit_id_map = HashMap::new(); // Metalog Root SHA1 -> CommitId
        let listed: HashSet<_> = root_ids.iter().copied().collect();

        // Figure out the "parents" relationship.
        let parents: HashMap<Id20, Vec<Id20>> = {
            let mut parents: HashMap<Id20, Vec<Id20>> = HashMap::new();

            // From the root_id list.
            for slice in root_ids.windows(2) {
                if let [parent, child] = slice {
                    parents.insert(*child, vec![*parent]);
                }
            }

            // From the implicit "Parent: " messages.
            // They might include pending changes. See D30970502.
            for root_id in root_ids.iter().copied() {
                let root = load_root(&self.blobs.read(), root_id)?;
                for line in root.message.lines() {
                    if let Some(hex_parent) = line.strip_prefix("Parent: ") {
                        if let Ok(parent_root_id) = Id20::from_hex(hex_parent.as_bytes()) {
                            if let Ok(_parent_root) = load_root(&self.blobs.read(), parent_root_id)
                            {
                                let parents = parents.entry(root_id).or_default();
                                if !parents.contains(&parent_root_id) {
                                    parents.push(parent_root_id);
                                }
                            }
                        }
                    }
                }
            }
            parents
        };

        // Export everything reachable from the "current" root.
        let mut to_visit: Vec<Id20> = vec![self.orig_root_id];
        while let Some(root_id) = to_visit.pop() {
            if commit_id_map.contains_key(&root_id) {
                // Already committed.
                continue;
            }

            let root_parents: &[Id20] = match parents.get(&root_id) {
                Some(parents) => parents.as_ref(),
                None => &[],
            };

            {
                // Need to commit missing parents first?
                let mut missing_parents = false;
                for parent in root_parents {
                    if !commit_id_map.contains_key(parent) {
                        if !missing_parents {
                            to_visit.push(root_id);
                            missing_parents = true;
                        }
                        to_visit.push(*parent);
                    }
                }
                if missing_parents {
                    continue;
                }
            }

            let root = load_root(&self.blobs.read(), root_id)?;

            // Add blobs.
            for (_key, SerId20(value_id)) in root.map.iter() {
                if blob_id_map.contains_key(value_id) {
                    continue;
                }
                let value = self
                    .blobs
                    .read()
                    .get(*value_id)?
                    .ok_or_else(|| self.error(format!("cannot read {:?}", value_id)))?;
                let git_blob_id = payload.blob(&value);
                blob_id_map.insert(*value_id, git_blob_id);
            }

            // Prepare files.
            let path_blob_ids: Vec<(String, BlobId)> = root
                .map
                .iter()
                .map(|(path, SerId20(value_id))| (path.to_string(), blob_id_map[value_id]))
                .collect();

            // Add commit.
            let git_parents: Vec<CommitId> = root_parents
                .iter()
                .filter_map(|p| commit_id_map.get(p))
                .cloned()
                .collect();
            let detach_message = if listed.contains(&root_id) {
                ""
            } else {
                "\nDetached: true"
            };
            let message = format!(
                "{}\n\nRootId: {}{}",
                root.message,
                root_id.to_hex(),
                detach_message
            );
            let commit_id = payload.commit(&message, root.timestamp, &git_parents, &path_blob_ids);
            commit_id_map.insert(root_id, commit_id);
        }

        // Run 'git init' if the directory does not exist.
        if !repo_path.is_dir() {
            Command::new("git")
                .args(["-c", "init.defaultBranch=main", "init", "-q"])
                .arg(repo_path)
                .status()?;
        }

        // Run 'git fast-import'.
        let payload: Vec<u8> = payload.into_vec();
        let mut import_process = Command::new("git")
            .args(["fast-import", "--quiet"])
            .current_dir(repo_path)
            .stdin(Stdio::piped())
            .spawn()?;
        let mut stdin = import_process.stdin.take().unwrap();
        stdin.write_all(&payload)?;
        drop(stdin);

        import_process.wait()?;

        Ok(())
    }
}

/// Payload for git-fast-import.
struct FastImportPayload {
    author: &'static str,
    id_count: usize,
    payload: Vec<u8>,
}

#[derive(Copy, Clone, Debug)]
struct BlobId(usize);

#[derive(Copy, Clone, Debug)]
struct CommitId(usize);

impl FastImportPayload {
    pub fn new(author: &'static str) -> Self {
        Self {
            author,
            id_count: 0,
            payload: Vec::new(),
        }
    }

    pub fn into_vec(self) -> Vec<u8> {
        self.payload
    }

    pub fn blob(&mut self, data: &[u8]) -> BlobId {
        let id = self.next_id();
        let payload = &mut self.payload;
        payload.extend_from_slice(b"blob\n");
        payload.extend_from_slice(format!("mark :{}\n", id).as_bytes());
        payload.extend_from_slice(format!("data {}\n", data.len()).as_bytes());
        payload.extend_from_slice(data);
        payload.push(b'\n');
        BlobId(id)
    }

    pub fn commit(
        &mut self,
        message: &str,
        timestamp: u64,
        parents: &[CommitId],
        path_blob_ids: &[(String, BlobId)],
    ) -> CommitId {
        let commit_id = self.next_id();
        let parents_lines = parents
            .iter()
            .enumerate()
            .map(|(i, parent_id)| {
                let prefix = if i == 0 { "from" } else { "merge" };
                format!("{} :{}\n", prefix, parent_id.0)
            })
            .collect::<Vec<_>>()
            .concat();
        let files_lines = path_blob_ids
            .iter()
            .map(|(path, blob_id)| format!("M 100644 :{} {}\n", blob_id.0, path))
            .collect::<Vec<_>>()
            .concat();
        let when = format!("{} +0000", timestamp);
        let commit_payload = format!(
            concat!(
                "commit refs/heads/main\n",
                "mark :{commit_id}\n",
                "committer {author} {when}\n",
                "data {message_len}\n{message}\n",
                "{parents_lines}",
                "deleteall\n",
                "{files_lines}",
                "\n",
            ),
            commit_id = commit_id,
            author = self.author,
            when = when,
            message_len = message.len(),
            message = &message,
            parents_lines = parents_lines,
            files_lines = files_lines,
        );
        self.payload.extend_from_slice(commit_payload.as_bytes());
        CommitId(commit_id)
    }

    fn next_id(&mut self) -> usize {
        self.id_count += 1;
        self.id_count
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This software may be used and distributed according to the terms of the
 * GNU General Public License version 2.
 */

//! # metalog
//!
//! See [`MetaLog`] for the main structure.

pub mod constants;
mod errors;
mod export;
mod metalog;

pub use errors::Error;
pub use errors::Result;
pub use indexedlog::Repair;
#[cfg(test)]
use parking_lot::Mutex;

pub use crate::metalog::resolver;
pub use crate::metalog::CommitOptions;
pub use crate::metalog::Id20;
pub use crate::metalog::MetaLog;

#[cfg(test)]
/// Lock for the environment.  This should be acquired by tests that rely on particular
/// environment variable values that might be overwritten by other tests.
pub static ENV_LOCK: Mutex<()> = Mutex::new(());


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This software may be used and distributed according to the terms of the
 * GNU General Public License version 2.
 */

use std::collections::BTreeMap;
use std::env;
use std::fmt;
use std::fs;
use std::io::Write;
use std::path::Path;
use std::path::PathBuf;
use std::sync::Arc;

use anyhow::Context;
use indexedlog::lock::ScopedDirLock;
use indexedlog::log as ilog;
use indexedlog::OpenWithRepair;
use indexedlog::Repair;
use lazy_static::lazy_static;
use minibytes::Bytes;
use parking_lot::RwLock;
use serde::Deserialize;
use serde::Serialize;
use serde_json::Value;
pub use zstore::Id20;
use zstore::Zstore;

use crate::constants::*;
use crate::Error;
use crate::Result;

/// Key-value metadata storage that can be atomically read and written,
/// and preserves a linear history.
///
/// Suitable to store small metadata. Namely:
/// - The number of different keys are bounded.
///   Ideally the keys is a constant list.
///   Good example: keys are constant, ex. `["bookmarks", "heads"]`.
///   Bad example: keys are dynamic, ex. file names.
/// - The size of values are bounded.
///   Consider using one level of indirection if huge data needs to be
///   stored.
///
/// Values under a same key are delta-ed against their previous versions.  This
/// means the storage is still efficient even if the data is large but changes
/// are small. Unlike traditional mercurial bdiff (used in revlogs), the delta
/// algorithm works fine for binary data.
///
/// The [`MetaLog`] does not specify actual format of `value`s. It's up to the
/// upper layer to define them.
pub struct MetaLog {
    pub(crate) path: PathBuf,

    /// blobs/log storage (sub) directory
    store_path: PathBuf,
    compaction_epoch: Option<u64>,

    /// An append-only store - each entry contains a plain Id20
    /// to an object that should be a root.
    log: Arc<RwLock<ilog::Log>>,

    /// General purposed blob store, keyed by Id20.
    pub(crate) blobs: Arc<RwLock<Zstore>>,

    /// The Id20 of root that has been written to disk.
    pub(crate) orig_root_id: Id20,

    /// The current (possibly modified) root.
    root: Root,
}

/// Options used by the `commit` API.
#[derive(Default)]
pub struct CommitOptions<'a> {
    pub message: &'a str,
    pub timestamp: u64,

    /// Do not write the new root id to the "roots" log.
    /// This is useful for cross-process transactions.
    pub detached: bool,

    /// How to resolve conflicts.
    ///
    /// The function takes the current MetaLog being written, and another MetaLog
    /// containing the latest change, and an "ancestor" MetaLog that contains data
    /// before the current pending changes.
    /// The function should try to set resolved contents on the current MetaLog and
    /// return `Ok(())` if it is able to resolve everything cleanly.
    pub resolver: Option<Box<dyn FnMut(&mut MetaLog, &MetaLog, &MetaLog) -> Result<()>>>,

    /// Prevent constructing via fields.
    _private: (),
}

impl MetaLog {
    /// Open [`MetaLog`] at the given directory. Create on demand.
    ///
    /// If `root_id` is `None`, the last `root_id` in the log will
    /// be used. Otherwise the specific `root_id` is used.
    pub fn open(path: impl AsRef<Path>, root_id: Option<Id20>) -> Result<MetaLog> {
        let path = path.as_ref();
        let (store_path, compaction_epoch) = resolve_compaction_epoch(path)?;
        let log = Self::ilog_open_options().open_with_repair(store_path.join("roots"))?;
        let orig_root_id = match root_id {
            Some(id) => id,
            None => find_last_root_id(&log)?,
        };
        // 'cache_size' is not defined scientifically. Adjust if needed.
        let cache_size = 100;
        let blobs = zstore::OpenOptions::default()
            .cache_size(cache_size)
            .open(&store_path.join("blobs"))?;
        let root = load_root(&blobs, orig_root_id)?;
        let metalog = MetaLog {
            path: path.to_path_buf(),
            store_path,
            compaction_epoch,
            log: Arc::new(RwLock::new(log)),
            blobs: Arc::new(RwLock::new(blobs)),
            orig_root_id,
            root,
        };
        tracing::debug!("opened with root {}", orig_root_id.to_hex());
        Ok(metalog)
    }

    /// Similar to open, but tries to get `root_id` from environment variables
    pub fn open_from_env(metalog_path: &Path) -> Result<MetaLog> {
        let metalog_root = if let Ok(forced_metalog_root) = env::var("HGFORCEMETALOGROOT") {
            Some(forced_metalog_root)
        } else if let Ok(pending_metalog) = env::var("HG_PENDING_METALOG") {
            if let Some(metalog_path) = metalog_path.to_str() {
                decode_pending_metalog(pending_metalog, metalog_path)?
            } else {
                None
            }
        } else {
            None
        };
        let root = if let Some(metalog_root) = metalog_root {
            if let Ok(decoded_root) = hex::decode(&metalog_root) {
                Some(Id20::from_slice(&decoded_root)?)
            } else {
                None
            }
        } else {
            None
        };
        MetaLog::open(metalog_path, root)
    }

    /// Obtain a new `MetaLog` with a different `root_id`.
    pub fn checkout(&self, root_id: Id20) -> Result<Self> {
        let root = load_root(&self.blobs.read(), root_id)?;
        let metalog = Self {
            path: self.path.clone(),
            store_path: self.store_path.clone(),
            compaction_epoch: self.compaction_epoch,
            log: self.log.clone(),
            blobs: self.blobs.clone(),
            orig_root_id: root_id,
            root,
        };
        Ok(metalog)
    }

    /// Compact the metalog. Compaction reclaims storage by retaining only blobs
    /// reachable from the current root. All other roots (and blobs) are
    /// discarded.
    ///
    /// After compaction writes through outstanding metalog handles will fail.
    /// Reads through outstanding metalog handles are unaffected.
    pub fn compact(path: impl AsRef<Path>) -> Result<()> {
        let _lock = ScopedDirLock::new(&path.as_ref());
        let metalog = Self::open(path, None)?;
        let curr_epoch = metalog.compaction_epoch.unwrap_or(0);
        // allow for a small (and arbitrary) number of failures to compact the metalog
        // note the side-effect of creating the new store sub-directory
        let next_epoch = (curr_epoch.checked_add(1).unwrap()..curr_epoch.checked_add(10).unwrap())
            .find(|epoch| fs::create_dir(metalog.path.join(epoch.to_string())).is_ok())
            .ok_or_else(|| "Failed to create compaction directory".to_string())?;
        {
            // the metalog open (create) happens directly against the *new* store sub-directory,
            // bypassing the current "pointer" resolution and defeating the write locking
            // (this function took the needed lock).
            let mut compact_metalog = Self::open(metalog.path.join(next_epoch.to_string()), None)?;
            for key in metalog.keys() {
                if let Some(value) = metalog.get(&key)? {
                    compact_metalog.set(&key, &value)?;
                }
            }
            let opts = CommitOptions {
                timestamp: metalog.root.timestamp,
                message: &metalog.root.message,
                ..Default::default()
            };
            compact_metalog
                .commit(opts)
                .with_context(|| "Failed to commit to compacted metalog")?;
        }
        indexedlog::utils::atomic_write(
            metalog.path.join("current"),
            next_epoch.to_string().as_bytes(),
            true,
        )
        .with_context(|| "Could not update metalog store pointer")?;
        if metalog.compaction_epoch.is_none() {
            let _ = fs::remove_dir_all(metalog.store_path.join("roots"));
            let _ = fs::remove_dir_all(metalog.store_path.join("blobs"));
        } else {
            assert!(metalog.store_path != metalog.path);
            let _ = fs::remove_dir_all(metalog.store_path);
        }

        Ok(())
    }

    /// List all `root_id`s stored in `path`.
    ///
    /// The oldest `root_id` is returned as the first item.
    pub fn list_roots(path: impl AsRef<Path>) -> Result<Vec<Id20>> {
        let (store_path, _) = resolve_compaction_epoch(path.as_ref())?;
        let log = Self::ilog_open_options().open_with_repair(store_path.join("roots"))?;
        let result = std::iter::once(EMPTY_ROOT_ID.clone())
            .chain(
                log.iter()
                    .map(|e| e.ok().and_then(|e| Id20::from_slice(e).ok()))
                    .take_while(|s| s.is_some())
                    .map(|s| s.unwrap()),
            )
            .collect();
        Ok(result)
    }

    /// Lookup a blob by key.
    pub fn get(&self, name: &str) -> Result<Option<Bytes>> {
        tracing::trace!("get {}", name);
        match self.root.map.get(name) {
            Some(SerId20(id)) => Ok(self.blobs.read().get(*id)?),
            None => Ok(None),
        }
    }

    /// Insert a blob entry with the given name.
    ///
    /// Changes are not flushed to disk. Use `flush` to write them.
    pub fn set(&mut self, name: &str, value: &[u8]) -> Result<Id20> {
        tracing::trace!("set {}", name);
        let delta_base_candidates = match self.root.map.get(name) {
            Some(SerId20(id)) => vec![*id],
            None => Vec::new(),
        };
        let new_id = self.blobs.write().insert(value, &delta_base_candidates)?;
        self.root.map.insert(name.to_string(), SerId20(new_id));
        Ok(new_id)
    }

    /// initialize metalog tracked keys
    pub fn init_tracked(&mut self) -> Result<()> {
        for key in METALOG_TRACKED {
            self.set(key, b"")?;
        }
        let tracked = METALOG_TRACKED.join("\n");
        self.set("tracked", tracked.as_bytes())?;
        let mut commit_opts = CommitOptions::default();
        commit_opts.message = "init tracked";
        self.commit(commit_opts)?;
        Ok(())
    }

    /// Remove an entry.
    ///
    /// Changes are not flushed to disk. Use `flush` to write them.
    pub fn remove(&mut self, name: &str) -> Result<()> {
        tracing::trace!("remove {}", name);
        self.root.map.remove(name);
        Ok(())
    }

    /// Get names of all keys.
    pub fn keys(&self) -> Vec<&str> {
        self.root.map.keys().map(AsRef::as_ref).collect()
    }

    /// Attempt to write pending changes to disk.
    ///
    /// Return the Id20 that can be passed to `open` for the new (or old) root.
    ///
    /// Raise an error if the on-disk state has changed. The callsite need to
    /// use some lock to protect races (in Mercurial, this can be a repo lock).
    ///
    /// If there are no key-value changes, then `message` and `timestamp` are
    /// discarded.  Otherwise, `message` and `timestamp` will be attached to the
    /// newly created "root" object. This is similar to commit message and date
    /// used by source control.
    pub fn commit(&mut self, options: CommitOptions) -> Result<Id20> {
        if !self.is_dirty() {
            // Nothing changed.
            return Ok(self.orig_root_id);
        }
        tracing::trace!("commit (before lock)");
        let _lock = ScopedDirLock::new(&self.path);
        tracing::debug!("commit (locked, detached = {})", options.detached);
        let (_, actual_compaction_epoch) = resolve_compaction_epoch(&self.path)?;
        if self.compaction_epoch != actual_compaction_epoch {
            return Err(Error(format!(
                "Commit failed. Compaction epoch changed since open; expected: {}, actual: {}",
                self.compaction_epoch.unwrap_or(0),
                actual_compaction_epoch.unwrap_or(0)
            )));
        }
        if self.is_latest_root_different()? && !options.detached {
            // If 'detached' is set, then just write it in a conflict-free way,
            // since the final root object is not committed yet.
            let ancestor = Self::open(&self.path, Some(self.orig_root_id))?;
            let other = Self::open(&self.path, None)?;
            let mut resolver = options
                .resolver
                .unwrap_or_else(|| Box::new(resolver::simple));
            (resolver)(self, &other, &ancestor)?;
        }
        self.root.message = options.message.to_string();
        self.root.timestamp = options.timestamp;
        let bytes = mincode::serialize(&self.root)?;
        let orig_root_id = self.orig_root_id;
        let mut blobs = self.blobs.write();
        let id = blobs.insert(&bytes, &vec![self.orig_root_id])?;
        blobs.flush()?;
        if !options.detached {
            let mut log = self.log.write();
            log.append(id.as_ref())?;
            log.sync()?;
            self.orig_root_id = id;
        }
        let current_store_paths = match self.compaction_epoch {
            Some(epoch) => vec![epoch.to_string()],
            None => vec!["roots".to_string(), "blobs".to_string()],
        };
        // try to cleanup any old metalog stores
        if let Ok(entries) = fs::read_dir(&self.path) {
            for entry in entries {
                if let Ok(entry) = entry {
                    if let Ok(file_name) = entry.file_name().into_string() {
                        if current_store_paths.contains(&file_name) {
                            continue;
                        }
                        // only remove directories that conform to the metalog store naming conventions
                        let is_numeric = file_name.parse::<u64>().is_ok();
                        if is_numeric || file_name == "blobs" || file_name == "roots" {
                            let _ = fs::remove_dir_all(entry.path());
                        }
                    }
                }
            }
        }
        tracing::debug!("committed {} => {}", orig_root_id.to_hex(), id.to_hex());
        Ok(id)
    }

    /// Check if the on-disk metalog's latest root is different from orig_root_id
    fn is_latest_root_different(&self) -> Result<bool> {
        let log = self.log.read();
        // The lastest root id in the in-process log is already different.
        if find_last_root_id(&log)? != self.orig_root_id {
            return Ok(true);
        }
        // The lastest root id in the in-process log is the same, but the
        // in-process log is outdated.
        if log.is_changed() {
            return Ok(true);
        }
        Ok(false)
    }

    /// Test if there are uncommitted changes.
    pub fn is_dirty(&self) -> bool {
        let bytes =
            mincode::serialize(&self.root).expect("in-memory mincode::serialize should success");
        zstore::sha1(&bytes) != self.orig_root_id
    }

    /// Why this change was made.
    pub fn message(&self) -> &str {
        &self.root.message
    }

    /// When this change was made, in seconds since epch.
    pub fn timestamp(&self) -> u64 {
        self.root.timestamp
    }

    /// Get the original root id.
    pub fn root_id(&self) -> Id20 {
        self.orig_root_id
    }

    /// Generate an error.
    pub fn error(&self, message: impl fmt::Display) -> Error {
        Error(format!("{:?}: {}", &self.path, message))
    }

    fn ilog_open_options() -> ilog::OpenOptions {
        ilog::OpenOptions::new()
            .index("reverse", |_| -> Vec<_> {
                // Reverse index so we can find the last entries quickly.
                vec![ilog::IndexOutput::Owned(
                    INDEX_REVERSE_KEY.to_vec().into_boxed_slice(),
                )]
            })
            .create(true)
    }
}

impl Repair<()> for MetaLog {
    fn repair(path: impl AsRef<Path>) -> indexedlog::Result<String> {
        let path = path.as_ref();
        let _lock = ScopedDirLock::new(path);
        let (store_path, _) = resolve_compaction_epoch(path).map_err(|e| {
            indexedlog::Error::from((
                "cannot locate storage dir (compaction epoch resolution failed)",
                e,
            ))
        })?;
        let blobs_path = store_path.join("blobs");
        let roots_path = store_path.join("roots");

        // Repair indexedlog without considering their dependencies.
        let mut message = format!(
            "Checking blobs at {:?}:\n{}\nChecking roots at {:?}:\n{}\n",
            &store_path,
            Zstore::repair(&blobs_path)?,
            &store_path,
            Self::ilog_open_options().repair(&roots_path)?,
        );

        // Check referred objects by Root and rollback to a Root where all objects are present.
        let blobs = Zstore::open(&blobs_path)
            .map_err(|e| indexedlog::Error::from(("cannot reopen blobs after repair", e)))?;
        let root_ids = MetaLog::list_roots(path)
            .map_err(|e| indexedlog::Error::from(("cannot list root ids after repair", e)))?;
        message += &format!("Checking blobs referred by {} Roots:\n", root_ids.len());

        // Filter out good Root IDs.
        let good_root_ids: Vec<Id20> = root_ids
            .iter()
            .filter(|root_id| match load_root(&blobs, **root_id) {
                Ok(root) => root
                    .map
                    .iter()
                    .all(|(key, SerId20(id))| match blobs.get(*id) {
                        Ok(Some(_)) => true,
                        _ => {
                            let desc = format!("Root {} ({})", root_id.to_hex(), root.message);
                            message +=
                                &format!("Key {:?} referred by {} cannot be read.\n", key, &desc);
                            false
                        }
                    }),
                Err(_) => {
                    message += &format!("Root {} cannot be read.\n", root_id.to_hex());
                    false
                }
            })
            .cloned()
            .collect();

        // Write out good Root IDs.
        if good_root_ids.len() == root_ids.len() {
            message += &format!("All Roots are verified.\n");
        } else {
            message += &format!(
                "Removing {} bad Root IDs.\n",
                root_ids.len() - good_root_ids.len()
            );

            // Write Root IDs to a backup in case something goes wrong.
            (|| -> std::io::Result<()> {
                let mut backup = std::fs::OpenOptions::new()
                    .append(true)
                    .create(true)
                    .open(store_path.join("roots.backup"))?;
                backup.write_all(
                    &root_ids.iter().map(|id| id.as_ref()).collect::<Vec<_>>()[..].concat(),
                )
            })()
            .map_err(|e| indexedlog::Error::from(("cannot create backup", e)))?;

            Self::ilog_open_options().delete_content(&roots_path)?;
            let mut root_id_log = Self::ilog_open_options().open_with_repair(&roots_path)?;
            for root_id in &good_root_ids {
                root_id_log.append(root_id.as_ref())?;
            }
            root_id_log.sync()?;
            message += &format!("Rebuilt Root log with {} Root IDs.\n", good_root_ids.len());
        }

        Ok(message)
    }
}

fn find_last_root_id(log: &ilog::Log) -> Result<Id20> {
    for entry in log.lookup(INDEX_REVERSE, INDEX_REVERSE_KEY)? {
        // The linked list in the index is in the reversed order.
        // So the first entry contains the last root id.
        return Ok(Id20::from_slice(entry?)?);
    }
    Ok(EMPTY_ROOT_ID.clone())
}

fn resolve_compaction_epoch(path: &Path) -> Result<(PathBuf, Option<u64>)> {
    match indexedlog::utils::atomic_read(&path.join("current")) {
        Ok(data) => {
            let epoch = std::str::from_utf8(&data)?;
            Ok((path.join(epoch), Some(epoch.parse()?)))
        }
        Err(err) if err.kind() == std::io::ErrorKind::NotFound => Ok((path.to_path_buf(), None)),
        Err(err) => Err(err.into()),
    }
}

fn decode_pending_metalog(pending_metalog: String, metalog_path: &str) -> Result<Option<String>> {
    let decoded_json = serde_json::from_str(pending_metalog.as_str());
    let decoded_json: Value = decoded_json.context("Failed to parse pending metalog")?;
    if let Value::String(s) = &decoded_json[metalog_path] {
        return Ok(Some(String::from(s)));
    }
    Ok(None)
}

pub(crate) fn load_root(blobs: &Zstore, id: Id20) -> Result<Root> {
    if id == EMPTY_ROOT_ID.clone() {
        return Ok(EMPTY_ROOT.clone());
    }
    let root = match blobs.get(id)? {
        Some(bytes) => mincode::deserialize(&bytes)?,
        None => {
            return Err(crate::Error(format!(
                "Root ID {} is not found",
                id.to_hex()
            )));
        }
    };

    Ok(root)
}

const INDEX_REVERSE: usize = 0;
const INDEX_REVERSE_KEY: &[u8] = b"r";

lazy_static! {
    static ref EMPTY_ROOT: Root = Root::default();
    static ref EMPTY_ROOT_ID: Id20 = zstore::sha1(
        &mincode::serialize(EMPTY_ROOT.deref()).expect("serialize EMPTY_ROOT should not fail")
    );
}

/// A root defines a snapshot of key-value pairs.
///
/// A root is similar to a commit in source control where the key-value pairs
/// can be seen as file name - file content pairs.
#[derive(Serialize, Deserialize, Default, Clone, Debug)]
pub(crate) struct Root {
    pub(crate) map: BTreeMap<String, SerId20>,
    pub(crate) timestamp: u64,
    pub(crate) message: String,
}

/// Force "tuple" serialization for `Id20`.
/// Practically with mincode, `SerId20` serializes to 20 bytes.
#[derive(Serialize, Deserialize, Default, Clone, Debug)]
pub(crate) struct SerId20(#[serde(with = "types::serde_with::hgid::tuple")] pub(crate) Id20);

/// Predefined conflict resolutions.
pub mod resolver {
    use std::collections::BTreeSet;

    use super::Id20;
    use super::MetaLog;
    use super::SerId20;
    use crate::Result;

    /// Simple merge strategy: Only reject conflicted changes.
    ///
    /// ```plain,ignore
    ///     ancestor this other result
    ///     A        A    A     A
    ///     A        A    B     B
    ///     A        B    A     B
    ///     A        B    B     B
    ///     A        B    C     (Error)
    /// ```
    pub fn simple(this: &mut MetaLog, other: &MetaLog, ancestor: &MetaLog) -> Result<()> {
        let mut conflicts = BTreeSet::new();
        let mut resolved: Vec<(String, Option<Id20>)> = Vec::new();
        for key in other.keys().iter().chain(this.keys().iter()) {
            let ancestor_id = ancestor.root.map.get(&key.to_string()).map(|t| t.0);
            let other_id = other.root.map.get(&key.to_string()).map(|t| t.0);
            let this_id = this.root.map.get(&key.to_string()).map(|t| t.0);
            match (
                ancestor_id == this_id,
                ancestor_id == other_id,
                this_id == other_id,
            ) {
                (false, false, false) => {
                    conflicts.insert(key.to_string());
                }
                (true, false, _) => {
                    resolved.push((key.to_string(), other_id));
                }
                _ => {}
            }
        }
        if !conflicts.is_empty() {
            let message = format!(
                "conflicted changes:\n{}",
                conflicts.into_iter().collect::<Vec<_>>().join("\n")
            );
            return Err(this.error(message));
        }
        for (key, opt_id) in resolved {
            match opt_id {
                Some(id) => this.root.map.insert(key, SerId20(id)),
                None => this.root.map.remove(&key),
            };
        }
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use std::fs;
    use std::io::Seek;
    use std::io::SeekFrom;
    use std::io::Write;

    use indexedlog::DefaultOpenOptions;
    use quickcheck::quickcheck;
    use rand_chacha::ChaChaRng;
    use rand_core::RngCore;
    use rand_core::SeedableRng;
    use tempfile::TempDir;

    use super::*;
    use crate::ENV_LOCK;

    #[test]
    fn test_root_id() {
        let dir = TempDir::new().unwrap();
        let mut metalog = MetaLog::open(&dir, None).unwrap();
        metalog.set("foo", b"bar").unwrap();
        assert_eq!(MetaLog::list_roots(&dir).unwrap().len(), 1);

        metalog.commit(commit_opt("commit 1", 11)).unwrap();
        assert_eq!(MetaLog::list_roots(&dir).unwrap().len(), 2);

        // no-op change
        metalog.set("foo", b"bar3").unwrap();
        metalog.set("foo", b"bar").unwrap();
        metalog.commit(commit_opt("commit 1-noop", 111)).unwrap();

        metalog.set("foo", b"bar2").unwrap();
        metalog.commit(commit_opt("commit 2", 22)).unwrap();
        let root_ids = MetaLog::list_roots(&dir).unwrap();
        assert_eq!(root_ids.len(), 3);

        let metalog = MetaLog::open(&dir, Some(root_ids[0])).unwrap();
        assert!(metalog.keys().is_empty());
        assert_eq!(metalog.message(), "");
        assert_eq!(metalog.timestamp(), 0);

        let metalog = MetaLog::open(&dir, Some(root_ids[1])).unwrap();
        assert_eq!(metalog.get("foo").unwrap().unwrap(), b"bar");
        assert_eq!(metalog.message(), "commit 1");
        assert_eq!(metalog.timestamp(), 11);

        let metalog = MetaLog::open(&dir, Some(root_ids[2])).unwrap();
        assert_eq!(metalog.get("foo").unwrap().unwrap(), b"bar2");
        assert_eq!(metalog.message(), "commit 2");
        assert_eq!(metalog.timestamp(), 22);

        let metalog = metalog.checkout(root_ids[1]).unwrap();
        assert_eq!(metalog.get("foo").unwrap().unwrap(), b"bar");
        assert_eq!(metalog.message(), "commit 1");
        assert_eq!(metalog.timestamp(), 11);
    }

    #[test]
    fn test_open_from_env() {
        let _guard = ENV_LOCK.lock();
        let dir = TempDir::new().unwrap();
        let mut orig_metalog = MetaLog::open(&dir, None).unwrap();
        orig_metalog.set("a", b"1").unwrap();
        orig_metalog.commit(commit_opt("first_commit", 11)).unwrap();
        orig_metalog.set("b", b"2").unwrap();
        orig_metalog
            .commit(commit_opt("second_commit", 22))
            .unwrap();
        orig_metalog.set("c", b"3").unwrap();
        orig_metalog.commit(commit_opt("third_commit", 33)).unwrap();
        let root_ids = MetaLog::list_roots(&dir).unwrap();

        std::env::set_var("HGFORCEMETALOGROOT", hex::encode(root_ids[2]));
        let metalog = MetaLog::open_from_env(dir.as_ref()).unwrap();
        assert_eq!(metalog.message(), "second_commit");
        assert_eq!(metalog.get("a").unwrap().unwrap(), b"1");

        std::env::remove_var("HGFORCEMETALOGROOT");
        let metalog = MetaLog::open_from_env(dir.as_ref()).unwrap();
        assert_eq!(metalog.message(), "third_commit");
    }

    /// Populate metalog for conflict testing.
    /// Return (ancestor, this, other).
    /// ancestor and other are committed, this has pending changes in memory.
    ///
    /// Content of metalog is decided by the name. A name contains 3
    /// characters for (ancestor, this, other). Each character could be:
    /// 0: not present; other: content with single char.
    fn populate_for_conflict_test(
        metalog: &mut MetaLog,
        names: &[&str],
    ) -> (MetaLog, MetaLog, MetaLog) {
        fn populate(metalog: &mut MetaLog, names: &[&str], index: usize) {
            for name in names {
                let ch = name.chars().nth(index).unwrap();
                if ch == '0' {
                    metalog.remove(name).unwrap();
                } else {
                    metalog.set(name, &[ch as u8]).unwrap();
                }
            }
        }

        // Create ancestor
        populate(metalog, names, 0);
        let ancestor_id = metalog.commit(commit_opt("ancestor", 0)).unwrap();

        // Prepare "this" - uncommitted
        let mut this = metalog.checkout(ancestor_id).unwrap();
        populate(&mut this, names, 1);

        // Prepare "other" - committed
        let mut other = metalog.checkout(ancestor_id).unwrap();
        populate(&mut other, names, 2);
        other.commit(commit_opt("other", 1)).unwrap();

        let ancestor = metalog.checkout(ancestor_id).unwrap();
        (ancestor, this, other)
    }

    #[test]
    fn test_default_resolver_conflicts() {
        let dir = TempDir::new().unwrap();
        let mut metalog = MetaLog::open(&dir, None).unwrap();
        let names = [
            "001", "010", "011", "012", "100", "101", "102", "110", "111", "112", "120", "121",
            "122", "123",
        ];
        let mut this = populate_for_conflict_test(&mut metalog, &names).1;
        let err = this
            .commit(commit_opt("this", 2))
            .unwrap_err()
            .to_string()
            .replace(&format!("{:?}", dir.path()), "<path>");
        assert_eq!(err, "<path>: conflicted changes:\n012\n102\n120\n123");
    }

    #[test]
    fn test_default_resolver_no_conflict() {
        let dir = TempDir::new().unwrap();
        let mut metalog = MetaLog::open(&dir, None).unwrap();
        // Similar to test_default_resolver_conflicts but without the conflicted cases.
        let names = [
            "001", "010", "011", "100", "101", "110", "111", "112", "121", "122",
        ];
        let mut this = populate_for_conflict_test(&mut metalog, &names).1;
        this.commit(commit_opt("this", 2)).unwrap();
        // Check resolved content.
        for name in names {
            let chars: Vec<char> = name.chars().collect();
            let expected: char = if chars[0] == chars[1] {
                chars[2]
            } else {
                chars[1]
            };
            if expected == '0' {
                assert!(this.get(name).unwrap().is_none());
            } else {
                assert_eq!(this.get(name).unwrap().unwrap(), &[expected as u8]);
            }
        }
    }

    #[test]
    fn test_custom_resolver() {
        let dir = TempDir::new().unwrap();
        let mut metalog = MetaLog::open(&dir, None).unwrap();
        metalog.set("a", b"0").unwrap();
        metalog.commit(commit_opt("commit 0", 0)).unwrap();

        let mut metalog1 = MetaLog::open(&dir, None).unwrap();
        let mut metalog2 = MetaLog::open(&dir, None).unwrap();
        metalog1.set("a", b"1").unwrap();
        metalog2.set("a", b"2").unwrap();
        metalog1.commit(commit_opt("commit 1", 1)).unwrap();

        let mut opts = commit_opt("commit 2", 2);
        opts.resolver = Some(Box::new(
            |this: &mut MetaLog, other: &MetaLog, _ancestor: &MetaLog| -> Result<()> {
                // Concatenate both sides.
                let mut v1 = this.get("a").unwrap().unwrap().as_ref().to_vec();
                let mut v2 = other.get("a").unwrap().unwrap().as_ref().to_vec();
                v1.append(&mut v2);
                this.set("a", &v1).unwrap();
                // Also try to write an extra key.
                this.set("c", b"c").unwrap();
                Ok(())
            },
        ));
        metalog2.commit(opts).unwrap();

        // Check the merged content.
        let metalog3 = MetaLog::open(&dir, None).unwrap();
        assert_eq!(metalog3.message(), "commit 2");
        assert_eq!(metalog3.get("a").unwrap().unwrap(), b"21");
        assert_eq!(metalog3.get("c").unwrap().unwrap(), b"c");
    }

    #[test]
    fn test_compaction() {
        let dir = TempDir::new().unwrap();
        // ensure current "pointer" resolution works if pointer is encoded as symlink target. (Flip
        // to symlinks here because the symlink encoding increases total size of store, affecting
        // the size assertions.)
        #[cfg(unix)]
        {
            indexedlog::config::SYMLINK_ATOMIC_WRITE
                .store(true, std::sync::atomic::Ordering::SeqCst);
        }
        let mut metalog = MetaLog::open(&dir, None).unwrap();
        fn get_store_size(path: &Path) -> u64 {
            fs::read_dir(path)
                .unwrap()
                .map(|res| res.unwrap().metadata().unwrap().len())
                .sum()
        }
        metalog.set("00", b"ab").unwrap();
        metalog.set("10", b"cd").unwrap();
        metalog.set("01", b"ef").unwrap();
        metalog.set("11a", b"gh").unwrap();
        metalog.set("11b", b"ij").unwrap();
        metalog.commit(commit_opt("commit 0", 0)).unwrap();
        let metalog_stale = MetaLog::open(&dir, None).unwrap();
        metalog.set("00", b"abc").unwrap();
        metalog.set("10", b"001").unwrap();
        metalog.remove("01").unwrap();
        metalog.commit(commit_opt("commit 1", 1)).unwrap();
        let pre_compact_root_size = get_store_size(&dir.path().join("roots"));
        let pre_compact_blob_size = get_store_size(&dir.path().join("blobs"));
        assert_eq!(MetaLog::list_roots(&dir).unwrap().len(), 3);
        assert_eq!(metalog.keys().len(), 4);

        MetaLog::compact(&dir).unwrap();
        #[cfg(unix)]
        {
            indexedlog::config::SYMLINK_ATOMIC_WRITE
                .store(false, std::sync::atomic::Ordering::SeqCst);
        }

        let mut metalog2 = MetaLog::open(&dir, None).unwrap();
        assert_eq!(metalog2.message(), metalog.message());
        assert_eq!(metalog2.timestamp(), metalog.timestamp());
        assert_eq!(metalog2.compaction_epoch, Some(1u64));
        let post_compact_root_size = get_store_size(&dir.path().join("1").join("roots"));
        let post_compact_blob_size = get_store_size(&dir.path().join("1").join("blobs"));

        assert!(
            post_compact_root_size < pre_compact_root_size,
            "roots size should be smaller after compaction, before: {}, after: {}",
            pre_compact_root_size,
            post_compact_root_size
        );
        assert!(
            post_compact_blob_size < pre_compact_blob_size,
            "blobs size should be smaller after compaction"
        );
        assert_eq!(metalog2.get("00").unwrap().unwrap(), b"abc");
        assert_eq!(metalog2.get("10").unwrap().unwrap(), b"001");
        assert_eq!(metalog2.get("01").unwrap(), None);
        assert_eq!(metalog2.get("11a").unwrap().unwrap(), b"gh");
        assert_eq!(metalog2.get("11b").unwrap().unwrap(), b"ij");
        assert_eq!(MetaLog::list_roots(&dir).unwrap().len(), 2);

        assert_eq!(
            MetaLog::open(&dir, None).unwrap().orig_root_id,
            metalog.orig_root_id
        );

        metalog.set("00", b"xyz").unwrap();
        metalog
            .commit(commit_opt("commit 2", 2))
            .expect_err("commit against old compaction epoch should fail");

        assert_eq!(
            metalog_stale
                .get("00")
                .expect("reads against stale metalog handles should succeed")
                .unwrap(),
            b"ab"
        );

        #[cfg(unix)]
        {
            assert!(
                !&dir.path().join("roots").exists(),
                "compaction should have removed old roots"
            );
            assert!(
                !&dir.path().join("blobs").exists(),
                "compaction should have removed old blobs"
            );
        }

        metalog2.set("00", b"qrs").unwrap();
        metalog2.commit(commit_opt("compact commit 1", 2)).unwrap();
        MetaLog::compact(&dir).unwrap();

        let mut metalog3 = MetaLog::open(&dir, None).unwrap();
        assert_eq!(metalog3.compaction_epoch, Some(2u64));
        assert_eq!(metalog3.get("00").unwrap().unwrap(), b"qrs");
        assert_eq!(metalog3.get("10").unwrap().unwrap(), b"001");
        assert_eq!(metalog3.get("01").unwrap(), None);
        assert_eq!(metalog3.get("11a").unwrap().unwrap(), b"gh");
        assert_eq!(metalog3.get("11b").unwrap().unwrap(), b"ij");

        #[cfg(unix)]
        {
            assert!(
                !&dir.path().join("1").exists(),
                "compaction should have removed old store path"
            );
        }
        drop(metalog);
        drop(metalog_stale);
        drop(metalog2);
        metalog3.set("00", b"tuv").unwrap();
        // commit here should result in old metalog stores being deleted on all platforms
        metalog3.commit(commit_opt("compact commit 2", 3)).unwrap();

        let deleted_paths = vec!["blobs", "roots", "1"];
        for path in &deleted_paths {
            assert!(!&dir.path().join(path).exists());
        }

        // verify that delete on commit works
        fs::create_dir(&dir.path().join("roots")).unwrap();
        fs::create_dir(&dir.path().join("1")).unwrap();
        fs::create_dir(&dir.path().join("1").join("roots")).unwrap();
        metalog3.set("00", b"xyz").unwrap();
        metalog3.commit(commit_opt("compact commit 3", 4)).unwrap();
        for path in &deleted_paths {
            assert!(!&dir.path().join(path).exists());
        }
        assert!(&dir.path().join("current").exists());
        assert!(&dir.path().join("2").exists());
    }

    quickcheck! {
        fn test_random_round_trips(map: BTreeMap<String, (Vec<u8>, Vec<u8>)>) -> bool {
            test_round_trips(map);
            true
        }
    }

    fn test_round_trips(map: BTreeMap<String, (Vec<u8>, Vec<u8>)>) {
        let dir = TempDir::new().unwrap();
        let mut metalog = MetaLog::open(&dir, None).unwrap();

        for (k, (v1, v2)) in map.iter() {
            metalog.set(k, v1).unwrap();
            assert_eq!(metalog.get(k).unwrap(), Some(Bytes::from(v1.clone())));
            metalog.set(k, v2).unwrap();
            assert_eq!(metalog.get(k).unwrap(), Some(Bytes::from(v2.clone())));
        }
        let root_id1 = metalog.commit(commit_opt("", 0)).unwrap();

        for (k, (_v1, v2)) in map.iter() {
            metalog.remove(k).unwrap();
            assert_eq!(metalog.get(k).unwrap(), None);
            metalog.set(k, v2).unwrap();
            assert_eq!(metalog.get(k).unwrap(), Some(Bytes::from(v2.clone())));
        }
        let root_id2 = metalog.commit(commit_opt("", 0)).unwrap();
        assert_eq!(root_id1, root_id2);

        metalog.commit(commit_opt("", 0)).unwrap();
        let metalog = MetaLog::open(&dir, None).unwrap();
        for (k, (_v1, v2)) in map.iter() {
            assert_eq!(metalog.get(k).unwrap(), Some(Bytes::from(v2.clone())));
        }
        assert_eq!(metalog.keys(), map.keys().collect::<Vec<_>>());
    }

    fn commit_opt(message: &str, timestamp: u64) -> CommitOptions {
        let mut opts = CommitOptions::default();
        opts.message = message;
        opts.timestamp = timestamp;
        opts
    }

    // Repair

    #[test]
    fn test_repair() {
        let dir = TempDir::new().unwrap();
        let repair = || {
            let path = format!("{:?}", &dir.path());
            let path = &path[1..path.len() - 1]; // strip leading and trailing '"'
            format!(
                "\n{}",
                MetaLog::repair(&dir)
                    .unwrap()
                    .lines()
                    // Remove 'Backed up' lines since they have dynamic file names.
                    .filter(|l| !l.contains("Backed up"))
                    .filter(|l| !l.contains("Processing"))
                    .filter(|l| !l.contains("bytes in log"))
                    .filter(|l| !l.contains("Reset log size to"))
                    .collect::<Vec<_>>()
                    .join("\n")
                    .replace(&path, "<path>")
                    // Normalize path difference on Windows.
                    .replace("\\\\", "/")
                    .trim_end()
            )
        };
        let create_log = || {
            let mut noise = [0u8; 4000];
            ChaChaRng::seed_from_u64(0).fill_bytes(&mut noise);

            let mut metalog = MetaLog::open(&dir, None).unwrap();
            metalog.set("a", &noise[..]).unwrap();
            metalog.set("a", b"1").unwrap();
            metalog.commit(commit_opt("commit 1", 1)).unwrap();
            metalog.set("b", b"2").unwrap();
            metalog.set("c", &noise[..]).unwrap();
            metalog.commit(commit_opt("commit 2", 2)).unwrap();
            metalog.set("d", &noise[..]).unwrap();
            metalog.commit(commit_opt("commit 3", 3)).unwrap();
            metalog.set("e", &noise[..]).unwrap();
            metalog.commit(commit_opt("commit 4", 4)).unwrap();
            metalog.set("f", &noise[..]).unwrap();
            metalog.commit(commit_opt("commit 5", 5)).unwrap();
        };
        let corrupt = |name: &str, offset: i64| pwrite(&dir.path().join(name), offset, b"cc");

        // Empty log.
        assert_eq!(
            repair(),
            r#"
Checking blobs at "<path>":
"<path>/blobs" does not exist. Nothing to repair.

Checking roots at "<path>":
"<path>/roots" does not exist. Nothing to repair.

Checking blobs referred by 1 Roots:
All Roots are verified."#
        );

        // Non-empty log.
        create_log();
        assert_eq!(
            repair(),
            r#"
Checking blobs at "<path>":
Index "id" passed integrity check

Checking roots at "<path>":
Index "reverse" passed integrity check

Checking blobs referred by 6 Roots:
All Roots are verified."#
        );

        // Break the last Root ID in "roots", used by "commit 5".
        corrupt("roots/log", -20);
        assert_eq!(
            repair(),
            r#"
Checking blobs at "<path>":
Index "id" passed integrity check

Checking roots at "<path>":
Rebuilt index "reverse"

Checking blobs referred by 5 Roots:
All Roots are verified."#
        );

        // Break the Root structure in "blobs", used by "commit 4" .
        corrupt("blobs/log", -150);
        assert_eq!(
            repair(),
            r#"
Checking blobs at "<path>":
Rebuilt index "id"

Checking roots at "<path>":
Index "reverse" passed integrity check

Checking blobs referred by 5 Roots:
Root 93b756c5e512ebd0dd7c4dfdb17924287869ec33 cannot be read.
Removing 1 bad Root IDs.
Rebuilt Root log with 4 Root IDs."#
        );

        fn reorder_blobs_log(path: &Path) {
            // Break the blob referred by commits. To do that, we need to reorder
            // blobs in "blobs/" so the large "noise" blob is at the end.
            let zpath = path.join("blobs");
            let blobs = {
                let zlog = Zstore::default_open_options().open(&zpath).unwrap();
                let mut blobs: Vec<Vec<u8>> = zlog.iter().map(|e| e.unwrap().to_vec()).collect();
                blobs.sort_unstable_by_key(|b| b.len());
                blobs
            };
            {
                Zstore::default_open_options()
                    .delete_content(&zpath)
                    .unwrap();
                let mut zlog = Zstore::default_open_options().open(&zpath).unwrap();
                for blob in blobs {
                    zlog.append(blob).unwrap();
                }
                zlog.flush().unwrap();
            }
        }
        reorder_blobs_log(&dir.path());

        // Now the last blob is the 4KB "noise" blob. Break it without breaking
        // other blobs.
        corrupt("blobs/log", -1000);
        assert_eq!(
            repair(),
            r#"
Checking blobs at "<path>":
Rebuilt index "id"

Checking roots at "<path>":
Index "reverse" passed integrity check

Checking blobs referred by 5 Roots:
Key "c" referred by Root c4d3e70640748daac548adb39b07818b0dc34e4f (commit 2) cannot be read.
Key "c" referred by Root b0f57751e2ec36db46dc3d38b88d538b40eebdb9 (commit 3) cannot be read.
Removing 2 bad Root IDs.
Rebuilt Root log with 3 Root IDs."#
        );

        MetaLog::compact(&dir).unwrap();
        create_log();
        reorder_blobs_log(&dir.path().join("1"));

        corrupt("1/blobs/log", -1000);
        assert_eq!(
            repair(),
            r#"
Checking blobs at "<path>/1":
Rebuilt index "id"

Checking roots at "<path>/1":
Index "reverse" passed integrity check

Checking blobs referred by 6 Roots:
Key "c" referred by Root c4d3e70640748daac548adb39b07818b0dc34e4f (commit 2) cannot be read.
Key "c" referred by Root b0f57751e2ec36db46dc3d38b88d538b40eebdb9 (commit 3) cannot be read.
Key "c" referred by Root 93b756c5e512ebd0dd7c4dfdb17924287869ec33 (commit 4) cannot be read.
Key "c" referred by Root 46652bcd89caba5046f90f37046266a60a2c1743 (commit 5) cannot be read.
Removing 4 bad Root IDs.
Rebuilt Root log with 2 Root IDs."#
        );
    }

    fn pwrite(path: &Path, offset: i64, data: &[u8]) {
        let mut file = fs::OpenOptions::new()
            .write(true)
            .read(true)
            .open(path)
            .unwrap();
        if offset < 0 {
            file.seek(SeekFrom::End(offset)).unwrap();
        } else {
            file.seek(SeekFrom::Start(offset as u64)).unwrap();
        }
        file.write_all(data).unwrap();
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This software may be used and distributed according to the terms of the
 * GNU General Public License version 2.
 */

use std::collections::HashSet;
use std::env;
use std::sync::Weak;
use std::thread;
use std::time::Duration;
use std::time::Instant;

use metrics::Counter;
use metrics::Registry;
use once_cell::sync::Lazy;
use progress_model::IoSample;
use progress_model::IoTimeSeries;
use progress_model::Registry as ProgressRegistry;
use progress_model::TimeSeriesMode;
use tracing::error;

/// Initializes metrics reporting while guard is not dropped.
/// Configuration is read from env variable EDENSCM_METRICS.
/// This variable holds coma separated list of reporting parameters in form Filter:Reporter.
///
/// Filter is the name of the metric, either full name, or a prefix (crate name without '.').
/// Reporter is optional, by default progress reporter is used - it renders progress bar with the counter.
///
/// Available renderers:
/// p | progress = render interactively using progress crate.
/// l | log = periodically print available counters. This best works for non-interactive executions like when debugging performance inside EdenFS
pub fn init_from_env(guard: Weak<()>) {
    if let Ok(config) = env::var("EDENSCM_METRICS") {
        init(&config, guard)
    }
}

fn init(config: &str, guard: Weak<()>) {
    let config = parse_config(config);
    if config.is_empty() {
        return;
    }
    thread::Builder::new()
        .name("metrics-report".to_string())
        .spawn(move || worker(config, guard))
        .unwrap();
}

fn worker(mut config: Vec<(MetricsFilter, Renderer)>, guard: Weak<()>) {
    let registry = Registry::global();
    while guard.upgrade().is_some() {
        let metrics = registry.counters();
        for (filter, renderer) in &mut config {
            let filtered = metrics.iter().filter_map(|(name, v)| {
                if filter.matches(name) {
                    Some((*name, *v))
                } else {
                    None
                }
            });
            renderer.render(filtered.collect());
        }
        thread::sleep(Duration::from_secs(1));
    }
}

fn parse_config(config: &str) -> Vec<(MetricsFilter, Renderer)> {
    let mut result = vec![];
    for item in config.split(',') {
        let mut split = item.split(':');
        let filter = split.next().expect("First element in split always exist");
        let render_type = split.next();
        let renderer = match render_type {
            None | Some("p") | Some("progress") => {
                if split.next().is_some() {
                    error!("Progress render does not have parameters, extra parameters ignored");
                }
                Renderer::ProgressBar(Default::default())
            }
            Some("l") | Some("log") => Renderer::Log,
            Some(other) => {
                error!("Invalid metrics renderer: {}", other);
                continue;
            }
        };
        let filter = MetricsFilter::parse(filter);
        result.push((filter, renderer));
    }
    result
}

struct MetricsFilter {
    prefix: String,
    exact: bool, // Either exact(true) or prefix(false)
}

impl MetricsFilter {
    pub fn parse(s: &str) -> MetricsFilter {
        let exact = s.contains('.');
        MetricsFilter {
            prefix: s.to_string(),
            exact,
        }
    }

    pub fn matches(&self, name: &str) -> bool {
        if self.exact {
            self.prefix.as_str() == name
        } else {
            name.starts_with(&self.prefix)
        }
    }
}

enum Renderer {
    ProgressBar(HashSet<&'static str>),
    Log,
}

impl Renderer {
    pub fn render(&mut self, metrics: Vec<(&'static str, &'static Counter)>) {
        match self {
            Self::ProgressBar(inner) => Self::render_progress(inner, metrics),
            Self::Log => Self::render_log(metrics),
        }
    }

    fn render_log(metrics: Vec<(&'static str, &'static Counter)>) {
        use std::fmt::Write;

        const LINE_LENGTH: usize = 120;
        static STARTED: Lazy<Instant> = Lazy::new(Instant::now);

        let started = *STARTED;
        if metrics.is_empty() {
            return;
        }

        let timestamp_ms = started.elapsed().as_millis();
        let mut lines = vec![];
        let mut current_line = String::with_capacity(LINE_LENGTH);
        write!(current_line, "T{:0>8}{: <31}", timestamp_ms, "").ok(); // 40 symbols
        for (name, counter) in metrics {
            if current_line.len() >= LINE_LENGTH {
                lines.push(current_line);
                current_line = String::with_capacity(LINE_LENGTH);
            }
            write!(current_line, "{:.<32}{: <8}", name, counter.value()).ok(); // 40 symbols
        }
        lines.push(current_line);
        println!("{}", lines.join("\n"));
    }

    fn render_progress(
        inner: &mut HashSet<&'static str>,
        metrics: Vec<(&'static str, &'static Counter)>,
    ) {
        let progress_registry = ProgressRegistry::main();
        for (name, counter) in metrics {
            if !inner.insert(name) {
                continue;
            }
            let take_sample = move || {
                // This abuses API little bit by supplying random counter into IoSample, need to fix later
                IoSample::from_io_bytes(counter.value() as _, 0)
            };
            let visible_name = format!("{:.<32}", name);
            let time_series =
                IoTimeSeries::new_with_mode(visible_name, "", TimeSeriesMode::ValueNoUnit);
            let task =
                time_series.async_sampling(take_sample, IoTimeSeries::default_sample_interval());
            async_runtime::spawn(task);

            progress_registry.register_io_time_series(&time_series);
        }
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This software may be used and distributed according to the terms of the
 * GNU General Public License version 2.
 */

use std::collections::HashMap;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
use std::sync::RwLock;

use futures::Future;
use once_cell::sync::Lazy;
use once_cell::sync::OnceCell;

pub struct Counter {
    name: &'static str,
    inner: AtomicUsize,
    registered: OnceCell<()>,
}

impl Counter {
    /// By convention metric name should be crate.metric_name
    /// Metrics without '.' in name are not allowed (cause compilation error)
    pub const fn new(name: &'static str) -> Self {
        // Unfortunately we can't check name this here because of const restriction
        let inner = AtomicUsize::new(0);
        let registered = OnceCell::new();
        Self {
            name,
            inner,
            registered,
        }
    }

    pub fn increment(&'static self) {
        self.add(1);
    }

    pub fn add(&'static self, val: usize) {
        self.inner().fetch_add(val, Ordering::Relaxed);
    }

    pub fn sub(&'static self, val: usize) {
        self.inner().fetch_sub(val, Ordering::Relaxed);
    }

    pub fn value(&'static self) -> usize {
        self.inner().load(Ordering::Relaxed)
    }

    /// Increment counter by v and decrement it back by v when returned guard is dropped
    pub fn entrance_guard(&'static self, v: usize) -> EntranceGuard {
        self.add(v);
        EntranceGuard(self, v)
    }

    fn inner(&'static self) -> &AtomicUsize {
        self.registered
            .get_or_init(|| Registry::global().register_counter(self));
        &self.inner
    }
}

pub struct EntranceGuard(&'static Counter, usize);

impl Drop for EntranceGuard {
    fn drop(&mut self) {
        self.0.sub(self.1);
    }
}

pub async fn wrap_future_keep_guards<F: Future>(
    future: F,
    _guards: Vec<EntranceGuard>,
) -> F::Output {
    future.await
}

#[derive(Default)]
pub struct Registry {
    counters: RwLock<HashMap<&'static str, &'static Counter>>,
}

impl Registry {
    pub fn global() -> &'static Self {
        static REGISTRY: Lazy<Registry> = Lazy::new(Registry::default);
        &*REGISTRY
    }

    pub fn register_counter(&self, counter: &'static Counter) {
        if self
            .counters
            .write()
            .unwrap()
            .insert(counter.name, counter)
            .is_some()
        {
            panic!("Counter {} is duplicated", counter.name)
        }
    }

    pub fn counters(&self) -> HashMap<&'static str, &'static Counter> {
        self.counters.read().unwrap().clone()
    }
}

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

    #[test]
    fn counters_test() {
        static COUNTER1: Counter = Counter::new("COUNTER1");
        static COUNTER2: Counter = Counter::new("COUNTER2");
        COUNTER1.increment();
        COUNTER2.add(5);
        let counters = Registry::global().counters();
        assert_eq!(1, counters.get("COUNTER1").unwrap().value());
        assert_eq!(5, counters.get("COUNTER2").unwrap().value());
    }

    #[test]
    fn entrance_test() {
        static COUNTER3: Counter = Counter::new("COUNTER3");
        let guard1 = COUNTER3.entrance_guard(1);
        let counters = Registry::global().counters();
        assert_eq!(1, counters.get("COUNTER3").unwrap().value());
        std::mem::drop(guard1);
        let counters = Registry::global().counters();
        assert_eq!(0, counters.get("COUNTER3").unwrap().value());
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This software may be used and distributed according to the terms of the
 * GNU General Public License version 2.
 */

use thiserror::Error;

#[derive(Error, Debug)]
#[error("{0}")]
pub struct DeprecatedFeature(pub String);


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This software may be used and distributed according to the terms of the
 * GNU General Public License version 2.
 */

use configmodel::Config;
use configmodel::ConfigExt;

use crate::errors::DeprecatedFeature;

pub fn deprecate(
    config: &dyn Config,
    name: &str,
    error_str: String,
) -> Result<(), DeprecatedFeature> {
    if config.get_or("deprecate", name, || false).unwrap_or(false) {
        Err(DeprecatedFeature(error_str))
    } else {
        Ok(())
    }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This software may be used and distributed according to the terms of the
 * GNU General Public License version 2.
 */

//! Utilities for doing migrations

pub mod errors;
pub mod feature;


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

use std::str;

use byteorder::NetworkEndian;
use byteorder::ReadBytesExt;
use serde::de;
use serde::de::Deserialize;
use serde::de::DeserializeSeed;
use serde::de::EnumAccess;
use serde::de::IntoDeserializer;
use serde::de::SeqAccess;
use serde::de::VariantAccess;
use serde::de::Visitor;
use vlqencoding::VLQDecode;

use crate::Error;
use crate::Result;

pub struct Deserializer<'de> {
    bytes: &'de [u8],
}

impl<'de> Deserializer<'de> {
    pub fn new(bytes: &'de [u8]) -> Self {
        Deserializer { bytes }
    }

    #[inline]
    fn read_slice(&mut self) -> Result<&'de [u8]> {
        let len = Deserialize::deserialize(&mut *self)?;
        let (slice, rest) = self.bytes.split_at(len);
        self.bytes = rest;
        Ok(slice)
    }

    #[inline]
    fn read_str(&mut self) -> Result<&'de str> {
        str::from_utf8(self.read_slice()?).map_err(Into::into)
    }
}

macro_rules! impl_nums {
    ($ty:ty, $dser_method:ident, $visitor_method:ident, $($reader_method:tt)*) => {
        #[inline]
        fn $dser_method<V>(self, visitor: V) -> Result<V::Value>
            where V: Visitor<'de>
        {
            let value = self.bytes.$($reader_method)*()?;
            visitor.$visitor_method(value)
        }
    };
}

impl<'de, 'a> serde::Deserializer<'de> for &'a mut Deserializer<'de> {
    type Error = Error;

    fn deserialize_any<V>(self, _visitor: V) -> Result<V::Value>
    where
        V: Visitor<'de>,
    {
        Err(Error::new("`deserialize_any` is not supported"))
    }

    #[inline]
    fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value>
    where
        V: Visitor<'de>,
    {
        match self.bytes.read_u8()? {
            1 => visitor.visit_bool(true),
            0 => visitor.visit_bool(false),
            _ => Err(Error::new("invalid boolean")),
        }
    }

    impl_nums!(u8, deserialize_u8, visit_u8, read_u8);
    impl_nums!(u16, deserialize_u16, visit_u16, read_vlq);
    impl_nums!(u32, deserialize_u32, visit_u32, read_vlq);
    impl_nums!(u64, deserialize_u64, visit_u64, read_vlq);
    impl_nums!(i8, deserialize_i8, visit_i8, read_i8);
    impl_nums!(i16, deserialize_i16, visit_i16, read_vlq);
    impl_nums!(i32, deserialize_i32, visit_i32, read_vlq);
    impl_nums!(i64, deserialize_i64, visit_i64, read_vlq);
    impl_nums!(f32, deserialize_f32, visit_f32, read_f32::<NetworkEndian>);
    impl_nums!(f64, deserialize_f64, visit_f64, read_f64::<NetworkEndian>);

    #[inline]
    fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value>
    where
        V: Visitor<'de>,
    {
        visitor.visit_unit()
    }

    #[inline]
    fn deserialize_char<V>(self, visitor: V) -> Result<V::Value>
    where
        V: Visitor<'de>,
    {
        let width = utf8_char_width(self.bytes[0]);
        if width == 1 {
            return visitor.visit_char(self.bytes[0] as char);
        }
        if width == 0 {
            return Err(Error::new("invalid char"));
        }
        let res = match str::from_utf8(&self.bytes[..width]) {
            Ok(s) => s.chars().next().unwrap(),
            Err(err) => {
                return Err(err.into());
            }
        };
        self.bytes = &self.bytes[width..];
        visitor.visit_char(res)
    }

    #[inline]
    fn deserialize_str<V>(self, visitor: V) -> Result<V::Value>
    where
        V: Visitor<'de>,
    {
        visitor.visit_borrowed_str(self.read_str()?)
    }

    #[inline]
    fn deserialize_string<V>(self, visitor: V) -> Result<V::Value>
    where
        V: Visitor<'de>,
    {
        visitor.visit_borrowed_str(self.read_str()?)
    }

    #[inline]
    fn deserialize_bytes<V>(self, visitor: V) -> Result<V::Value>
    where
        V: Visitor<'de>,
    {
        visitor.visit_borrowed_bytes(self.read_slice()?)
    }

    #[inline]
    fn deserialize_byte_buf<V>(self, visitor: V) -> Result<V::Value>
    where
        V: Visitor<'de>,
    {
        visitor.visit_borrowed_bytes(self.read_slice()?)
    }

    #[inline]
    fn deserialize_enum<V>(
        self,
        _enum: &'static str,
        _variants: &'static [&'static str],
        visitor: V,
    ) -> Result<V::Value>
    where
        V: Visitor<'de>,
    {
        visitor.visit_enum(self)
    }

    #[inline]
    fn deserialize_tuple<V>(self, _len: usize, visitor: V) -> Result<V::Value>
    where
        V: Visitor<'de>,
    {
        visitor.visit_seq(self)
    }

    #[inline]
    fn deserialize_option<V>(self, visitor: V) -> Result<V::Value>
    where
        V: Visitor<'de>,
    {
        match self.bytes.read_u8()? {
            0 => visitor.visit_none(),
            1 => visitor.visit_some(self),
            _ => Err(Error::new("invalid Option")),
        }
    }

    #[inline]
    fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value>
    where
        V: Visitor<'de>,
    {
        struct SeqAccess<'a, 'de: 'a> {
            deserializer: &'a mut Deserializer<'de>,
            remaining: usize,
        }

        impl<'de, 'a> de::SeqAccess<'de> for SeqAccess<'a, 'de> {
            type Error = Error;

            #[inline]
            fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>>
            where
                T: DeserializeSeed<'de>,
            {
                if self.remaining > 0 {
                    self.remaining -= 1;
                    seed.deserialize(&mut *self.deserializer).map(Some)
                } else {
                    Ok(None)
                }
            }
        }

        let len = Deserialize::deserialize(&mut *self)?;

        visitor.visit_seq(SeqAccess {
            deserializer: self,
            remaining: len,
        })
    }

    #[inline]
    fn deserialize_map<V>(self, visitor: V) -> Result<V::Value>
    where
        V: Visitor<'de>,
    {
        struct MapAccess<'a, 'de: 'a> {
            deserializer: &'a mut Deserializer<'de>,
            remaining: usize,
        }

        impl<'de, 'a> de::MapAccess<'de> for MapAccess<'a, 'de> {
            type Error = Error;

            #[inline]
            fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>>
            where
                K: DeserializeSeed<'de>,
            {
                if self.remaining > 0 {
                    self.remaining -= 1;
                    seed.deserialize(&mut *self.deserializer).map(Some)
                } else {
                    Ok(None)
                }
            }

            #[inline]
            fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value>
            where
                V: DeserializeSeed<'de>,
            {
                seed.deserialize(&mut *self.deserializer)
            }
        }

        let len = Deserialize::deserialize(&mut *self)?;

        visitor.visit_map(MapAccess {
            deserializer: self,
            remaining: len,
        })
    }

    #[inline]
    fn deserialize_struct<V>(
        self,
        _name: &str,
        _fields: &'static [&'static str],
        visitor: V,
    ) -> Result<V::Value>
    where
        V: Visitor<'de>,
    {
        visitor.visit_seq(self)
    }

    fn deserialize_identifier<V>(self, _visitor: V) -> Result<V::Value>
    where
        V: Visitor<'de>,
    {
        Err(Error::new("`deserialize_identifier` is not supported"))
    }

    #[inline]
    fn deserialize_newtype_struct<V>(self, _name: &str, visitor: V) -> Result<V::Value>
    where
        V: Visitor<'de>,
    {
        visitor.visit_newtype_struct(self)
    }

    #[inline]
    fn deserialize_unit_struct<V>(self, _name: &'static str, visitor: V) -> Result<V::Value>
    where
        V: Visitor<'de>,
    {
        visitor.visit_unit()
    }

    #[inline]
    fn deserialize_tuple_struct<V>(
        self,
        _name: &'static str,
        _len: usize,
        visitor: V,
    ) -> Result<V::Value>
    where
        V: Visitor<'de>,
    {
        visitor.visit_seq(self)
    }

    fn deserialize_ignored_any<V>(self, _visitor: V) -> Result<V::Value>
    where
        V: Visitor<'de>,
    {
        Err(Error::new("`deserialize_ignored_any` is not supported"))
    }
}

// For tuples, structs, tuple structs, and fixed size seqs.
impl<'de> SeqAccess<'de> for Deserializer<'de> {
    type Error = Error;

    #[inline]
    fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>>
    where
        T: DeserializeSeed<'de>,
    {
        seed.deserialize(self).map(Some)
    }
}

impl<'de, 'a> EnumAccess<'de> for &'a mut Deserializer<'de> {
    type Error = Error;
    type Variant = Self;

    #[inline]
    fn variant_seed<V>(self, seed: V) -> Result<(V::Value, Self)>
    where
        V: DeserializeSeed<'de>,
    {
        let index = Deserialize::deserialize(&mut *self)?;
        let deserializer = <u32 as IntoDeserializer<Error>>::into_deserializer(index);
        let value = seed.deserialize(deserializer)?;
        Ok((value, self))
    }
}

impl<'de, 'a> VariantAccess<'de> for &'a mut Deserializer<'de> {
    type Error = Error;

    #[inline]
    fn unit_variant(self) -> Result<()> {
        Ok(())
    }

    #[inline]
    fn newtype_variant_seed<T>(self, seed: T) -> Result<T::Value>
    where
        T: DeserializeSeed<'de>,
    {
        seed.deserialize(self)
    }

    #[inline]
    fn tuple_variant<V>(self, _len: usize, visitor: V) -> Result<V::Value>
    where
        V: Visitor<'de>,
    {
        visitor.visit_seq(self)
    }

    #[inline]
    fn struct_variant<V>(self, _fields: &'static [&'static str], visitor: V) -> Result<V::Value>
    where
        V: Visitor<'de>,
    {
        visitor.visit_seq(self)
    }
}

#[rustfmt::skip]
static UTF8_CHAR_WIDTH: [u8; 256] = [
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x1F
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x3F
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x5F
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x7F
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x9F
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xBF
    0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xDF
    3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 0xEF
    4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xFF
];

#[inline]
fn utf8_char_width(b: u8) -> usize {
    UTF8_CHAR_WIDTH[b as usize] as usize
}


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

use std::error;
use std::fmt;
use std::fmt::Display;
use std::io;
use std::str;
use std::string;

use serde::de;
use serde::ser;

#[derive(Debug)]
pub struct Error {
    msg: String,
}

pub type Result<T> = std::result::Result<T, Error>;

impl Error {
    pub fn new<T: Display>(msg: T) -> Self {
        Error {
            msg: msg.to_string(),
        }
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        &self.msg
    }
}

impl Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str(&self.msg)
    }
}

impl ser::Error for Error {
    fn custom<T: Display>(msg: T) -> Self {
        Error::new(msg)
    }
}

impl de::Error for Error {
    fn custom<T: Display>(msg: T) -> Self {
        Error::new(msg)
    }
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Self {
        Error::new(err)
    }
}

impl From<str::Utf8Error> for Error {
    fn from(err: str::Utf8Error) -> Self {
        Error::new(err)
    }
}

impl From<string::FromUtf8Error> for Error {
    fn from(err: string::FromUtf8Error) -> Self {
        Error::new(err)
    }
}


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

mod de;
mod error;
mod ser;

#[cfg(test)]
mod tests;

use std::io;

use serde::Deserialize;
use serde::Serialize;

use self::de::Deserializer;
pub use self::error::Error;
pub use self::error::Result;
use self::ser::Serializer;

pub fn serialize<T>(value: &T) -> Result<Vec<u8>>
where
    T: Serialize,
{
    let mut out = Vec::new();
    serialize_into(&mut out, value)?;
    Ok(out)
}

pub fn serialize_into<W, T: ?Sized>(writer: W, value: &T) -> Result<()>
where
    W: io::Write,
    T: Serialize,
{
    let mut ser = Serializer::new(writer);
    Serialize::serialize(value, &mut ser)
}

pub fn deserialize<'de, T>(bytes: &'de [u8]) -> Result<T>
where
    T: Deserialize<'de>,
{
    let mut de = Deserializer::new(bytes);
    Deserialize::deserialize(&mut de)
}


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

use std::io::Write;

use byteorder::NetworkEndian;
use byteorder::WriteBytesExt;
use serde::ser::SerializeMap;
use serde::ser::SerializeSeq;
use serde::ser::SerializeStruct;
use serde::ser::SerializeStructVariant;
use serde::ser::SerializeTuple;
use serde::ser::SerializeTupleStruct;
use serde::ser::SerializeTupleVariant;
use vlqencoding::VLQEncode;

use crate::Error;
use crate::Result;

pub struct Serializer<W> {
    writer: W,
}

impl<W> Serializer<W>
where
    W: Write,
{
    pub fn new(w: W) -> Self {
        Serializer { writer: w }
    }
}

impl<'a, W> serde::Serializer for &'a mut Serializer<W>
where
    W: Write,
{
    type Ok = ();
    type Error = Error;

    type SerializeSeq = Self;
    type SerializeTuple = Self;
    type SerializeTupleStruct = Self;
    type SerializeTupleVariant = Self;
    type SerializeMap = Self;
    type SerializeStruct = Self;
    type SerializeStructVariant = Self;

    #[inline]
    fn serialize_unit(self) -> Result<()> {
        Ok(())
    }

    #[inline]
    fn serialize_unit_struct(self, _: &'static str) -> Result<()> {
        Ok(())
    }

    #[inline]
    fn serialize_bool(self, v: bool) -> Result<()> {
        self.writer.write_u8(v as u8).map_err(From::from)
    }

    #[inline]
    fn serialize_u8(self, v: u8) -> Result<()> {
        self.writer.write_u8(v).map_err(From::from)
    }

    #[inline]
    fn serialize_u16(self, v: u16) -> Result<()> {
        self.writer.write_vlq(v).map_err(From::from)
    }

    #[inline]
    fn serialize_u32(self, v: u32) -> Result<()> {
        self.writer.write_vlq(v).map_err(From::from)
    }

    #[inline]
    fn serialize_u64(self, v: u64) -> Result<()> {
        self.writer.write_vlq(v).map_err(From::from)
    }

    #[inline]
    fn serialize_i8(self, v: i8) -> Result<()> {
        self.writer.write_i8(v).map_err(From::from)
    }

    #[inline]
    fn serialize_i16(self, v: i16) -> Result<()> {
        self.writer.write_vlq(v).map_err(From::from)
    }

    #[inline]
    fn serialize_i32(self, v: i32) -> Result<()> {
        self.writer.write_vlq(v).map_err(From::from)
    }

    #[inline]
    fn serialize_i64(self, v: i64) -> Result<()> {
        self.writer.write_vlq(v).map_err(From::from)
    }

    #[inline]
    fn serialize_f32(self, v: f32) -> Result<()> {
        self.writer
            .write_f32::<NetworkEndian>(v)
            .map_err(From::from)
    }

    #[inline]
    fn serialize_f64(self, v: f64) -> Result<()> {
        self.writer
            .write_f64::<NetworkEndian>(v)
            .map_err(From::from)
    }

    #[inline]
    fn serialize_str(self, v: &str) -> Result<()> {
        self.serialize_u64(v.len() as u64)?;
        self.writer.write_all(v.as_bytes()).map_err(From::from)
    }

    #[inline]
    fn serialize_char(self, c: char) -> Result<()> {
        self.writer
            .write_all(encode_utf8(c).as_slice())
            .map_err(From::from)
    }

    #[inline]
    fn serialize_bytes(self, v: &[u8]) -> Result<()> {
        self.serialize_u64(v.len() as u64)?;
        self.writer.write_all(v).map_err(From::from)
    }

    #[inline]
    fn serialize_none(self) -> Result<()> {
        self.writer.write_u8(0).map_err(From::from)
    }

    #[inline]
    fn serialize_some<T: ?Sized>(self, v: &T) -> Result<()>
    where
        T: serde::Serialize,
    {
        self.writer.write_u8(1)?;
        v.serialize(self)
    }

    #[inline]
    fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq> {
        let len = len.expect("do not know how to serialize a sequence with no length");
        self.serialize_u64(len as u64)?;
        Ok(self)
    }

    #[inline]
    fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple> {
        Ok(self)
    }

    #[inline]
    fn serialize_tuple_struct(
        self,
        _name: &'static str,
        _len: usize,
    ) -> Result<Self::SerializeTupleStruct> {
        Ok(self)
    }

    #[inline]
    fn serialize_tuple_variant(
        self,
        _name: &'static str,
        variant_index: u32,
        _variant: &'static str,
        _len: usize,
    ) -> Result<Self::SerializeTupleVariant> {
        self.serialize_u32(variant_index)?;
        Ok(self)
    }

    #[inline]
    fn serialize_map(self, len: Option<usize>) -> Result<Self::SerializeMap> {
        let len = len.expect("do not know how to serialize a map with no length");
        self.serialize_u64(len as u64)?;
        Ok(self)
    }

    #[inline]
    fn serialize_struct(self, _name: &'static str, _len: usize) -> Result<Self::SerializeStruct> {
        Ok(self)
    }

    #[inline]
    fn serialize_struct_variant(
        self,
        _name: &'static str,
        variant_index: u32,
        _variant: &'static str,
        _len: usize,
    ) -> Result<Self::SerializeStructVariant> {
        self.serialize_u32(variant_index)?;
        Ok(self)
    }

    #[inline]
    fn serialize_newtype_struct<T: ?Sized>(self, _name: &'static str, value: &T) -> Result<()>
    where
        T: serde::ser::Serialize,
    {
        value.serialize(self)
    }

    #[inline]
    fn serialize_newtype_variant<T: ?Sized>(
        self,
        _name: &'static str,
        variant_index: u32,
        _variant: &'static str,
        value: &T,
    ) -> Result<()>
    where
        T: serde::ser::Serialize,
    {
        self.serialize_u32(variant_index)?;
        value.serialize(self)
    }

    #[inline]
    fn serialize_unit_variant(
        self,
        _name: &'static str,
        variant_index: u32,
        _variant: &'static str,
    ) -> Result<()> {
        self.serialize_u32(variant_index)
    }
}

impl<'a, W> SerializeSeq for &'a mut Serializer<W>
where
    W: Write,
{
    type Ok = ();
    type Error = Error;

    #[inline]
    fn serialize_element<V: ?Sized>(&mut self, value: &V) -> Result<()>
    where
        V: serde::Serialize,
    {
        value.serialize(&mut **self)
    }

    #[inline]
    fn end(self) -> Result<()> {
        Ok(())
    }
}

impl<'a, W> SerializeTuple for &'a mut Serializer<W>
where
    W: Write,
{
    type Ok = ();
    type Error = Error;

    #[inline]
    fn serialize_element<V: ?Sized>(&mut self, value: &V) -> Result<()>
    where
        V: serde::Serialize,
    {
        value.serialize(&mut **self)
    }

    #[inline]
    fn end(self) -> Result<()> {
        Ok(())
    }
}

impl<'a, W> SerializeTupleStruct for &'a mut Serializer<W>
where
    W: Write,
{
    type Ok = ();
    type Error = Error;

    #[inline]
    fn serialize_field<V: ?Sized>(&mut self, value: &V) -> Result<()>
    where
        V: serde::Serialize,
    {
        value.serialize(&mut **self)
    }

    #[inline]
    fn end(self) -> Result<()> {
        Ok(())
    }
}

impl<'a, W> SerializeTupleVariant for &'a mut Serializer<W>
where
    W: Write,
{
    type Ok = ();
    type Error = Error;

    #[inline]
    fn serialize_field<V: ?Sized>(&mut self, value: &V) -> Result<()>
    where
        V: serde::Serialize,
    {
        value.serialize(&mut **self)
    }

    #[inline]
    fn end(self) -> Result<()> {
        Ok(())
    }
}

impl<'a, W> SerializeMap for &'a mut Serializer<W>
where
    W: Write,
{
    type Ok = ();
    type Error = Error;

    #[inline]
    fn serialize_key<K: ?Sized>(&mut self, key: &K) -> Result<()>
    where
        K: serde::Serialize,
    {
        key.serialize(&mut **self)
    }

    #[inline]
    fn serialize_value<V: ?Sized>(&mut self, value: &V) -> Result<()>
    where
        V: serde::Serialize,
    {
        value.serialize(&mut **self)
    }

    #[inline]
    fn end(self) -> Result<()> {
        Ok(())
    }
}

impl<'a, W> SerializeStruct for &'a mut Serializer<W>
where
    W: Write,
{
    type Ok = ();
    type Error = Error;

    #[inline]
    fn serialize_field<V: ?Sized>(&mut self, _key: &'static str, value: &V) -> Result<()>
    where
        V: serde::Serialize,
    {
        value.serialize(&mut **self)
    }

    #[inline]
    fn end(self) -> Result<()> {
        Ok(())
    }
}

impl<'a, W> SerializeStructVariant for &'a mut Serializer<W>
where
    W: Write,
{
    type Ok = ();
    type Error = Error;

    #[inline]
    fn serialize_field<V: ?Sized>(&mut self, _key: &'static str, value: &V) -> Result<()>
    where
        V: serde::Serialize,
    {
        value.serialize(&mut **self)
    }

    #[inline]
    fn end(self) -> Result<()> {
        Ok(())
    }
}

#[inline]
fn encode_utf8(c: char) -> EncodeUtf8 {
    const TAG_CONT: u8 = 0b1000_0000;
    const TAG_TWO_B: u8 = 0b1100_0000;
    const TAG_THREE_B: u8 = 0b1110_0000;
    const TAG_FOUR_B: u8 = 0b1111_0000;
    const MAX_ONE_B: u32 = 0x80;
    const MAX_TWO_B: u32 = 0x800;
    const MAX_THREE_B: u32 = 0x10000;

    let code = c as u32;
    let mut buf = [0; 4];
    let pos = if code < MAX_ONE_B {
        buf[3] = code as u8;
        3
    } else if code < MAX_TWO_B {
        buf[2] = (code >> 6 & 0x1F) as u8 | TAG_TWO_B;
        buf[3] = (code & 0x3F) as u8 | TAG_CONT;
        2
    } else if code < MAX_THREE_B {
        buf[1] = (code >> 12 & 0x0F) as u8 | TAG_THREE_B;
        buf[2] = (code >> 6 & 0x3F) as u8 | TAG_CONT;
        buf[3] = (code & 0x3F) as u8 | TAG_CONT;
        1
    } else {
        buf[0] = (code >> 18 & 0x07) as u8 | TAG_FOUR_B;
        buf[1] = (code >> 12 & 0x3F) as u8 | TAG_CONT;
        buf[2] = (code >> 6 & 0x3F) as u8 | TAG_CONT;
        buf[3] = (code & 0x3F) as u8 | TAG_CONT;
        0
    };
    EncodeUtf8 { buf, pos }
}

struct EncodeUtf8 {
    buf: [u8; 4],
    pos: usize,
}

impl EncodeUtf8 {
    #[inline]
    fn as_slice(&self) -> &[u8] {
        &self.buf[self.pos..]
    }
}


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

use quickcheck::quickcheck;
use serde::Deserialize;
use serde::Serialize;

#[derive(Serialize, Deserialize, Debug)]
struct Wrap(f64, i32, u8);

// workaround for f64 not implementing Eq
impl PartialEq for Wrap {
    fn eq(&self, other: &Self) -> bool {
        let (f1, f2) = (self.0, other.0);
        (self.1, self.2) == (other.1, other.2) && ((f1.is_nan() && f2.is_nan()) || f1 == f2)
    }
}
impl Eq for Wrap {}

#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)]
struct Foo {
    bar: String,
    baz: Option<Wrap>,
    derp: bool,
    list: Vec<u32>,
}

quickcheck! {
    fn test_roundtrip(bar: String, baz: Option<(f64, i32, u8)>, derp: bool, list: Vec<u32>) -> bool {
        let foo = Foo { bar, baz: baz.map(|(a, b, c)| Wrap(a, b, c)), derp, list };
        let bytes = crate::serialize(&foo).unwrap();
        let foo_deserialized: Foo = crate::deserialize(&bytes).unwrap();
        foo == foo_deserialized
    }
}


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

//! Run function repetitively and prints out aggregated result.
//!
//! Differences from other benchmark library:
//! - Do not enforce measuring "wall time". It could be changed to "CPU time", "memory usage", etc.
//! - Do not run only the benchmark part repetitively. For example, a benchmark needs some complex
//!   setup that cannot be reused across runs. That setup cost needs to be excluded from benchmark
//!   result cleanly.
//! - Minimalism. Without fancy features.

use std::env::args;

pub mod measure;
pub use measure::Measure;

/// Measure the best wall clock time.
pub fn elapsed(func: impl FnMut()) -> Result<self::measure::WallClock, String> {
    self::measure::WallClock::measure(func)
}

/// Run a function repeatably. Print the measurement result.
///
/// The actual measurement is dependent on the return value of the function.
/// For example,
/// - [`WallClock::measure`] (or [`elapsed`]) measures the wall clock time,
///   and the function being measured does not need to provide an output.
/// - [`Bytes::measure`] might expect the function to return a [`usize`]
///   in bytes. The function will be only run once.
///
/// If `std::env::args` (excluding the first item and flags) is not empty, and none of them is a
/// substring of `name`, skip running and return directly.
///
/// Example:
///
/// ```
/// use minibench::*;
/// bench("example", || {
///     // prepare
///     elapsed(|| {
///         // measure
///     })
/// })
/// ```
pub fn bench<T: Measure, F: FnMut() -> Result<T, String>>(name: impl ToString, mut func: F) {
    let name = name.to_string();
    // The first arg is the program name. Skip it and flag-like arguments (ex. --bench).
    let args: Vec<String> = args().skip(1).filter(|a| !a.starts_with('-')).collect();
    if args.is_empty() || args.iter().any(|a| name.find(a).is_some()) {
        let mut try_func = || -> Result<T, String> {
            let mut measured = func()?;
            while measured.need_more() {
                measured = measured.merge(func()?);
            }
            Ok(measured)
        };
        let text = match try_func() {
            Ok(measured) => measured.to_string(),
            Err(text) => text,
        };
        println!("{:50}{}", name, text);
    }
}